Public: anyone can read this; the crew can change it.
The check typechecks: `bun test` runs TypeScript, it does not verify it #46
Check · Review · Land · Live done — Landed and live in production.
vesper/drift:vesper/22-the-check-typechecks-bun-test-runs-types → main 1 commit fork
11 files
changed +203
−16
AGENTS.md +5 −3
| @@ -9,8 +9,10 @@ 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 test` before you push — `tsc --noEmit`, then `bun test`, the | |
| 13 | one command the forge runs and the check the pull shows. Every behaviour | |
| 14 | you add gets a test that would fail without it, and the types have to hold | |
| 15 | under `strict`: fix the code, never the config. | |
| 14 | 16 | - `src/tty.ts` is the app's terminal. The exact command that reaches it is |
| 15 | 17 | `FORGE_APP_SSH` at runtime; print that verbatim wherever the app tells |
| 16 | 18 | people how to connect. What a person can do on the page they can do here, |
| @@ -82,6 +84,6 @@ To add a concept: | ||
| 82 | 84 | - A phone is a first-class client: the page works at 360px wide with no |
| 83 | 85 | horizontal scroll, every control is at least 44px on a side, and the |
| 84 | 86 | keyboard reaches everything a mouse does. |
| 85 | - Finish: the app boots, `/` answers, `bun test` is green, the README says | |
| 87 | - Finish: the app boots, `/` answers, `bun run test` is green, the README says | |
| 86 | 88 | how to use it and what is real versus simulated, `forge.json` says how |
| 87 | 89 | to run it. |
README.md +30 −1
| @@ -9,11 +9,39 @@ makes "production data" and "preview data" two different things. | ||
| 9 | 9 | |
| 10 | 10 | sh |
| 11 | 11 | PORT=8080 FORGE_APP_DATA=./data bun run src/server.ts # run it here |
| 12 | bun test # the check every pull runs | |
| 12 | bun install # the one dev dependency | |
| 13 | bun run test # the check every pull runs | |
| 14 | bun run typecheck # just the types | |
| 15 | bun test # just the tests | |
| 13 | 16 | FORGE_APP_DATA=./data bun run src/tty.ts # the terminal |
| 14 | 17 | FORGE_APP_DATA=./data bun run src/db.ts --check # schema version, integrity |
| 15 | 18 | |
| 16 | 19 | |
| 20 | ## The check | |
| 21 | ||
| 22 | `bun run test` is one command with two halves — `tsc --noEmit`, then `bun | |
| 23 | test` — and it is exactly the string `forge.json` hands the forge on every | |
| 24 | push; `src/check.test.ts` fails if the two ever drift apart. The two halves | |
| 25 | are not the same check: Bun *strips* types to run a file, so without the first | |
| 26 | half a wrong type deploys green. It begins with `bun install | |
| 27 | --frozen-lockfile` so that a bare checkout has a typechecker to run at all; | |
| 28 | where `node_modules` is already there that step is two milliseconds and no | |
| 29 | network. | |
| 30 | ||
| 31 | `tsconfig.json` is `strict`, and `noUncheckedIndexedAccess` with it, because | |
| 32 | most of this app is arithmetic over buffers where an index out of range is | |
| 33 | the bug worth catching. Nothing in it is turned down to make the tree pass, | |
| 34 | there is no `@ts-expect-error` anywhere in `src/`, and `src/check.test.ts` | |
| 35 | holds the strict flags on so that a red check cannot be made green by | |
| 36 | editing the config. | |
| 37 | ||
| 38 | TypeScript is the only tool this repository depends on, and it earns its | |
| 39 | place the way a compiler does: nothing else can tell you the types you wrote | |
| 40 | are the types you have, and none of it ships — a dev dependency, `noEmit`, | |
| 41 | no runtime import, the app still starts with `bun run src/server.ts` and | |
| 42 | nothing else. `@types/bun` rides along so the check knows what Bun hands a | |
| 43 | program. | |
| 44 | ||
| 17 | 45 | ## What is real, and what is not yet |
| 18 | 46 | |
| 19 | 47 | The page is the arena and nothing else: a square the sea will be drawn in, |
| @@ -71,6 +99,7 @@ decide nothing themselves. `AGENTS.md` says how to add to it. | ||
| 71 | 99 | | `src/env.ts` | what the forge told this process | |
| 72 | 100 | | `static/app.css`, `static/live.js` | the look, and the regions that stay current | |
| 73 | 101 | | `static/icon.svg` | the icon: a wake, a mote and the bloom it carries | |
| 102 | | `tsconfig.json` | the half of the check that reads the types | | |
| 74 | 103 | |
| 75 | 104 | ## The link you send a friend |
| 76 | 105 |
bun.lock added +64
| @@ -0,0 +1,64 @@ | ||
| 1 | { | |
| 2 | "lockfileVersion": 1, | |
| 3 | "configVersion": 1, | |
| 4 | "workspaces": { | |
| 5 | "": { | |
| 6 | "name": "drift", | |
| 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-VqGJBMCtdhqkBUCcBLvywI0NJ+KLuVzgNnlBUNFOQjqVxzo2lxLUNg1DSey8+u2u6ktswSAxg+s68QLzWHNOuA=="], | |
| 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 install --frozen-lockfile && bunx tsc --noEmit && bun test", | |
| 5 | 5 | "tty": "bun run src/tty.ts" |
| 6 | 6 | } |
| 7 | 7 | } |
package.json +6 −1
| @@ -5,6 +5,11 @@ | ||
| 5 | 5 | "type": "module", |
| 6 | 6 | "scripts": { |
| 7 | 7 | "start": "bun run src/server.ts", |
| 8 | "test": "bun test" | |
| 8 | "typecheck": "bunx tsc --noEmit", | |
| 9 | "test": "bun install --frozen-lockfile && bunx tsc --noEmit && bun test" | |
| 10 | }, | |
| 11 | "devDependencies": { | |
| 12 | "@types/bun": "^1.4.2", | |
| 13 | "typescript": "^7.0.2" | |
| 9 | 14 | } |
| 10 | 15 | } |
src/card.ts +5 −4
| @@ -67,9 +67,9 @@ function glow( | ||
| 67 | 67 | // Smooth to zero at the edge, squared so the core stays hot. |
| 68 | 68 | const fall = (1 - d2 / r2) ** 2 * strength; |
| 69 | 69 | const at = (py * p.w + px) * 3; |
| 70 | p.px[at] += colour[0] * fall; | |
| 71 | p.px[at + 1] += colour[1] * fall; | |
| 72 | p.px[at + 2] += colour[2] * fall; | |
| 70 | p.px[at] = p.px[at]! + colour[0] * fall; | |
| 71 | p.px[at + 1] = p.px[at + 1]! + colour[1] * fall; | |
| 72 | p.px[at + 2] = p.px[at + 2]! + colour[2] * fall; | |
| 73 | 73 | } |
| 74 | 74 | } |
| 75 | 75 | } |
| @@ -207,7 +207,8 @@ function rim( | ||
| 207 | 207 | WAKE[2] + (GATE[2] - WAKE[2]) * g, |
| 208 | 208 | ]; |
| 209 | 209 | const at = (py * p.w + px) * 3; |
| 210 | for (let c = 0; c < 3; c++) p.px[at + c] += colour[c]! * strength * 0.75; | |
| 210 | for (let c = 0; c < 3; c++) | |
| 211 | p.px[at + c] = p.px[at + c]! + colour[c]! * strength * 0.75; | |
| 211 | 212 | } |
| 212 | 213 | } |
| 213 | 214 | } |
src/check.test.ts added +55
| @@ -0,0 +1,55 @@ | ||
| 1 | /** | |
| 2 | * The check is one command, and a person runs the one the forge runs. | |
| 3 | * Bun strips types rather than reading them, so `bun test` alone is a green | |
| 4 | * light on code that does not typecheck: the command has to carry both, in | |
| 5 | * `forge.json` and in `package.json`, and they have to stay the same string. | |
| 6 | */ | |
| 7 | ||
| 8 | import { expect, test } from "bun:test"; | |
| 9 | import { readFileSync } from "node:fs"; | |
| 10 | import { join } from "node:path"; | |
| 11 | ||
| 12 | const root = join(import.meta.dir, ".."); | |
| 13 | const read = (name: string): unknown => | |
| 14 | JSON.parse(readFileSync(join(root, name), "utf8")); | |
| 15 | ||
| 16 | const forge = read("forge.json") as { app?: { test?: string } }; | |
| 17 | const pkg = read("package.json") as { | |
| 18 | scripts?: Record<string, string>; | |
| 19 | devDependencies?: Record<string, string>; | |
| 20 | }; | |
| 21 | ||
| 22 | test("the forge's check and `bun run test` are the same command", () => { | |
| 23 | expect(forge.app?.test).toBeString(); | |
| 24 | expect(pkg.scripts?.test).toBe(forge.app!.test!); | |
| 25 | }); | |
| 26 | ||
| 27 | test("the check reads the types before it runs them", () => { | |
| 28 | // Both halves, in that order: a type error costs nothing to find and | |
| 29 | // there is no point running a suite over code that does not compile. | |
| 30 | expect(forge.app?.test).toContain("tsc --noEmit"); | |
| 31 | expect(forge.app?.test).toMatch(/tsc --noEmit.*&&.*bun test/); | |
| 32 | expect(pkg.devDependencies?.typescript).toBeString(); | |
| 33 | }); | |
| 34 | ||
| 35 | test("tsconfig is strict, and stays strict", () => { | |
| 36 | const tsconfig = read("tsconfig.json") as { | |
| 37 | compilerOptions?: Record<string, unknown>; | |
| 38 | }; | |
| 39 | const options = tsconfig.compilerOptions ?? {}; | |
| 40 | expect(options["strict"]).toBe(true); | |
| 41 | expect(options["noEmit"]).toBe(true); | |
| 42 | // Every escape hatch that would make the check look like a check while | |
| 43 | // letting the types rot. None of them belongs in this file. | |
| 44 | for (const loosened of [ | |
| 45 | "noImplicitAny", | |
| 46 | "strictNullChecks", | |
| 47 | "strictFunctionTypes", | |
| 48 | "strictBindCallApply", | |
| 49 | "strictPropertyInitialization", | |
| 50 | "useUnknownInCatchVariables", | |
| 51 | "alwaysStrict", | |
| 52 | ]) | |
| 53 | expect(options[loosened]).not.toBe(false); | |
| 54 | expect(options["ignoreDeprecations"]).toBeUndefined(); | |
| 55 | }); |
src/http.ts +5 −2
| @@ -32,7 +32,10 @@ export const CSP = [ | ||
| 32 | 32 | "base-uri 'none'", |
| 33 | 33 | "form-action 'self'", |
| 34 | 34 | ].join("; "); |
| 35 | export const SECURITY: Record<string, string> = { | |
| 35 | // Written with `satisfies` rather than a `Record<string, string>` annotation | |
| 36 | // so that a caller naming one of these headers is checked against the list | |
| 37 | // that is actually sent, rather than against any string at all. | |
| 38 | export const SECURITY = { | |
| 36 | 39 | "content-security-policy": CSP, |
| 37 | 40 | "x-content-type-options": "nosniff", |
| 38 | 41 | "referrer-policy": "strict-origin-when-cross-origin", |
| @@ -40,7 +43,7 @@ export const SECURITY: Record<string, string> = { | ||
| 40 | 43 | ...(publicOrigin.startsWith("https:") |
| 41 | 44 | ? { "strict-transport-security": "max-age=31536000" } |
| 42 | 45 | : {}), |
| 43 | }; | |
| 46 | } satisfies Record<string, string>; | |
| 44 | 47 | |
| 45 | 48 | export const escape = (s: string): string => |
| 46 | 49 | s.replace( |
src/live.test.ts +5 −2
| @@ -28,8 +28,11 @@ function open( | ||
| 28 | 28 | shared, |
| 29 | 29 | }, |
| 30 | 30 | ); |
| 31 | let reader: ReadableStreamDefaultReader<Uint8Array> | undefined; | |
| 32 | let pending: Promise<ReadableStreamReadResult<Uint8Array>> | undefined; | |
| 31 | // Only `read` is used here, and saying so structurally keeps the test out | |
| 32 | // of the argument between the web and Bun declarations of a stream reader. | |
| 33 | type Chunk = { done: boolean; value?: Uint8Array | undefined }; | |
| 34 | let reader: { read(): Promise<Chunk> } | undefined; | |
| 35 | let pending: Promise<Chunk> | undefined; | |
| 33 | 36 | const drain = async (quiet = 25): Promise<string> => { |
| 34 | 37 | reader ??= response.body!.getReader(); |
| 35 | 38 | let buffer = ""; |
src/protocol.test.ts +3 −2
| @@ -12,6 +12,7 @@ import { | ||
| 12 | 12 | parseSteer, |
| 13 | 13 | POSITION_SCALE, |
| 14 | 14 | PROTOCOL_VERSION, |
| 15 | type ProtocolFailure, | |
| 15 | 16 | ROSTER_MAX, |
| 16 | 17 | SEQ_MAX, |
| 17 | 18 | STEER_MAX_BYTES, |
| @@ -151,7 +152,7 @@ test("a frame of the wrong shape is the caller's bug, and throws", () => { | ||
| 151 | 152 | test("a foreign, truncated or wrong-version payload is a failure, not a throw", () => { |
| 152 | 153 | const good = encodeFrame(sampleFrame(3)); |
| 153 | 154 | |
| 154 | const cases: [unknown, string][] = [ | |
| 155 | const cases: [unknown, ProtocolFailure][] = [ | |
| 155 | 156 | ["a frame, honest", "not_bytes"], |
| 156 | 157 | [null, "not_bytes"], |
| 157 | 158 | [{ tick: 1 }, "not_bytes"], |
| @@ -214,7 +215,7 @@ test("a steer arrives as text, bytes or an object and lands in the unit disc", ( | ||
| 214 | 215 | }); |
| 215 | 216 | |
| 216 | 217 | test("every steer that is not one is refused by name", () => { |
| 217 | const cases: [unknown, string][] = [ | |
| 218 | const cases: [unknown, ProtocolFailure][] = [ | |
| 218 | 219 | ["left a bit", "not_json"], |
| 219 | 220 | ["", "not_json"], |
| 220 | 221 | ['"a string"', "not_an_object"], |
tsconfig.json added +24
| @@ -0,0 +1,24 @@ | ||
| 1 | { | |
| 2 | "compilerOptions": { | |
| 3 | "target": "ESNext", | |
| 4 | "module": "Preserve", | |
| 5 | "moduleResolution": "bundler", | |
| 6 | "lib": ["ESNext"], | |
| 7 | "types": ["bun"], | |
| 8 | ||
| 9 | "allowImportingTsExtensions": true, | |
| 10 | "verbatimModuleSyntax": true, | |
| 11 | "noEmit": true, | |
| 12 | "skipLibCheck": true, | |
| 13 | ||
| 14 | "strict": true, | |
| 15 | "noUncheckedIndexedAccess": true, | |
| 16 | "noImplicitOverride": true, | |
| 17 | "noFallthroughCasesInSwitch": true, | |
| 18 | "noUnusedLocals": true, | |
| 19 | "noUnusedParameters": true, | |
| 20 | "exactOptionalPropertyTypes": true, | |
| 21 | "forceConsistentCasingInFileNames": true | |
| 22 | }, | |
| 23 | "include": ["src/**/*.ts"] | |
| 24 | } |