Found gating server memory in stacksjs/harness (harness#11 has the full history). Rendering the same ~1,700-line view against identical props, per render:
bun:jscheapStats shows only ~1.1MB of JS-heap allocation and nothing retained after GC — the projection is fine.- Process RSS climbs 30→209MB over the first ten renders and plateaus around 267MB, never returning to the OS — allocator high-water, not a leak. Forced
Bun.gc(true)after every render caps almost nothing (253→224MB over 60 renders), so the churn is native-side (transpiler/bundler/regex/WTF-string territory), invisible to the JS heap. - A template containing one component tag (
<Sidebar>from @stacksjs/components) produces a 211KB page — 192KB of it inline script — and burns ~9MB/render. A trivial<div></div>still churns ~1.4MB/render.
Where it goes: the client-script bundler's disk cache hits every render (verified with STX_DEBUG), but everything downstream of it re-runs per render on unchanged inputs:
- the ~190KB cached bundle is re-read from disk, re-wrapped and re-inlined,
- the DOM-API validator re-scans the full client script (its warnings print once per render),
- scope ids (
stx_sidebar_<n>_<rand>) regenerate each render, so the output is never byte-identical and can't be memoized by callers from the outside, - the island wrapper is rebuilt.
The compiled island artifact is a function of (template content, config) only — the scope id is already threaded in as an IIFE argument (})(\"stx_sidebar_4_15gdi7\")), so the whole post-bundler artifact looks memoizable keyed by content hash, with the scope id (and data-stx-props) interpolated at render time. That would turn SSR of an unchanged template into string interpolation and cut per-render allocation by roughly an order of magnitude.
Numbers from bun 1.3.14, stx 0.2.188, macOS arm64. Reproduction is just renderView of any island-bearing view in a loop, watching ps -o rss= against heapStats().heapSize.
Downstream we now cache rendered pages keyed by a state revision (stacksjs/harness@75482a4), which covers polling — but every genuine state change still pays the full churn, and any hot stx SSR surface without an equivalent cache pays it per request.
🤖 Generated with Claude Code