diff --git a/AGENTS.md b/AGENTS.md index 286d46e..08e79a7 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -65,8 +65,8 @@ they never reach a consumer. `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 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`, - the lowest line carrying ES2022 — never the higher one those repo-only tools want. + never the library's, and `engines.node` states the floor the shipped JavaScript needs — `>=18` — + never the higher one those repo-only tools want. - ESM only — no CommonJS build, no dual-package hazard. - 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`), @@ -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, 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: -§6's compile gate proves no host API is named, and only running proves those engines execute what -is written. Both refuse a run that matches no test, so Node's is the only vacuous-green guard. +The gate runs that same suite under Deno and Bun as well as Node, the three images pinned alike, +and neither extra leg is Node's proof twice. Deno refuses an extensionless or directory specifier, +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 functions, and a branch floor that only ever moves upward. It sits below 100 because the guards diff --git a/ci.sh b/ci.sh index 7b729e9..07cbae9 100755 --- a/ci.sh +++ b/ci.sh @@ -5,7 +5,11 @@ cd "$(dirname "$0")" bun_image=oven/bun:1.4.0-alpine deno_image=denoland/deno:2.9.6 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 run typecheck @@ -21,4 +25,4 @@ if printf '%s' "$test_output" | grep -q 'ℹ tests 0'; then fi in_image "$deno_image" deno test --allow-read --no-check src/ -in_image "$bun_image" bun test +in_image "$bun_image" bun test src/ diff --git a/todo.md b/todo.md index b262f10..1224155 100644 --- a/todo.md +++ b/todo.md @@ -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 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 - lands here: `tsconfig.build.json` gains emit of JS and `.d.ts` to `dist/` (the dev config's - `allowImportingTsExtensions` forces `noEmit`, so the build config needs - `rewriteRelativeImportExtensions`), plus `exports`/`files` in `package.json`. The + lands here: `tsconfig.build.json` gains emit of JS and `.d.ts` to `dist/` (its own + `allowImportingTsExtensions` forces `noEmit`, so `rewriteRelativeImportExtensions` lands + beside it), plus `exports`/`files` in `package.json`. The 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 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 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 diff --git a/tsconfig.build.json b/tsconfig.build.json index de207e6..40534a9 100644 --- a/tsconfig.build.json +++ b/tsconfig.build.json @@ -1,9 +1,29 @@ { - "extends": "./tsconfig.json", "compilerOptions": { "lib": ["ES2022"], + "module": "NodeNext", + "moduleResolution": "NodeNext", "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"] } diff --git a/tsconfig.json b/tsconfig.json index 56b43c1..2a9b579 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,28 +1,9 @@ { + "extends": "./tsconfig.build.json", "compilerOptions": { "lib": ["ESNext"], - "module": "NodeNext", - "moduleResolution": "NodeNext", "target": "ESNext", - "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 + "types": ["node"] }, - "include": ["src"] + "exclude": [] }