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

forked from trevato/cs

Not found is a page, not a fragment: the 404 and the 500 are served through the shell, and a favicon is not a miss #115

shipped production is serving it opened this has it
opened this

Every wrong URL on this server lands on unstyled markup, and it should land on the same dark, phone-sized document every game is served inside.

GET /nope today answers 433 bytes: <h1>Not found</h1><p>The games on this server:</p><ul>… — no doctype, no <meta charset>, no viewport, no style, no header, no footer. The 500 in error() is one bare <h1>Something went wrong</h1>. Both go through html() in src/server.ts, so they carry the security floor and nothing else. src/pages/shell.ts already renders the document and the nav for both games; the only thing it insists on that these two pages cannot give it is a script.

Done looks like

  • [ ] PageOptions.script in src/pages/shell.ts becomes optional, and the shell emits no <script> element at all when it is absent. The existing "the document is a document, and the only script is the bundle" test keeps passing and gains a sibling: a page with no script has no <script> in it anywhere.
  • [ ] A new src/pages/miss.ts — one concept, the pages that are not a game — renders both: the 404 (a heading, the sentence, listHtml() from ./games.ts, every link the nav's 44px) and the 500 (it says something went wrong and names no path, no stack, no runtime, no version).
  • [ ] src/server.ts serves both through it. The status codes stay 404 and 500, the content type stays text/html; charset=utf-8, the whole security floor is still on both, and the 500 still logs the error server-side and puts nothing from it on the page.
  • [ ] Both carry the header nav with every game from the registry, the environment pill, the commit and the footer, and hold the same bar the game pages do: one <h1>, the viewport meta, no horizontal scroll at 360px, every control at least 44px.
  • [ ] The shell gains an inline SVG data-URI favicon (<link rel="icon" href="data:image/svg+xml,…">), so /favicon.ico stops being a request this app answers with a 404 by design. No file under static/, no second request, and the CSP already allows img-src 'self' data:.
  • [ ] HEAD on a missing path still answers 404 with no body, and a POST that is not same-origin is still refused before any of this.
  • [ ] Tests: a 404 is a whole document (starts <!doctype html>, has the charset, the viewport, the nav and the footer), still lists every game from src/games.ts with label, href and blurb escaped, and carries every security header; the 500 is a document too and contains no absolute path from the error it was handed.

Files I expect to change

src/pages/shell.ts, src/pages/miss.ts (new), src/pages/miss.test.ts or src/server.test.ts, src/pages/games.ts, src/server.ts, src/pages/shell.test.ts.

Risks

  • Making script optional must not let a game page ship without its bundle. The per-game tests already assert exactly one script tag on / and /range; keep them and let them be the guard.
  • Reaching the 500 from a test needs something that throws inside fetch. Prefer testing the handler as a function, or a renderer stubbed for the test, over anything that leaves a broken route behind.
  • The 404 body is built once at start-up today (NOT_FOUND); keep it built once — it never changes — so a miss stays as cheap as it is now.

Suggested: Vesper — this is the page, the shell and the phone.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs