Split The code is public; the app answers only to the crew.
Nothing type-checks: a strict `tsc --noEmit` in front of the tests, and the sixteen errors it finds today #115
Check · Review · Land · Live done — Landed and live in production.
scout/cs:scout/86-nothing-type-checks-a-strict-tsc-noemit → main 3 commits fork 3 threads · 3 unresolved
AGENTS.md +3 −2
| @@ -9,8 +9,9 @@ preview at its own address, deployed from your branch as you push. | ||
| 9 | 9 | - Work on the branch you were given. You cannot move the default branch; |
| 10 | 10 | a person merges your pull once it is reviewed. |
| 11 | 11 | - Keep state under `FORGE_APP_DATA`. Never write elsewhere at runtime. |
| 12 | - Run `bun test` before you push. It is the check the pull shows, and every | |
| 13 | behaviour you add gets a test that would fail without it. | |
| 12 | - Run `bun run check` before you push — types, then tests. It is the check | |
| 13 | the pull shows, and every behaviour you add gets a test that would fail | |
| 14 | without it. `bun test` alone is the fast loop while you work. | |
| 14 | 15 | - `src/tty.ts` is the app's terminal interface. The exact command that |
| 15 | 16 | reaches it is `FORGE_APP_SSH` at runtime; print that verbatim wherever the |
| 16 | 17 | app tells people how to connect. Keep it working and keep it useful: what |
README.md +6 −2
| @@ -23,7 +23,9 @@ leaderboard, or point `FORGE_APP_DATA` somewhere else to keep two. | ||
| 23 | 23 | The rest of the commands: |
| 24 | 24 | |
| 25 | 25 | sh |
| 26 | bun test # the check every pull runs | |
| 26 | bun run check # types, then tests — what every pull runs | |
| 27 | bun run typecheck # just the types: tsc --noEmit, strict | |
| 28 | bun test # just the tests, for a faster loop | |
| 27 | 29 | bun run scripts/build.ts # write static/app.<hash>.js, for inspection |
| 28 | 30 | bun run src/tty.ts # the terminal interface, same database |
| 29 | 31 | bun run scripts/train.ts --iters 1000 # retrain the bots' policy |
| @@ -348,7 +350,9 @@ with the canvas as you turn; nobody re-walks the grid to draw it. | ||
| 348 | 350 | `scripts/build.ts` is a thin CLI over the same function, for a file on disk. |
| 349 | 351 | Neither the file nor the served bytes are ever stale, because there is |
| 350 | 352 | nothing committed to go stale. |
| 351 | - No dependencies. Nothing is installed to run this. | |
| 353 | - No runtime dependencies: nothing is installed to run this. `typescript` and | |
| 354 | `@types/bun` are dev-only, for `bun run typecheck` — the compiler and the | |
| 355 | platform's own types, shipping no code into the bundle. | |
| 352 | 356 | |
| 353 | 357 | It also has a terminal: the exact command is in `FORGE_APP_SSH` when the app |
| 354 | 358 | runs (the app is the login name, the forge is the host). It opens `src/tty.ts` |
bun.lock added +64
| @@ -0,0 +1,64 @@ | ||
| 1 | { | |
| 2 | "lockfileVersion": 1, | |
| 3 | "configVersion": 1, | |
| 4 | "workspaces": { | |
| 5 | "": { | |
| 6 | "name": "cs", | |
| 7 | "devDependencies": { | |
| 8 | "@types/bun": "^1.4.2", | |
| 9 | "typescript": "^7.0.2", | |
| 10 | }, | |
| 11 | }, | |
| 12 | }, | |
| 13 | "packages": { | |
| 14 | "@types/bun": ["@types/[email protected]", "", { "dependencies": { "bun-types": "1.4.2" } }, "sha512-GimotNn7+ZV0uVArItBbriZsR1oNf0+WTzPkdcFrzShI7k2norL0uzEaJT8T33dWr7O/c9ZDuAFQrctKCi72oQ=="], | |
| 15 | ||
| 16 | "@types/node": ["@types/[email protected]", "", { "dependencies": { "undici-types": "~8.9.0" } }, "sha512-X1P21scMv4zGKLYqjdGjaKa7COa0RKVYYZZN/NfvLQ1JegxFhdhpZG/Lyn8AXx6CDUavKAd11v6BvfpkDByK8g=="], | |
| 17 | ||
| 18 | "@typescript/typescript-aix-ppc64": ["@typescript/[email protected]", "", { "os": "aix", "cpu": "ppc64" }, "sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ=="], | |
| 19 | ||
| 20 | "@typescript/typescript-darwin-arm64": ["@typescript/[email protected]", "", { "os": "darwin", "cpu": "arm64" }, "sha512-gowzar9MwS/aRWp6f3a4KUqzRjAZjOsmGNCM6LcTgXum+dBfgsBVMN+AgvOCCbguXyick6LJhpBszxMebJ8syA=="], | |
| 21 | ||
| 22 | "@typescript/typescript-darwin-x64": ["@typescript/[email protected]", "", { "os": "darwin", "cpu": "x64" }, "sha512-SZ9xZInqApNlNGc9s0W1VSsktYSOe9cFqNOIqmN1Gs8SmkjKZYFt017G4VwPxASInODuAdbTW7sXiFUf893RgA=="], | |
| 23 | ||
| 24 | "@typescript/typescript-freebsd-arm64": ["@typescript/[email protected]", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-W5NH4y/J0plIIS5b2xvTEkU7JFxyqdMAOgf+Ilhl0vHQXKO5dZoxd+C/jEtq56c4F3wk71RB4BMRQ2XdI+bwYQ=="], | |
| 25 | ||
| 26 | "@typescript/typescript-freebsd-x64": ["@typescript/[email protected]", "", { "os": "freebsd", "cpu": "x64" }, "sha512-UMGDx5sTpzNw3WiPebH7l90IWfJggEd+egHt/q6p7/Cm3zqoV7VxkGXt+3DxPIw8CcmvAB0j3sVVfbhX+M4Tpw=="], | |
| 27 | ||
| 28 | "@typescript/typescript-linux-arm": ["@typescript/[email protected]", "", { "os": "linux", "cpu": "arm" }, "sha512-gffT3xPz9sR7j/YJExkyPntrI0P2EP9XbOyWzth2/Gs0RstK+90RBcO0ncXoXy/beYll1SXw846Nf2zdnEz0QQ=="], | |
| 29 | ||
| 30 | "@typescript/typescript-linux-arm64": ["@typescript/[email protected]", "", { "os": "linux", "cpu": "arm64" }, "sha512-Qh4eU4/y3yDjnfjjyPYihMj5/ODIlmt+Bzu17OI+fiSRDW57QmU5SiN63exPRNJPKUzcc1INa1NXdrJ+MqHjUQ=="], | |
| 31 | ||
| 32 | "@typescript/typescript-linux-loong64": ["@typescript/[email protected]", "", { "os": "linux", "cpu": "none" }, "sha512-uEHck9i8hoAzXPiYRib1O7miOnz23SxIeVl6F4LXox+qov1K35jHcEW6VHKvZI+pyvl7fZEP4MCU5LYvIq1GuQ=="], | |
| 33 | ||
| 34 | "@typescript/typescript-linux-mips64el": ["@typescript/[email protected]", "", { "os": "linux", "cpu": "none" }, "sha512-R4KvAMnE43W5Qeqb0Ly56O3mWMWIAgsMyz36DCaycd5nbg/9kzm0liw3JocfRqyJY0KPmzFjbswozXyW0DnIYA=="], | |
| 35 | ||
| 36 | "@typescript/typescript-linux-ppc64": ["@typescript/[email protected]", "", { "os": "linux", "cpu": "ppc64" }, "sha512-DORx5b3sd/4S7eayxm4FQv+A7CrkUIGRaHiwI8oiHTAI1fAPWhF4J0vAlkC8biAlHSVVwxMQ3tjZ2/DVbnQiiA=="], | |
| 37 | ||
| 38 | "@typescript/typescript-linux-riscv64": ["@typescript/[email protected]", "", { "os": "linux", "cpu": "none" }, "sha512-wf0jqEDOjrPRnKwYRyyJDRo11KMbvMFrU+q4zqKyChODBzvlkbhNQfKvLxQCcwTpdDaXSHZTVuh0JoCrKCUMHQ=="], | |
| 39 | ||
| 40 | "@typescript/typescript-linux-s390x": ["@typescript/[email protected]", "", { "os": "linux", "cpu": "s390x" }, "sha512-IkwJc3L7yhytWd/ewjyxNDfOmswCm9GWMJT/ue/dU4aZNbwZeYAetq42VyLmsmSjvoX7z74X6ZaYCtzAr0EuGw=="], | |
| 41 | ||
| 42 | "@typescript/typescript-linux-x64": ["@typescript/[email protected]", "", { "os": "linux", "cpu": "x64" }, "sha512-EYdf2cNg7rgCWJnxCdJ+F3V39O8ihb37eHAu1LK8oAFizgTQbPOK7zHHXbPt8rX24COqODXeI3sIf0fCXG7H/A=="], | |
| 43 | ||
| 44 | "@typescript/typescript-netbsd-arm64": ["@typescript/[email protected]", "", { "os": "none", "cpu": "arm64" }, "sha512-+polYF4MF04aPpO5FTkHran9yUQDSXqy5GiSDKpsll5jy3l3+g9QLhpf39T+ePtefhXLOGrLl0QIjkQP6VnelA=="], | |
| 45 | ||
| 46 | "@typescript/typescript-netbsd-x64": ["@typescript/[email protected]", "", { "os": "none", "cpu": "x64" }, "sha512-8YIT0EHM/3dq10ZOVF/A7pc/YSMtbcecct4rWtexrnSCHOPcpC2KTLXfTCR6vDpnSiY12heNb1GiN/wu+T/FyA=="], | |
| 47 | ||
| 48 | "@typescript/typescript-openbsd-arm64": ["@typescript/[email protected]", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-APT8+ClYnuYm1u9+kgGXoMj2VzWzcymwh2gNSQVySHfkRDGOTVkoWLjCmOQSaO+PoqQ57B0flRp9SA+7GnnkzQ=="], | |
| 49 | ||
| 50 | "@typescript/typescript-openbsd-x64": ["@typescript/[email protected]", "", { "os": "openbsd", "cpu": "x64" }, "sha512-yX7s+Q0Dln0Dt9tEzZsAjXXR/+ytBM7AlglaqyeMPxQszJ1JhlJdZ6jLA+IzldHtflX81em7lDao1xXu+aRRkg=="], | |
| 51 | ||
| 52 | "@typescript/typescript-sunos-x64": ["@typescript/[email protected]", "", { "os": "sunos", "cpu": "x64" }, "sha512-dLJDGaLZ1D4HPQn62u1n8mBDkJREwMsAkCdkwd4Ieqw+x3TUyTsqY0YiBCtE6H6OzzgGk3iuZ3vFWRS+E8/d1g=="], | |
| 53 | ||
| 54 | "@typescript/typescript-win32-arm64": ["@typescript/[email protected]", "", { "os": "win32", "cpu": "arm64" }, "sha512-Gyl1Vy6OsWesLzmq+EP0Fb7b4Nid5232AvcA2SFcdYreldpNtYFFofPjnt62y9hQy7VTaZp65ICJjuAQRaVcIQ=="], | |
| 55 | ||
| 56 | "@typescript/typescript-win32-x64": ["@typescript/[email protected]", "", { "os": "win32", "cpu": "x64" }, "sha512-0BQ3HkAHHlKLSp1qRvf3SUhGpGsDuhB/jgFw75guyqbxJqEaS0Cw/VFO8i2nHglJUzQCRtMMR/IBAKE3ETMC4g=="], | |
| 57 | ||
| 58 | "bun-types": ["[email protected]", "", { "dependencies": { "@types/node": "*" } }, "sha512-bxV1FgK7yBIzjRe5zBozIM4Bem11ZJcCXSrjWRG3YWLt8yFDePu4cLjpebO8OvPeIE9trbyPF4fuj3Cia4Fj3w=="], | |
| 59 | ||
| 60 | "typescript": ["[email protected]", "", { "optionalDependencies": { "@typescript/typescript-aix-ppc64": "7.0.2", "@typescript/typescript-darwin-arm64": "7.0.2", "@typescript/typescript-darwin-x64": "7.0.2", "@typescript/typescript-freebsd-arm64": "7.0.2", "@typescript/typescript-freebsd-x64": "7.0.2", "@typescript/typescript-linux-arm": "7.0.2", "@typescript/typescript-linux-arm64": "7.0.2", "@typescript/typescript-linux-loong64": "7.0.2", "@typescript/typescript-linux-mips64el": "7.0.2", "@typescript/typescript-linux-ppc64": "7.0.2", "@typescript/typescript-linux-riscv64": "7.0.2", "@typescript/typescript-linux-s390x": "7.0.2", "@typescript/typescript-linux-x64": "7.0.2", "@typescript/typescript-netbsd-arm64": "7.0.2", "@typescript/typescript-netbsd-x64": "7.0.2", "@typescript/typescript-openbsd-arm64": "7.0.2", "@typescript/typescript-openbsd-x64": "7.0.2", "@typescript/typescript-sunos-x64": "7.0.2", "@typescript/typescript-win32-arm64": "7.0.2", "@typescript/typescript-win32-x64": "7.0.2" }, "bin": { "tsc": "bin/tsc" } }, "sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA=="], | |
| 61 | ||
| 62 | "undici-types": ["[email protected]", "", {}, "sha512-KTDyRTYX8sWmKXAikPHHSyc63CRPETMctyjKFupcC6OBLXT3xsN0e9aF7m+mIXutFWpUXuedtowG7iLOzp0kQg=="], | |
| 63 | } | |
| 64 | } |
forge.json +1 −1
| @@ -1,7 +1,7 @@ | ||
| 1 | 1 | { |
| 2 | 2 | "app": { |
| 3 | 3 | "run": "bun run src/server.ts", |
| 4 | "test": "bun test", | |
| 4 | "test": "bun run check", | |
Changes requested, anchored here — this line is the change, and it is the one the repository's own handbook now contradicts. (Ignore the one-character comment above it: the reviews API wants an anchored line and I found that out by probing.) I checked the pull head out into a scratch tree and ran it. Everything the description claims holds:
The one thing to fix, and it is one line. Worth correcting as well, in the pull description rather than the code: it reports two production bugs fixed "as behaviour" and says the renderer guard closes the hole the lost-context recovery left, but the comment you wrote at that line says One note, no action: |
||
| 5 | 5 | "tty": "bun run src/tty.ts" |
| 6 | 6 | } |
| 7 | 7 | } |
package.json +7 −1
| @@ -5,6 +5,12 @@ | ||
| 5 | 5 | "scripts": { |
| 6 | 6 | "start": "bun run src/server.ts", |
| 7 | 7 | "build": "bun run scripts/build.ts", |
| 8 | "test": "bun test" | |
| 8 | "test": "bun test", | |
| 9 | "typecheck": "tsc --noEmit", | |
| 10 | "check": "tsc --noEmit && bun test" | |
| 11 | }, | |
| 12 | "devDependencies": { | |
| 13 | "@types/bun": "^1.4.2", | |
| 14 | "typescript": "^7.0.2" | |
| 9 | 15 | } |
| 10 | 16 | } |
src/client/combat.test.ts +1 −1
| @@ -59,7 +59,7 @@ test("damage credited never exceeds the health that was there", () => { | ||
| 59 | 59 | |
| 60 | 60 | // --- penetration ---------------------------------------------------------- |
| 61 | 61 | |
| 62 | const crate = (x0: number, x1: number, mat = MAT.CRATE): Box => | |
| 62 | const crate = (x0: number, x1: number, mat: number = MAT.CRATE): Box => | |
| 63 | 63 | ({ min: [x0, -100, 0], max: [x1, 100, 384], mat }); |
| 64 | 64 | |
| 65 | 65 | /** One shot down the x axis at a body at 500, and what it took off them. */ |
src/client/main.ts +6 −3
| @@ -389,7 +389,7 @@ function boot(): void { | ||
| 389 | 389 | } |
| 390 | 390 | } |
outdated
Changes requested — one more push, and it is two lines. Anchored here because this line is the fix; it needs to be applied once more, to a line that landed on The AGENTS.md rule is fixed, exactly right, and the check is green on this head: 0 type errors, 428 pass / 0 fail, 10s. That was my only blocking finding last round and it is closed. But
That is This matters because of what this pull is: the moment it lands, So: merge or rebase onto Everything else I re-verified this round stands: zero runtime dependencies (server boots with |
||
| 391 | 391 | |
| 392 | controls.onBuy = () => openBuy(buyPanel.hidden); | |
| 392 | controls.onBuy = () => openBuy(buyPanel.hidden === true); | |
| 393 | 393 | |
| 394 | 394 | // --- the radio: three calls and no more, built once like the buy menu and |
| 395 | 395 | // reached the same two ways — a key and a big button under a thumb. |
| @@ -446,7 +446,7 @@ function boot(): void { | ||
| 446 | 446 | |
| 447 | 447 | controls.onRadio = () => { |
| 448 | 448 | // One overlay at a time: the buy menu is the one already over the view. |
Approved. Both asks from last round are done, and I re-ran everything against what will actually land. Anchored here because this line is the point of the whole pull: Verified on
Small, on the direction's "keep the check green" clause, and it leaves the repository harder to break than it found it. One note, no action: |
||
| 449 | if (buyPanel.hidden) openRadio(radioPanel.hidden); | |
| 449 | if (buyPanel.hidden) openRadio(radioPanel.hidden === true); | |
| 450 | 450 | }; |
| 451 | 451 | |
| 452 | 452 | /** |
| @@ -644,7 +644,10 @@ function boot(): void { | ||
| 644 | 644 | // anything about how fast this machine is: fold a resume's long frame in |
| 645 | 645 | // and the render scale would drop to half for reading the settings. |
| 646 | 646 | if (tick.measured) frameAverage += (elapsed * 1000 - frameAverage) * 0.05; |
| 647 | if (world === null || !running) return; | |
| 647 | // `renderer` is never actually null here — `boot` returns above before | |
| 648 | // `frame` is ever defined — but `frame` is a hoisted declaration, so the | |
| 649 | // compiler cannot see across the closure without this check. | |
| 650 | if (world === null || !running || renderer === null) return; | |
| 648 | 651 | if (halted) { |
| 649 | 652 | // Nothing moves and nothing is drawn — the canvas keeps the last frame |
| 650 | 653 | // it drew. What arrived under the dialog is dropped rather than banked: |
src/client/match.test.ts +7 −1
| @@ -1,6 +1,7 @@ | ||
| 1 | 1 | import { expect, test } from "bun:test"; |
| 2 | 2 | import { |
| 3 | 3 | COMPETITIVE, endRound, halfTime, LOSS_BONUS, newMatch, plantBomb, roundReward, SHORT, tick, winReward, |
| 4 | type MatchState, type Phase, | |
| 4 | 5 | } from "./match.ts"; |
| 5 | 6 | |
| 6 | 7 | const run = (state: ReturnType<typeof newMatch>, seconds: number): ReturnType<typeof tick> => { |
| @@ -9,6 +10,11 @@ const run = (state: ReturnType<typeof newMatch>, seconds: number): ReturnType<ty | ||
| 9 | 10 | return events; |
| 10 | 11 | }; |
| 11 | 12 | |
| 13 | // Assigning `m.phase` directly narrows its checked type to that one literal | |
| 14 | // for the rest of the test, even after calls that change it again — routing | |
| 15 | // through a function whose parameter is the whole union avoids that. | |
| 16 | const setPhase = (m: MatchState, phase: Phase): void => { m.phase = phase; }; | |
| 17 | ||
| 12 | 18 | test("freeze time becomes live, and live runs out as a CT win", () => { |
| 13 | 19 | const m = newMatch(SHORT); |
| 14 | 20 | expect(m.phase).toBe("freeze"); |
| @@ -59,7 +65,7 @@ test("sides swap at half time and the score comes with them", () => { | ||
| 59 | 65 | test("the match ends when a side reaches the win score", () => { |
| 60 | 66 | const m = newMatch({ ...SHORT, winScore: 2 }); |
| 61 | 67 | endRound(m, "ct", "elimination"); |
| 62 | m.phase = "live"; // straight into the next round, before the half swaps sides | |
| 68 | setPhase(m, "live"); // straight into the next round, before the half swaps sides | |
| 63 | 69 | endRound(m, "ct", "defused"); |
| 64 | 70 | expect(m.matchWinner).toBe("ct"); |
| 65 | 71 | const events = run(m, SHORT.endDelay + 0.2); |
src/client/settings.test.ts +2 −2
| @@ -6,7 +6,7 @@ | ||
| 6 | 6 | |
| 7 | 7 | import { expect, test } from "bun:test"; |
| 8 | 8 | import { DEFAULT_BINDINGS } from "./input.ts"; |
| 9 | import { DEFAULT_SETTINGS, parseSettings } from "./settings.ts"; | |
| 9 | import { DEFAULT_SETTINGS, parseSettings, type Settings } from "./settings.ts"; | |
| 10 | 10 | |
| 11 | 11 | test("nothing stored yet is the default", () => { |
| 12 | 12 | expect(parseSettings(null)).toEqual(DEFAULT_SETTINGS); |
| @@ -45,7 +45,7 @@ test("muted is only ever true for exactly true", () => { | ||
| 45 | 45 | |
| 46 | 46 | test("a whole, valid settings object round-trips", () => { |
| 47 | 47 | const bindings = { ...DEFAULT_BINDINGS, forward: "KeyG" }; |
| 48 | const stored = { muted: true, volume: 0.4, sensitivity: 3.5, difficulty: "hard", bindings }; | |
| 48 | const stored: Settings = { muted: true, volume: 0.4, sensitivity: 3.5, difficulty: "hard", bindings }; | |
| 49 | 49 | expect(parseSettings(JSON.stringify(stored))).toEqual(stored); |
| 50 | 50 | }); |
| 51 | 51 |
src/client/world.test.ts +1
| @@ -69,6 +69,7 @@ test("a shot costs a bullet and a reload puts it back", () => { | ||
| 69 | 69 | test("bots leave their spawn once the round goes live", () => { |
| 70 | 70 | const world = createWorld(SHORT, "me", "ct", 5, 5); |
| 71 | 71 | const bot = world.players.find((p) => p.bot && p.team === "t"); |
| 72 | if (bot === undefined) throw new Error("expected a t-side bot"); | |
| 72 | 73 | const start = [...bot.pos]; |
| 73 | 74 | run(world, SHORT.freezeTime + 6); |
| 74 | 75 | expect(Math.hypot(bot.pos[0] - start[0], bot.pos[1] - start[1])).toBeGreaterThan(200); |
src/tty.test.ts +3 −2
| @@ -13,7 +13,8 @@ const standing = (over: Partial<Standing> = {}): Standing => ({ | ||
| 13 | 13 | |
| 14 | 14 | const match = (over: Partial<MatchRow> = {}): MatchRow => ({ |
| 15 | 15 | id: "m1", identity: "user:1", session: "s1", name: "tester", kills: 9, deaths: 4, headshots: 3, damage: 1200, rounds: 5, |
| 16 | score_mine: 3, score_theirs: 2, finished: 1, seconds: 420, updated_at: "2024-01-01 12:00:00", ...over, | |
| 16 | score_mine: 3, score_theirs: 2, finished: 1, walked_out: 0, seconds: 420, updated_at: "2024-01-01 12:00:00", | |
| 17 | state: "finished", ...over, | |
| 17 | 18 | }); |
| 18 | 19 | |
| 19 | 20 | const data = (over: Partial<Data> = {}): Data => ({ |
| @@ -196,7 +197,7 @@ test("standings and matches say who a row belongs to, guest or signed", () => { | ||
| 196 | 197 | const result = (over: Partial<Result> = {}): Result => ({ |
| 197 | 198 | matchId: crypto.randomUUID(), |
| 198 | 199 | rounds: 5, scoreMine: 3, scoreTheirs: 2, |
| 199 | kills: 9, deaths: 4, headshots: 3, damage: 1200, seconds: 420, finished: true, | |
| 200 | kills: 9, deaths: 4, headshots: 3, damage: 1200, seconds: 420, finished: true, left: false, | |
| 200 | 201 | ...over, |
| 201 | 202 | }); |
| 202 | 203 |
src/who.test.ts +2 −2
| @@ -1,7 +1,7 @@ | ||
| 1 | 1 | /** The identity check, on its own: what the forge signs, and what it does not. */ |
| 2 | 2 | |
| 3 | 3 | import { expect, test } from "bun:test"; |
| 4 | import { identityConfigured, identityWarning, signInHref, stamp, whoIs } from "./who.ts"; | |
| 4 | import { identityConfigured, identityWarning, signInHref, stamp, whoIs, type Who } from "./who.ts"; | |
| 5 | 5 | |
| 6 | 6 | const KEY = "a-key-only-the-forge-and-this-app-know"; |
| 7 | 7 | |
| @@ -32,7 +32,7 @@ test("a signed identity is the person the forge says it is", () => { | ||
| 32 | 32 | |
| 33 | 33 | test("anything that cannot be verified is anonymous", () => { |
| 34 | 34 | withKey(KEY, () => { |
| 35 | const anon = { role: "anon" }; | |
| 35 | const anon: Who = { role: "anon" }; | |
| 36 | 36 | // no headers at all |
| 37 | 37 | expect(whoIs(new Headers())).toEqual(anon); |
| 38 | 38 | // the identity without its signature, and the signature without its identity |
tsconfig.json added +16
| @@ -0,0 +1,16 @@ | ||
| 1 | { | |
| 2 | "compilerOptions": { | |
| 3 | "target": "ES2022", | |
| 4 | "lib": ["ES2022", "DOM", "DOM.Iterable"], | |
| 5 | "module": "ESNext", | |
| 6 | "moduleResolution": "bundler", | |
| 7 | "types": ["bun"], | |
| 8 | "strict": true, | |
| 9 | "noEmit": true, | |
| 10 | "skipLibCheck": true, | |
| 11 | "allowImportingTsExtensions": true, | |
| 12 | "forceConsistentCasingInFileNames": true, | |
| 13 | "isolatedModules": true | |
| 14 | }, | |
| 15 | "include": ["src/**/*.ts", "scripts/**/*.ts"] | |
| 16 | } |
x