feat: use cache request provider support in ssr layer (#368)
Summary
Add full "use cache: request" provider support across the RSC, SSR,
and client (browser) environments, enabling per-request deduplication of
expensive computations that works transparently across rendering layers.
Problem
React Server Components and SSR run in separate environments (often
separate threads). When a function marked with "use cache: request" is
called from both a server component and a client component during the
same request, the function body
executes multiple times — once per environment. There's no mechanism to
share the cached result across the RSC → SSR boundary, and no way to
hydrate the value in the browser without prop-drilling.
Solution
This PR introduces a cross-environment shared cache protocol with two modes:
- Worker thread mode (production): A
SharedArrayBuffer-based append-only log withAtomics.wait/Atomics.notifyfor lock-free cross-thread communication. The RSC thread writes serialized cache entries; the SSR worker thread blocks until entries arrive. - In-process mode (dev/edge): A plain
Map-backed cache with the sameread/writeAPI, used when RSC and SSR run on the same thread. Values are serialized using new synchronous RSC Flight protocol helpers (syncToBuffer/syncFromBuffer) added to@lazarv/rsc, preserving all RSC-supported types (Date, Map, Set, RegExp, URL, typed arrays, React elements, etc.) across the thread boundary.
Key changes
@lazarv/rsc — Synchronous Flight serialization
syncToBuffer(value)— Synchronously serializes a value into an RSC Flight payload buffer (server)syncFromBuffer(buffer)— Synchronously deserializes an RSC Flight payload buffer (client/SSR)
@lazarv/react-server/cache — Request cache infrastructure
request-cache-shared.mjs(new) —SharedArrayBufferprotocol (createSharedRequestCache,attachSharedRequestCache) and in-process fallback (createInProcessRequestCache)cache/index.mjs— Server-sideuseCachegains a dedicated request-provider fast path with in-process dedup, thenable annotation for React'suse(), andSharedArrayBufferwrite-throughcache/ssr.mjs(new) — SSR-specificuseCachethat reads from the shared cache (SAB or in-process) and returns pre-resolved thenables for synchronoususe()consumptioncache/client.mjs— Browser-sideuseCachegains hydration support: reads fromself.__react_server_request_cache_entries__(injected inline script), deserializes withsyncFromBuffer, and returns stable pre-resolved thenables
SSR handlers — Per-request cache lifecycle
lib/dev/ssr-handler.mjsandlib/start/ssr-handler.mjs— Create a per-requestStorageCache+ shared cache (SAB or in-process) and inject them into the rendering context viaREQUEST_CACHE_CONTEXT/REQUEST_CACHE_SHAREDsymbols
server/render-dom.mjs — SSR worker integration
- Attaches the shared cache reader in the worker thread via
ContextStorageand a dedicatedRequestCacheStorageALS - Serializes resolved cache entries into inline
<script>tags for browser hydration, usingObject.assignfor incremental injection within streamed Suspense boundaries - Entries marked with
no-hydrateare excluded from the inline script
lib/plugins/use-cache-inline.mjs — Build plugin
- Recognizes
"use cache: request"in SSR environment builds (not just client) - Parses the new
no-hydratebare flag andhydrate=falseparameter - Emits synchronous wrapper functions for request-provider cache calls on the client/SSR side (avoids unnecessary async overhead for pre-resolved thenables)
server/request-cache-context.mjs (new)
- Standalone
AsyncLocalStoragefor the request cache reader, independent of the mainContextStoragechain — ensures cache modules find the reader even when the ALS chain breaks across bundled edge modules
no-hydrate directive
Functions can opt out of browser hydration while still benefiting from RSC↔SSR deduplication:
async function getServerOnlyData() {
"use cache: request; no-hydrate";
return { secret: process.env.API_KEY };
}
The cached value is shared between RSC and SSR during rendering, but is not embedded in the HTML — the client component will recompute the value in the browser.
Documentation
- Updated English and Japanese caching documentation with comprehensive coverage of request-scoped caching, cross-environment deduplication, hydration behavior, and the no-hydrate directive
Tests
7 new integration tests covering:
- Same-value deduplication within a single request
- Different values across separate requests (request-scoped, not persistent)
- Client component reading RSC-cached values without prop-drilling
- Date type preservation across RSC/SSR/client boundaries
- Hydrated content matching SSR output in the browser
- no-hydrate directive excluding cache entries from inline scripts
- Streamed Suspense boundaries with incremental cache entry injection