Answer the architecture pass: the config that owns the contract, and what each runtime leg buys
CI / gate (push) Successful in 8s

This commit is contained in:
2026-09-01 16:59:00 +02:00
parent 082dabb261
commit de9f23581e
5 changed files with 47 additions and 36 deletions
+8 -5
View File
@@ -65,8 +65,8 @@ they never reach a consumer.
`tsconfig.build.json` is that gate, typechecking the shipped files alone with `types: []` and `tsconfig.build.json` is that gate, typechecking the shipped files alone with `types: []` and
`lib: ES2022`, so `node:fs`, `process` and an ES2024 method are compile errors here rather than a `lib: ES2022`, so `node:fs`, `process` and an ES2024 method are compile errors here rather than a
consumer's crash there. Node's test runner, the corpus reads and the build are the repo's own, consumer's crash there. Node's test runner, the corpus reads and the build are the repo's own,
never the library's, and `engines.node` states the floor the shipped JavaScript needs — `>=18`, never the library's, and `engines.node` states the floor the shipped JavaScript needs — `>=18`
the lowest line carrying ES2022 — never the higher one those repo-only tools want. never the higher one those repo-only tools want.
- ESM only — no CommonJS build, no dual-package hazard. - ESM only — no CommonJS build, no dual-package hazard.
- One entrypoint: built JavaScript, `.d.ts` beside it. Do not add a TypeScript-source entrypoint — - One entrypoint: built JavaScript, `.d.ts` beside it. Do not add a TypeScript-source entrypoint —
Node refuses to type-strip under `node_modules` (`ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING`), Node refuses to type-strip under `node_modules` (`ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING`),
@@ -120,9 +120,12 @@ Test for the behaviour wanted first, then implement until green. `node --test`,
Node, tsc and npm never run on the host — only via the pinned images (§9). Tests are independent, Node, tsc and npm never run on the host — only via the pinned images (§9). Tests are independent,
coverage does not decline, containers are torn down after a run. coverage does not decline, containers are torn down after a run.
The gate runs that same suite under Deno and Bun as well as Node, the three images pinned alike: The gate runs that same suite under Deno and Bun as well as Node, the three images pinned alike,
§6's compile gate proves no host API is named, and only running proves those engines execute what and neither extra leg is Node's proof twice. Deno refuses an extensionless or directory specifier,
is written. Both refuse a run that matches no test, so Node's is the only vacuous-green guard. so it holds the module graph to the fully-spelled form a browser can load; Bun runs
JavaScriptCore, the one engine of the three that is not V8, where the Unicode property escapes
emphasis matching leans on can disagree. Both refuse a run matching no test, so Node's is the only
vacuous-green guard.
The floors live in the `test` script, so `npm test` and the gate are one path: 100% of lines and The floors live in the `test` script, so `npm test` and the gate are one path: 100% of lines and
functions, and a branch floor that only ever moves upward. It sits below 100 because the guards functions, and a branch floor that only ever moves upward. It sits below 100 because the guards
+6 -2
View File
@@ -5,7 +5,11 @@ cd "$(dirname "$0")"
bun_image=oven/bun:1.4.0-alpine bun_image=oven/bun:1.4.0-alpine
deno_image=denoland/deno:2.9.6 deno_image=denoland/deno:2.9.6
node_image=node:24.19.0-alpine3.24 node_image=node:24.19.0-alpine3.24
in_image() { docker run --rm -u "$(id -u):$(id -g)" -e HOME=/tmp -v "$PWD:/app" -w /app --entrypoint "$2" "$1" "${@:3}"; } in_image() {
local image=$1 entrypoint=$2
shift 2
docker run --rm -u "$(id -u):$(id -g)" -e HOME=/tmp -v "$PWD:/app" -w /app --entrypoint "$entrypoint" "$image" "$@"
}
in_image "$node_image" npm ci in_image "$node_image" npm ci
in_image "$node_image" npm run typecheck in_image "$node_image" npm run typecheck
@@ -21,4 +25,4 @@ if printf '%s' "$test_output" | grep -q ' tests 0'; then
fi fi
in_image "$deno_image" deno test --allow-read --no-check src/ in_image "$deno_image" deno test --allow-read --no-check src/
in_image "$bun_image" bun test in_image "$bun_image" bun test src/
+7 -4
View File
@@ -139,13 +139,16 @@ numbering is the order the work was planned in, not the order it ships.
additions are read here as one list before that freeze — nine sessions mint them additions are read here as one list before that freeze — nine sessions mint them
independently, and one cause wearing two codes is breaking to undo after `0.1.0`. `0.1.0` independently, and one cause wearing two codes is breaking to undo after `0.1.0`. `0.1.0`
is the markdown round-trip: both markdown directions, the types, `isAdfDocument`. The build is the markdown round-trip: both markdown directions, the types, `isAdfDocument`. The build
lands here: `tsconfig.build.json` gains emit of JS and `.d.ts` to `dist/` (the dev config's lands here: `tsconfig.build.json` gains emit of JS and `.d.ts` to `dist/` (its own
`allowImportingTsExtensions` forces `noEmit`, so the build config needs `allowImportingTsExtensions` forces `noEmit`, so `rewriteRelativeImportExtensions` lands
`rewriteRelativeImportExtensions`), plus `exports`/`files` in `package.json`. The beside it), plus `exports`/`files` in `package.json`. The
maintainer's bump PR also removes `private: true`, the guard against any earlier publish. maintainer's bump PR also removes `private: true`, the guard against any earlier publish.
§6's browser half is first checkable here, on the emitted `dist/index.js` a browser can §6's browser half is first checkable here, on the emitted `dist/index.js` a browser can
load — the compile gate names no host API, and a real page converting a document is the load — the compile gate names no host API, and a real page converting a document is the
other half. other half; name the engines that page covers, since Bun's JavaScriptCore is not Safari's
and none of the three legs is SpiderMonkey. `engines.node` gets its one-line proof here
too — `import('./dist/index.js')` under a pinned `node:18` image, which cannot run the
suite that type stripping wants 22+ for, but proves exactly what the field claims.
**Settled** (the maintainer, 2026-09-01): the round-trip proved over the checked-in corpus **Settled** (the maintainer, 2026-09-01): the round-trip proved over the checked-in corpus
is what `0.1.0` ships on, and the open-ended proof work follows it rather than gating it — is what `0.1.0` ships on, and the open-ended proof work follows it rather than gating it —
3k's spec suite and 4's generators and maintainer-supplied payloads are `0.2.0`, 4b's retry 3k's spec suite and 4's generators and maintainer-supplied payloads are `0.2.0`, 4b's retry
+23 -3
View File
@@ -1,9 +1,29 @@
{ {
"extends": "./tsconfig.json",
"compilerOptions": { "compilerOptions": {
"lib": ["ES2022"], "lib": ["ES2022"],
"module": "NodeNext",
"moduleResolution": "NodeNext",
"target": "ES2022", "target": "ES2022",
"types": [] "types": [],
"allowImportingTsExtensions": true,
"erasableSyntaxOnly": true,
"isolatedModules": true,
"noEmit": true,
"verbatimModuleSyntax": true,
"strict": true,
"exactOptionalPropertyTypes": true,
"noFallthroughCasesInSwitch": true,
"noImplicitOverride": true,
"noImplicitReturns": true,
"noUncheckedIndexedAccess": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"forceConsistentCasingInFileNames": true,
"skipLibCheck": true
}, },
"exclude": ["src/**/*.test.ts"] "exclude": ["src/**/*.test.ts"],
"include": ["src"]
} }
+3 -22
View File
@@ -1,28 +1,9 @@
{ {
"extends": "./tsconfig.build.json",
"compilerOptions": { "compilerOptions": {
"lib": ["ESNext"], "lib": ["ESNext"],
"module": "NodeNext",
"moduleResolution": "NodeNext",
"target": "ESNext", "target": "ESNext",
"types": ["node"], "types": ["node"]
"allowImportingTsExtensions": true,
"erasableSyntaxOnly": true,
"isolatedModules": true,
"noEmit": true,
"verbatimModuleSyntax": true,
"strict": true,
"exactOptionalPropertyTypes": true,
"noFallthroughCasesInSwitch": true,
"noImplicitOverride": true,
"noImplicitReturns": true,
"noUncheckedIndexedAccess": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"forceConsistentCasingInFileNames": true,
"skipLibCheck": true
}, },
"include": ["src"] "exclude": []
} }