Problem
SSR action-result seeding (#472) does not work on Bun. It rides Node's synchronous module.registerHooks load hook, which installs the transparent 'use server' facade that captures each action result during an SSR render and serializes it into the page; the generated RPC stub then reads that seed on its first client call, eliminating the on-hydration re-fetch. Bun has no module.registerHooks, so registerSeedHooks() no-ops there (fail-open, logged once).
Consequence: on Bun, every SHIPPING async-render component (one that also carries a signal / @event, so it is not elided) re-issues its action RPC once on hydration, a round trip Node elides via the seed. Correctness is unaffected (the first paint already has the SSR data, stale-while-revalidate, the re-fetch returns the same value), but it is a real per-component network cost and a Node/Bun parity gap. Display-only async-render components are elided, so they are unaffected.
Design / approach
The #472 facade is deliberately build-free (no source transform): it wraps 'use server' exports in a Proxy at module load via module.registerHooks. To reach parity on Bun without a build step, host the same facade through a Bun-native seam:
Bun.plugin loader (preferred): a Bun loader plugin can intercept the .server.{ts,js} module load and return the same re-exporting facade the Node hook produces, feeding the ambient AsyncLocalStorage seed collector. Keep action-seed.js runtime-neutral and select the install mechanism by serverRuntime() (the listener-core seam), the same way the listener shells are chosen.
- Alternatives to weigh: a Bun
--preload registration, or a server-side wrap at the action-registration boundary (where the RPC index is built) instead of at module load, which might be runtime-neutral and could simplify BOTH shells.
- Must stay consume-once, fail-open, keyed by action-hash + fn + serialized args, identical wire to the Node path, so a page rendered on either runtime seeds the same way.
Distinct from #528 (the correctness bug where a signal-driven async-render component goes inert after the on-hydration re-fetch); this issue is the perf/parity gap that erases the re-fetch on Bun in the first place.
Acceptance criteria
Problem
SSR action-result seeding (#472) does not work on Bun. It rides Node's synchronous
module.registerHooksload hook, which installs the transparent'use server'facade that captures each action result during an SSR render and serializes it into the page; the generated RPC stub then reads that seed on its first client call, eliminating the on-hydration re-fetch. Bun has nomodule.registerHooks, soregisterSeedHooks()no-ops there (fail-open, logged once).Consequence: on Bun, every SHIPPING async-render component (one that also carries a signal /
@event, so it is not elided) re-issues its action RPC once on hydration, a round trip Node elides via the seed. Correctness is unaffected (the first paint already has the SSR data, stale-while-revalidate, the re-fetch returns the same value), but it is a real per-component network cost and a Node/Bun parity gap. Display-only async-render components are elided, so they are unaffected.Design / approach
The #472 facade is deliberately build-free (no source transform): it wraps
'use server'exports in a Proxy at module load viamodule.registerHooks. To reach parity on Bun without a build step, host the same facade through a Bun-native seam:Bun.pluginloader (preferred): a Bun loader plugin can intercept the.server.{ts,js}module load and return the same re-exporting facade the Node hook produces, feeding the ambientAsyncLocalStorageseed collector. Keepaction-seed.jsruntime-neutral and select the install mechanism byserverRuntime()(the listener-core seam), the same way the listener shells are chosen.--preloadregistration, or a server-side wrap at the action-registration boundary (where the RPC index is built) instead of at module load, which might be runtime-neutral and could simplify BOTH shells.Distinct from #528 (the correctness bug where a signal-driven async-render component goes inert after the on-hydration re-fetch); this issue is the perf/parity gap that erases the re-fetch on Bun in the first place.
Acceptance criteria
action-seed.jsselects the install mechanism by runtime; the Nodemodule.registerHookspath is unchanged.SEED_SKIP) run on Bun.