Skip to main content

Module ssr

Module ssr 

Source
Expand description

In-process SSR dispatch for mode:"ssr" routes (R449-F2; supersedes the bun-subprocess implementation R434-F3 shipped).

On startup the manifest is read; if any route is mode:"ssr" an mesofact_ssr::SsrRuntime is booted in this process. Each route’s render_entrypoint is pre-loaded into the isolate (paid once at startup), keyed by its derived URL prefix. The dev server then calls SsrChild::dispatch for each matching request — one V8 turn, no cross-process hop, no HTTP serialisation.

The bun subprocess + ssr-wrapper.ts + reverse-proxy machinery is gone. So is the requirement for bun on PATH; the dev server now works on a plain Rust toolchain.

@yah:relay(R444, “Plumb dev S3 coords into in-process SSR isolate so R2Adapter resolves at runtime in dev hotreload”) @yah:status(review) @yah:assignee(agent:bundle-anthropic-miravel) @yah:at(2026-08-13T19:29:07Z) @yah:next(“Thread the dev S3 coords from mesofact-dev’s main/ssr::spawn into SsrRuntime::start (crates/mesofact-dev/src/ssr.rs:380 + the SpawnOptions struct) and on into the mesofact-ssr isolate bootstrap.”) @yah:next(“Expose them to JS inside the isolate so @mesofact/runtime config.ts requireEnv(env, …) resolves: either inject a process.env shim (globalThis.process = { env: {…} }) in the bootstrap, or pass an explicit env map the runtime’s registerSourcesFromConfig consumes. Decide which the runtime should read (process.env shim is least-invasive to existing TS).”) @yah:next(“Verify end-to-end (this completes R490-F7’s PENDING criterion): a mesofact dev app with a [sources.r2] source doing r2.fetch/list inside an SSR render handler resolves against mesofact-dev’s s3s-fs in bun run dev and returns the bytes (PUT one, fetch it back through the rendered route).”) @yah:next(“Coordinate the env-var-name convention with R490-F7: today mesofact-dev injects conventional R2_* names; keep the isolate shim consistent (or have mesofact-dev read the workload’s mesofact.config.toml to learn the declared endpoint_env names).”) @yah:gotcha(“Cross-camp seam: this is the runtime-reads half of the PARENT camp’s R490-F7 (in the yah camp at /Users/leif/ss/yah). R490-F7 landed the dev S3 surface (s3s-fs in mesofact-dev) + BUILD-TIME r2 reads via the build subprocess env. The blocker for RUNTIME reads is here in the subcamp: the in-process V8 SSR runtime (SsrRuntime, R449-F2) can’t inherit process.env, so the @mesofact/runtime R2Adapter executing inside SSR render code never sees R2_ENDPOINT.”) @yah:gotcha(“mesofact-dev already computes the coords (DevS3::env_vars(): R2_ENDPOINT/R2_BUCKET/R2_ACCESS_KEY_ID/R2_SECRET_ACCESS_KEY) and writes .mesofact-dev/s3.json. The missing piece is getting those into the isolate’s JS env so registerSourcesFromConfig() can resolve [sources.r2].”) @yah:handoff(“Dev S3 coords now reach the in-process SSR isolate end-to-end. Rust: SpawnOptions gained env: Vec<(String,String)> + with_env() (crates/mesofact/src/ssr.rs); mesofact-dev’s main.rs starts DevS3 BEFORE the first ssr::spawn (was after) and threads dev_s3.env_vars() into both the initial spawn and the post-build respawn hook; mesofact/src/cli/serve.rs (prod) passes std::env::vars() since that process genuinely has yubaba-injected secrets.”) @yah:handoff(“mesofact::ssr::spawn resolves [sources.r2] from the workload’s mesofact.config.toml against opts.env (new resolve_r2_sources, fail-fast on a missing/empty declared env var, same boot-time contract mesofact-worker’s runWorker uses) and calls the new SsrRuntime::register_r2_sources before any route registers.”) @yah:handoff(“mesofact-ssr: SsrRuntime::start(env) Object.assign’s env onto globalThis.process.env right after the bootstrap script (before harness/any route loads) via a new mesofact-ssr:env execute_script. New Job::RegisterR2 + call_bridge (a call_harness variant with no bundle-URL keying) push resolved R2SourceCoords into the isolate.”) @yah:handoff(“ssr_runtime_shim.js’s r2()/sqlite() were a HARDCODED THROWING STUB pre-R444 (registerSourcesFromConfig was a no-op) — discovered mid-relay, not just missing env plumbing as the ticket text assumed. Replaced with a real per-isolate registry + a ported R2Adapter (BaseSource noTrack/timeout/tag-emit, S3 GET/LIST-v2, XML parser) using plain unsigned fetch() — works against mesofact-dev’s anonymous s3s-fs (AllowAllAccess) but NOT real Cloudflare R2 (needs SigV4). sqlite() stays a throwing stub (out of R444’s r2-only scope).”) @yah:handoff(“DISCOVERED + FIXED (blocking, in-blast-radius): DispatchTarget::dispatch (mesofact/src/ssr.rs) called the blocking SsrRuntime::dispatch round-trip INLINE on the caller’s async executor thread (‘isolate round-trip is fast, no I/O’ — no longer true once route code does real fetch()). On a single-threaded runtime that also hosts the I/O the route awaits (mesofact-dev’s own dev-S3 surface in this crate’s tests; any single-worker deployment), that’s a hard deadlock: the one thread able to service the connection is the one now blocked waiting on it. Fixed by Arc + spawn_blocking. Found via a raw-TCP-mock vs real-s3s-server A/B (mock worked, s3s hung) that isolated it to a same-thread scheduling conflict, not a protocol/TLS issue.”) @yah:handoff(“Also fixed: deno_fetch’s HTTP client needs a process-wide rustls CryptoProvider even for plain http:// (both aws-lc-rs and ring end up in the graph, so rustls can’t auto-pick) — first real fetch() panicked without it. Added rustls (aws-lc-rs feature) + a Once-guarded install in SsrRuntime::start.”) @yah:handoff(“Filed R820 (open) for the SigV4 gap: verified deno_crypto 0.271 hard-pins deno_core=0.410 vs this crate’s 0.404 line (cargo check fails on deno_v8’s v8/quickjs feature_error) — a whole-extension-set version bump, correctly out of R444’s blast radius.”) @yah:verify(“cargo test –workspace (oss/mesofact) — all green: mesofact 136, mesofact-ssr 3, mesofact-dev 15, plus every other workspace member, 0 failed”) @yah:verify(“cargo test -p mesofact-dev –features ssr ssr_route_resolves_r2_source_against_dev_s3 — the ticket’s exact verify criterion: an SSR route importing r2 from @mesofact/runtime, .fetch()-ing a key PUT into mesofact-dev’s real DevS3/s3s-fs surface through SsrSpawnOptions::with_env(dev.env_vars()), dispatched through the real in-process isolate, returns the bytes”) @yah:verify(“cargo build –workspace and cargo check -p mesofact-dev –no-default-features — clean (ssr feature stays fully optional)”) @yah:verify(“cargo clippy -p mesofact-ssr -p mesofact –features ssr -p mesofact-dev –features ssr — no new warnings on any touched file (one pre-existing unrelated warning in mesofact-core/proxy/router.rs, not touched here)”) @yah:gotcha(“Production R2 reads through this isolate are UNSIGNED (see R820) — fine against mesofact-dev’s anonymous s3s-fs, will 403 against real Cloudflare R2 until R820 lands the deno_core bump + vendored aws4fetch.”)

Structs§

HookEntry
One declared Mode 2 hook — schema mirror of mesofact_core::manifest::Hook.
LogBuffer
Bounded ring buffer for SSR runtime log lines. Kept around so the dev log surface that expected stderr lines from the bun child still has something to render — though the in-process runtime writes far fewer lines.
Manifest
Parsed manifest.json slice. Only the fields the SSR path cares about.
ResiliencePolicy
W181 v1 — schema mirror of mesofact_core::manifest::ResiliencePolicy.
RetryPolicy
RouteEntry
SpawnOptions
Options for spawn. The workload directory anchors the state dir; the gen_dir is the snapshot the SSR runtime should resolve entrypoints against.
SsrChild
In-process SSR dispatch + the data the router needs to use it.
SsrSlot
Swappable holder for the SSR child. The router reads current() on every request; the watcher’s post-build hook installs (or rotates) the child after each successful gen flip. Cheap to clone.

Constants§

DEFAULT_RESILIENCE_TIMEOUT_MS
Default per-attempt request timeout when resilience.timeout_ms is unset.

Functions§

derive_prefix
W173 derivation: prefix is everything up to the first :param or * segment. Non-parametric SSR routes use the full path.
matches_prefix
W173 segment-aware match: path == prefix || path.startsWith(prefix + "/").
spawn
Inspect the manifest; if it has any SSR route, boot an SsrRuntime, pre- load each route’s render_entrypoint, and return a SsrChild handle. Returns Ok(None) when no SSR routes are declared — the caller serves static only.