Bundle sizes on worker builder events
Status summary#
Implemented, tested, and verified against a real local dev build. Coordinator
events now carry sizes (module/asset bytes + counts) and a human-readable
source descriptor. Verification already produced the intended kind of
insight: the main stateless worker.ts bundle is NOT tiny today (~604 KiB,
barely bigger than the guestbook app's ~572 KiB) — see notes.
Ask (from Misha, lightly interpreted)#
can we add bundle sizes on builder events. all the bundles should have different size. main stateless worker should be tiny. github-review-bot for example should be different
"Builder events" = the dynamic worker build coordinator telemetry
(worker-build.started/coalesced/settled/reused from
observeCoordinatorEvent in
apps/os/src/domains/workers/worker-build-coordinator-durable-object.ts).
Today they carry only buildKey (an opaque sha), durationMs, waiters,
outcome. You can't tell from the logs which worker was built nor how big the
bundle came out — so you can't verify that the main stateless worker.ts
bundle is tiny while e.g. the github review bot / todo app bundles are bigger
and all different.
Plan#
-
artifact-store.ts: a pureworkerBuildArtifactSizes(artifact)returning{ moduleBytes, moduleCount, assetBytes, assetCount }— UTF-8 bytes over every module representation (string /js/cjs/text/json) and every asset; derived on demand so no artifact schema bump -
worker-build-coordinator.ts: events gainsource(all kinds) andsizes(settled+built, reused) —describeWorkerBuildSourcerenderscreateWorker:worker.ts/createApp:server=…,client=…; sizes computed once at settle and cached with the retained artifact for reuse events - Coordinator + sizes unit tests — extended
worker-build-coordinator.test.ts(event payload pinned incl. multibyte UTF-8) andartifact-store.test.ts - Eyeball a real build log line locally and paste a sample into the PR
body — done via
pnpm dev+pnpm getin+ curling thetestproject hosts; sample below and in PR #2308
Assumptions (made while Misha was AFK)#
- Raw (uncompressed) bytes, not gzip. These bundles go into Worker Loader, not over the wire to browsers, so raw size is the operative metric and it's deterministic. Gzip is an easy follow-up if wanted.
- No per-module breakdown on the event — logs have size budgets and the headline numbers answer "are these bundles actually different". A biggest-modules breakdown is a possible follow-up.
- Coordinator events are the right home (not the KV/memo cache-hit paths in
worker-loader.ts): "builder events" happen when something builds. Cache hits reuse an artifact whose size was already reported at build time. - No UI surface for now — this is log/observability only.
Implementation notes#
Real events from local dev (this branch, seeded template = packaged starter apps):
worker-build.settled source=createWorker:worker.ts
sizes={ assetBytes: 0, assetCount: 0, moduleBytes: 618735, moduleCount: 1 } durationMs=4142
worker-build.settled source=createWorker:node_modules/iterate/dist/starter-apps/guestbook/configured-worker.mjs
sizes={ assetBytes: 0, assetCount: 0, moduleBytes: 585958, moduleCount: 1 } durationMs=1169
Finding: the sizes differ (good — each entry gets its own bundle), but the
main stateless worker is ~604 KiB, not tiny. Plausible causes worth chasing in
a follow-up: the template worker.ts imports every starter-app connector
top-level, and the iterate sdk itself may not tree-shake well (single
sdk.ts barrel). Now that the sizes are on every build event, that
investigation has numbers to steer by.
Follow-up: per-package breakdown (same PR)#
After the zod discovery, Misha asked for the broken-down stats on the events
themselves. sizes now includes breakdown: module bytes bucketed by npm
package (via esbuild // <path> section comments; pnpm-store paths resolve to
their package name) or source path, largest first, top 8 with an (other)
rollup. Comment-less/minified text falls back to the module's own name. Live:
source=createWorker:worker.ts
breakdown={ zod: 516551, '@iterate-com/capnweb': 84041, iterate: 13309,
'virtual:worker.ts': 3114, 'bundle.js': 1720 }
Prebundled dist entries collapse into their package bucket (guestbook →
{ iterate: 585958 }) — attribution can only be as fine as the input's
section comments.