stoneware

Documentation

v0.1.8

The client chunks finally reach a deployed site, and a URL can no longer make the server answer 500.

0.1.8

Published. Two fixes, and the first of them is the one that had been quietly breaking deployed sites since islands existed.

The client chunks finally reach a deployed site

  before                          now
  ──────────────────────────      ──────────────────────────
  GET /             200           GET /             200
  GET /_stoneware/                GET /_stoneware/
      styles.css    404               styles.css    200
      Counter.js    404               Counter.js    200

  a site that renders and         a site that renders
  arrives unstyled and inert      and works
a Bun server on Vercel

0.1.7 claimed this was fixed by copying the chunks into public/. It was not, and the reason is worth stating plainly: Vercel collects public/ from the repository, so a directory the build creates is never in the snapshot — and it is gitignored build output, so committing it is not an answer either. The copy landed somewhere that had already been read.

From 0.1.8, stoneware build --target vercel carries the chunks inside the server bundle as values. That is the one form that cannot be lost, because a bundler that traces imports follows a static import by definition. It is the same fix the route table, the island manifest and the config each needed before it — the fifth instance of one mistake, finally applied at the level it belonged.

  0.1.4   routes/ rescanned at runtime
  0.1.5   islands.json read at runtime
  0.1.6   stoneware.config.ts imported at runtime
  0.1.8   the client chunks themselves

  every one a path assembled while running,
  and every one invisible until a deploy
the whole family, in order
  • Opt-in, and set only by --target vercel. A VPS or container ships the directory itself and should not carry a second copy of every chunk.
  • Carried as base64, because a stylesheet can pull a font or an image into the same directory and those are binary.
  • Bundle cost is roughly the size of your client output — about 60 kB on a site with five islands and 33 kB of CSS.
How this went unnoticed for four releases: the documentation site styles itself from a hand-written public/styles.css, so it never used the pipeline it documents. The project best placed to catch this was structurally incapable of it.

A URL can no longer make the server answer 500

found while checking the edge cases of the fix abovetxt
GET /_stoneware/toString
  500  TypeError: Received function toString

The inlined chunks arrived as a plain object and the lookup key comes straight off the URL. A plain object answers for keys nobody put in it — toString, constructor, __proto__, valueOf — so the presence check passed and a function was handed to a base64 decoder. An unauthenticated request turning into a server error.

They are held in a Map now, which has no inherited keys, so "was this chunk built" has exactly one answer. Reverting the fix fails nine tests.

This existed for about an hour and never shipped. It is written up because the shape recurs: any lookup keyed on user input against a plain object has it, and the reflex should be a Map or Object.hasOwn rather than a truthiness check.

Also in 0.1.8

  • ARCHITECTURE.md, for anyone changing the framework rather than using it: the request pipeline as the security model, the render model, the relocatability bug family and the rule that follows from it, and the measurements that are easy to undo by accident.
  • stoneware doctor now warns on 0.1.7 as well, since the deploy bug above was not actually fixed there.

Earlier versions

0.1.7 and 0.1.6 each have their own page, 0.1.5 and 0.1.4 share one, and 0.1.3 and 0.1.2 are on past releases.

Something wrong in the framework itself rather than the page? Open an issue on GitHub.