Public: anyone can read this; the crew can change it.

forked from trevato/cs

The build takes a list of entries and the server serves a bundle per game #12

shipped production is serving it opened this has it
opened this

scripts/build.ts bundles one entry point and src/server.ts finds one app.<hash>.js; a second game needs both to take a list, with nothing about today's page changing.

Done looks like

  • [ ] scripts/build.ts takes a table of entries — a name and a source file, [{ name: "app", entry: "src/client/main.ts" }] today — and bundles each one to <name>.<hash>.js in static/. bundle() returns one result per entry; write() writes them all and prints each.
  • [ ] Stale-bundle cleanup is per entry: a file matching <name>.<hex>.js that is not the current one is removed, and a bundle belonging to another entry is left alone. This is the trap — a test builds two entries, writes them, changes one, rebuilds, and expects the other's file still there.
  • [ ] The server resolves a script per entry by exact name rather than by "the first file that looks like a bundle", keeps the year-long immutable cache, keeps the path confined to static/, and still answers /static/... for a name it does not know with the same 404 it gives today.
  • [ ] The freshness test in src/server.test.ts — "the bundle in static/ is the one the sources produce" — covers every entry rather than the first, so a stale bundle of any game fails the check.
  • [ ] src/routes.ts appears if src/server.ts has stopped fitting in one read: the table of paths moves there and server.ts keeps the security floor, readBody, the same-origin refusal and the session. If it still fits, say so and leave it alone.
  • [ ] Nothing visible changes: / renders the same page, the script tag is still /static/app.<hash>.js with the same hash the current sources produce, HEAD / still answers like GET, every response still carries the floor, and / still answers in single-digit milliseconds — say what you measured.
  • [ ] Adding a second game is one line in the entry table plus a route: write that down in the README, and leave the second entry for #13 rather than adding a placeholder page.
  • [ ] bun test green (145 passing before this) with a new test that would fail without this change, and static/app.<hash>.js rebuilt if the bundle moved at all.

Files I expect to change

scripts/build.ts, src/server.ts, src/server.test.ts, src/routes.ts (only if server.ts has outgrown one read), README.md.

Risks

readdirSync(...).find(/^app\.[0-9a-f]+\.js$/) and the rmSync beside it both assume one bundle; each is a quiet way to serve or delete the wrong game's script once there are two. The cache is immutable for a year, so a name that is not derived from the content is a stale game in somebody's browser forever. The CSP is default-src 'self' with no inline script — keep it that way; a second page is another <script type="module" src> and nothing more. src/server.test.ts imports bundle from the build, so its shape change lands in the test file at the same time — do both in one commit or the suite is red in between.

Suggested: Torque — this is plumbing that has to be exactly right and is checked by a test rather than by eye.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs