Public: anyone can read this; the crew can change it.
forked from trevato/cs
No generated file lives in git: the bundles are built at boot into FORGE_APP_DATA, and static/ leaves the repository #247
Check · Review · Land · Live done — Landed and live in production.
Pushed. Final state: `bun run test` (typecheck + 1113 tests) passes, `static/` is gone from git, and a cold boot builds and serves.
shipped production is serving it deploy ae07c2d
Sign in to join this conversation.
new above
offline
Pushed. Final state:
bun run test(typecheck + 1113 tests) passes,static/is gone from git, and a cold boot builds and serves.---
static/leaves the repository and the bundles are generated where generated files belong.src/bundles.tsis the new module — one concept, where a deployment's built files go (FORGE_APP_DATA/static) and what a failed build says — andsrc/server.tscalls it with a top-levelawaitbeforeBun.servebinds, then resolves the scripts, the assets, the held-and-gzipped map and/example's measured sizes from that directory. A build that cannot write throws naming the directory and the process stops on it, exactly as a missing bundle did before (#198);findScriptskeeps its refusal underneath, now unreachable on purpose.Nothing a browser sees changed: same content-addressed names,
public, max-age=31536000, immutable, the #236 gzip weighed once at start-up, the same content types, the same<link rel="preload">in the head. The sizes are identical before and after, because the same tree builds the same files — app 95,598 raw / 37,788 gzip-9 (ceiling 98,000 / 39,000), range 34,810 / 14,569 (37,000 / 15,500), example 21,115 / 9,189 (23,500 / 10,200), the weights asset 11,120 / 8,251 (12,300 / 9,100). Note thatapp.ea37d3064a.jsis what this tree builds and none of the threeapp.*.jsmaincarries is; after this there is nothing to be stale.Boot, measured on this box, process start to first response over HTTP, cold: 103 / 103 / 109 ms before, 139 / 140 / 132 ms after into an empty data directory and 126 / 137 / 128 ms into a populated one. The in-process build is 26–60 ms of that (the boot log prints it:
4 files built into …/static, 158.8 kB raw, in 26.1 ms). Building on first request with a lock was the alternative and is not worth ~30 ms at deploy.Tests build instead of reading a committed file, and would fail without this change:
src/server.test.tsasserts every served bundle is byte-for-byte whatbundle()produces and that the directory is underFORGE_APP_DATA, drops a decoy into the repository'sstatic/and expects a 404, and holdsbuildBundlesinto an unwritable path to a loud refusal.scripts/build.test.tswrites a build to a directory of its own and weighs each file on disk against its ceiling, and proves two builds of one tree produce the same names and the same bytes — Bun's output is byte-stable here, so the wholecodeis compared, not just the hash.scripts/bundle.test-helper.tsbuilds once per test process for the three games' bundle tests. The engine's three "no bundle carries this" tests could not import the build (the engine imports nothing outside itself), sosrc/engine/bundles.test.tsspawns it into a temp directory the waypurity.test.tsspawns its probe;scripts/build.tsgained the optional output directory that makes that possible, andbun run scripts/build.ts [dir]is still the way to get the files on disk (into the now-ignoredstatic/by default). No new dependency. Suite is 1113 tests in ~22 s.What I did not do: I could not check a preview — the pull opens after this run, so there was none to fetch. What I can say is that the build is unconditional and per boot, so a preview has no committed artifact to serve and nothing but its own branch's build to serve instead; #237's rebuild of
mainis still what repairsmain, and what remains of #220 is the "two builds agree" test that landed here, so whoever picks it up should close it.🤖 Generated with Claude Code
Closes #241