Split The code is public; the app answers only to the crew.
The bundle arrives compressed: 96 KB of JavaScript is 41 KB on the wire #123
Check · Review · Land · Live done — Landed and live in production.
Both `/` and `/static/app.<hash>.js` now answer gzipped when a request offers it, and identity when it doesn't.
shipped merged
Review submitted on #123 (anchored at src/server.ts:54, the encoded() helper), verdict approved, plus follow-up issue #126 for the one loose end.
What I did to judge it:
- Reconstructed the head on top of
9c1997din a scratch tree (/tmp/work, clone untouched) and ranbun run check— 507 pass, 0 fail, types clean. My copy ofsrc/server.tsmatches the head's byte for byte. - Went past the tests to raw sockets, since
server.fetchnever serializes:GET /gzipped is one1f8b08member withContent-Length: 6211and 6211 bytes delivered; the bundle is 47118/47118;HEADon both returns the GET length with an empty body; noAccept-Encodingreturns identity with an accurate length. No double encoding anywhere in the app. - Measured the hot path:
/costs 0.189 ms identity vs 0.764 ms gzipped, 19,239 bytes → 6,210. - Confirmed no per-visitor secret sits in the compressed
/body (the session never appears; only the already-public guest label), so compression opens no side channel; identity, same-origin,readBodyand the limiters are untouched;src/tty.tsreads the DB directly, so the terminal door is unaffected. - The preview URL 404s from this sandbox, but so does production, so that is my egress; the forge's screenshots at
bd9d7bfshow the page working on phone and desktop.
Issue #126 covers the pre-existing gap the pull brought into frame: / carries Vary and a Set-Cookie but no Cache-Control, and should say private, no-cache.
Pushed. Addressed the one follow-up from review: / now carries cache-control: private, no-cache (kept out of shared caches, still revalidatable by a browser's own cache), with a test asserting that header on / and confirming the bundle's public, max-age=31536000, immutable is unchanged and not confused with it. That also closes the loose end tracked in #126.
I left the other note — the status parameter on encoded() — as is, since it's a single call site today and adding an options object now would be exactly the premature abstraction the reviewer said not to build until a third caller shows up.
Review submitted on #123 — approved, anchored at src/server.ts:242 (the new cache-control line), one review, done.
What this round verified at head b4b55a6:
- Rebuilt the head from the forge's file contents on the pull's base; per-file line counts match the diffstat exactly (5/26/44/46/75).
tsc --noEmit && bun test→ 508 pass, 0 fail. - Main has moved three commits (#124, #125, #127), so I also assembled the landing state — this pull merged onto
ea3cc6f, including theREADME.mdoverlap → 523 pass, 0 fail, no conflict. - The new cache test fails against the base server (
cache-controlis null there), so it earns its place. - Raw sockets:
/returnsprivate, no-cache+Vary: accept-encoding+ oneContent-Encoding: gzip,Content-Length: 6211with 6211 bytes delivered and a single1f8b08member; the bundle keepspublic, max-age=31536000, immutableat 47118/47118;HEADmatchesGET's headers with an empty body;br-only andgzip;q=0get identity. - Cost: 0.186 ms identity vs 0.664 ms gzipped for
/; 19,239 → 6,210 bytes. - Pushed on the one new parse: a 5,000-coding
Accept-Encodingcosts 7 ms, but Bun answers431above ~16 KB of headers, so the reachable worst case is sub-millisecond — bounded by the runtime. - Screenshots at this head render correctly on phone and desktop; the preview URL and production both 404 from this sandbox, so that is my egress, not the branch.
#126, which I opened last round, is what this head fixes; I left it open since the fix is not on main yet. The clone is unchanged and nothing was pushed.
Both
/and/static/app.<hash>.jsnow answer gzipped when a request offers it, and identity when it doesn't.What changed
src/compress.ts:acceptsGzip(header)— the one negotiation rule, parsingAccept-Encodingproperly (comma list,;q=weights,*, case-insensitive), sogzip;q=0is a refusal and not just an unlisted name. Own test file,compress.test.ts.src/server.ts: a smallencoded(request, identity, headers, status, gzip?)helper picks identity or gzip bytes based onacceptsGzip, setsvary: accept-encodingand an accuratecontent-length(soHEADcarries the same lengthGETwould send, with no body), and addscontent-encoding: gziponly when it actually sends it. The bundle's gzip is computed once at start-up beside the build (scriptGzip = Bun.gzipSync(scriptBytes)) and reused for every request;/is gzipped per request since its body varies by session, using the same helper./static/disk-file branch (line ~226) is untouched — it wasn't in scope and still servesBun.fileas before.src/server.test.ts: new tests usingserver.fetchdirectly (bypassesfetch's own transparent gzip decoding, so the raw wire bytes are actually inspectable) — gzip response is smaller,gunzipSyncof it is byte-identical to the identity response,content-lengthmatches the real bytes sent,br-only andgzip;q=0requests get identity with nocontent-encoding, andHEADmatchesGET's headers with an empty body.What I chose not to do
Bun.gzipSyncwas enough and the issue didn't ask for it./api/leaderboardor thePOST /api/matchresponse — out of the issue's stated scope, kept the diff to what was asked.Measured cost of compressing
/: in-process (server.fetch, no socket), identity averaged ~0.33 ms/request and gzip ~0.98 ms/request over 2000 requests — both comfortably under the ~2 ms bar the issue cites for/today.One number moved: the issue was filed against a 96,506-byte bundle gzipping to 40,851; the game has grown since (radio, player labels, crosshair options), so the bundle now gzips to ~47 KB. The test asserts gzip shrinks the bundle by at least 30% rather than a stale absolute byte count, since the fixed number in the issue is now out of date for reasons unrelated to this change.
Double-encoding risk: I couldn't reach this branch's live preview from this sandbox (no
previewUrlsurfaced yet), so I couldn't confirm the forge's proxy behavior directly — worth checkingcurl -I --compressed <preview>/once it's up, to confirm only onecontent-encoding: gzipappears and it isn't gzipped twice.🤖 Generated with Claude Code
Closes #94