feat: serve static assets from the build manifest - #16908
Conversation
|
Install the latest version of pnpm add https://pkg.svelte.dev/@sveltejs/kit/c/ccb4bc970fd47aa4fee43e1cf4363f282e62ac05Open in |
🦋 Changeset detectedLatest commit: ccb4bc9 The changes in this PR will be included in the next version bump. This PR includes changesets to release 2 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
81ea893 to
1192a76
Compare
050fcd5 to
59ff3e6
Compare
|
I'm going to merge the downstack PR of this one because it's straightforward and gets rid of a dep, but this one needs some additional discussion. It makes some tradeoffs that might be worth it, but I don't think are straightforward enough to just yolo into without the broader team's input. We can talk about this at the maintainer's meeting on Friday. Stuff I see while going through (some of these are existing Generated static output is now immutableCurrently, If a deployment adds a file to the build output, the new file is never served. Deleting or replacing a file has similar unpredictable consequences. This is relevant to (off the top of my head):
This isn't necessarily an unacceptable model, but it's a major compatibility break from what we currently have. A compromise would be to manifest the known path set but obtain size and validator metadata at startup... but I'm not sure how much better that would really be.
|
|
Thanks! On the HTTP semantics, everything you listed reproduces on this build and I'll push fixes: q-value parsing for I took a look at sirv's source code. sirv sends I measured on this branch vs the sirv build, N × 3 KB compressible files plus 3 × 5 MB binaries, Node 22, loopback, five cold starts each:
At N=1000 the build difference is 0.8 s and everything else is within noise except the manifest (246 KB vs 18 KB). Startup and resident memory go the other way from what you expected because sirv's With On the freeze itself, sirv already freezes at boot: |
68a047e to
f6397a4
Compare
|
With the aliases built at boot and the variants storing only a size, the manifest is ~150 bytes per file (1.5 MB at 10k files, down from 2.4 MB). Time to listening at 10k files is 510 to 652 ms against sirv's 536 to 689, with the walk being the same A file overwritten after the build is served with its new content, uncompressed, because the |
f6397a4 to
b76f6b9
Compare
Right, but freezing at boot and freezing at buildtime are very different things -- freezing at boot still allows you to, for example, build the same app once but swap out a JSON configuration file and deploy it multiple places. |
|
Exactly. The implementation should not treat the build manifest as an immutable allowlist: it needs to validate recorded files and discover additions at startup, so deployment-time replacement or addition of static configuration files remains supported. |
Since d554932, recorded files are stat-ed at startup and rehashed on a size or mtime change, added files under the client dir are discovered, deleted ones dropped. |
|
I haven't looked too closely at the code because unfortunately it needs a rebase — the recent adapter API changes (dropping Having said that: are we sure we want to do boot time rather than build time? When would you 'swap out a JSON configuration file' in your |
|
The mime table is manifest.mimeTypes, which kit already ships and fetch.js already uses, so neither option adds a lookup. I lean build-time too, every other adapter already serves exactly what the build produced. Dropping ccb4bc9 gets the build-time version back. |
Key the prerendered table by the exact paths kit prerendered, which removes the prerendered set and the request-time gate that filtered out unreachable alias keys. The manifest now ships one string per prerendered page instead of four. Fold create_asset_map into serve_static and resolve content types once at boot instead of per request.
An unhandled read stream error (a file deleted from the build output, EMFILE) crashed the process. Headers are already sent by then, so drop the connection.
…from the original hash
b76f6b9 to
ccb4bc9
Compare
Closes #16565, fixes #11766.
Serving static assets from build manifest is 10 to 20% faster than sirv on a local loopback benchmark, winning all eight comparisons across two interleaved rounds of plain, brotli, icon, and extensionless-alias requests at 20 concurrent keep-alive connections.
Currently, the static file server re-derives at request time what the build already computed.
adapter-nodenow writes two tables into the emitted manifest, one per mount, mapping every servable pathname (including precomputedfoo.html/foo/index.htmlaliases in sirv's resolution order) to its file, size, content-hash ETag and compressed-variant sizes/hashes. Serving (src/static.js) is a map lookup and a stream,Varyis sent exactly when a variant exists (which retiresuncompressed_extensionsand the over-send #16566 had to work around), and the range handling is RFC 7233 (bytes=0-0returned the whole file under sirv,bytes=-3was off by one — the former is the probe HTML5 video and PDF.js use).sirv,@polka/urland their transitivemrmime/totalistdrop out of the adapter, along with the 27kB sirv chunk in the build output.manifest.mimeTypesis now also seeded from client output extensions (same mechanism as the prerendered seeding from #16564), since.js/.css/imported-asset types previously came from sirv's bundled mrmime.Deliberate behavior changes: ETags are content hashes instead of mtime-derived (stable across rebuilds of identical files),
Last-Modifiedis no longer sent (deploy processes reset mtimes, and the ETag supersedes it),Accept-Rangesis always advertised, and files with unknown extensions omitContent-Typeinstead of sending an empty one. Base-path key construction follows the same${base}composition the oldasset_dirused, but no adapter-node test app exercises a base path - that's a pre-existing coverage hole.Sits on #16907; only the last commit is new.
Please don't delete this checklist! Before submitting the PR, please make sure you do the following:
Tests
pnpm testand lint the project withpnpm lintandpnpm checkChangesets
pnpm changesetand following the prompts. Changesets that add features should beminorand those that fix bugs should bepatch. Please prefix changeset messages withfeat:,fix:, orchore:.