@rangojs/router 0.21.0 → 0.22.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (244) hide show
  1. package/dist/bin/rango.js +13 -1
  2. package/dist/types/browser/event-controller.d.ts +30 -13
  3. package/dist/types/browser/navigation-store.d.ts +9 -0
  4. package/dist/types/browser/partial-update.d.ts +2 -1
  5. package/dist/types/browser/prefetch/default-strategy.d.ts +16 -20
  6. package/dist/types/browser/react/NavigationProvider.d.ts +14 -1
  7. package/dist/types/browser/react/context.d.ts +14 -0
  8. package/dist/types/browser/rsc-router.d.ts +13 -2
  9. package/dist/types/browser/types.d.ts +10 -0
  10. package/dist/types/build/generate-manifest.d.ts +2 -0
  11. package/dist/types/cache/background-task.d.ts +22 -0
  12. package/dist/types/cache/cache-key-utils.d.ts +50 -5
  13. package/dist/types/cache/cache-policy.d.ts +17 -0
  14. package/dist/types/cache/cache-scope.d.ts +1 -1
  15. package/dist/types/cache/cf/cf-cache-store.d.ts +7 -1
  16. package/dist/types/cache/document-cache.d.ts +8 -5
  17. package/dist/types/cache/handle-snapshot.d.ts +23 -0
  18. package/dist/types/cache/index.d.ts +1 -1
  19. package/dist/types/cache/invalidation-order.d.ts +14 -0
  20. package/dist/types/cache/memory-segment-store.d.ts +33 -1
  21. package/dist/types/cache/shell-snapshot.d.ts +10 -1
  22. package/dist/types/cache/store-scope.d.ts +22 -0
  23. package/dist/types/cache/tag-invalidation.d.ts +7 -6
  24. package/dist/types/cache/types.d.ts +63 -14
  25. package/dist/types/cache/vercel/vercel-cache-store.d.ts +7 -1
  26. package/dist/types/errors.d.ts +6 -0
  27. package/dist/types/handles/deferred-resolution.d.ts +7 -0
  28. package/dist/types/internal-suspense-audit.d.ts +1 -0
  29. package/dist/types/loader-store.d.ts +4 -8
  30. package/dist/types/outlet-provider.d.ts +2 -1
  31. package/dist/types/prerender/cloudflare.d.ts +49 -0
  32. package/dist/types/prerender/create-prerender-trigger.d.ts +121 -0
  33. package/dist/types/prerender/index.d.ts +12 -0
  34. package/dist/types/prerender/memory-prerender-store.d.ts +29 -0
  35. package/dist/types/prerender/on-demand.d.ts +272 -0
  36. package/dist/types/prerender/producer-guard.d.ts +33 -0
  37. package/dist/types/prerender/shell-manifest-key.d.ts +1 -18
  38. package/dist/types/prerender/warm-request.d.ts +65 -0
  39. package/dist/types/prerender/warm.d.ts +65 -0
  40. package/dist/types/prerender/writable-store.d.ts +170 -0
  41. package/dist/types/prerender.d.ts +27 -0
  42. package/dist/types/reverse.d.ts +1 -1
  43. package/dist/types/route-content-wrapper.d.ts +3 -1
  44. package/dist/types/router/error-handling.d.ts +2 -0
  45. package/dist/types/router/handler-context.d.ts +1 -1
  46. package/dist/types/router/match-middleware/cache-lookup.d.ts +14 -0
  47. package/dist/types/router/pattern-matching.d.ts +2 -0
  48. package/dist/types/router/prefetch-default.d.ts +4 -4
  49. package/dist/types/router/prerender-match.d.ts +23 -1
  50. package/dist/types/router/route-trie-builder.d.ts +8 -1
  51. package/dist/types/router/router-context.d.ts +1 -0
  52. package/dist/types/router/router-interfaces.d.ts +30 -0
  53. package/dist/types/router/router-options.d.ts +24 -0
  54. package/dist/types/router/segment-resolution/fresh.d.ts +7 -0
  55. package/dist/types/router/segment-resolution/loader-cache.d.ts +2 -1
  56. package/dist/types/router/segment-resolution/revalidation.d.ts +5 -1
  57. package/dist/types/router/segment-resolution/streamed-handler-boundary.d.ts +43 -0
  58. package/dist/types/router/trie-matching.d.ts +2 -0
  59. package/dist/types/rsc/shell-build-manifest.d.ts +6 -4
  60. package/dist/types/rsc/shell-capture-constants.d.ts +20 -5
  61. package/dist/types/rsc/shell-capture.d.ts +14 -1
  62. package/dist/types/rsc/shell-serve.d.ts +2 -2
  63. package/dist/types/rsc/ssr-module-loader.d.ts +15 -0
  64. package/dist/types/rsc/types.d.ts +8 -0
  65. package/dist/types/segment-boundary-content.d.ts +17 -0
  66. package/dist/types/segment-loader-promise.d.ts +2 -6
  67. package/dist/types/server/build-version-table.d.ts +9 -0
  68. package/dist/types/server/context.d.ts +12 -4
  69. package/dist/types/server/handle-store.d.ts +2 -0
  70. package/dist/types/server/request-context.d.ts +63 -2
  71. package/dist/types/suspense-audit-react.d.ts +26 -0
  72. package/dist/types/suspense-audit.d.ts +157 -0
  73. package/dist/types/testing/action-form.d.ts +34 -0
  74. package/dist/types/testing/flight.entry.d.ts +2 -0
  75. package/dist/types/testing/index.d.ts +4 -0
  76. package/dist/types/testing/internal/server-modules.d.ts +8 -0
  77. package/dist/types/testing/serve-shell-request.d.ts +34 -4
  78. package/dist/types/testing/shell-status.d.ts +14 -6
  79. package/dist/types/testing/vitest-stubs/plugin-rsc.d.ts +9 -6
  80. package/dist/types/thenable-status.d.ts +6 -0
  81. package/dist/types/types/handler-context.d.ts +7 -0
  82. package/dist/types/types/route-entry.d.ts +5 -0
  83. package/dist/types/urls/path-helper.d.ts +18 -0
  84. package/dist/types/vite/discovery/state.d.ts +11 -0
  85. package/dist/types/vite/inject-client-debug.d.ts +6 -25
  86. package/dist/types/vite/utils/bundle-analysis.d.ts +6 -2
  87. package/dist/vite/index.js +180 -497
  88. package/package.json +13 -3
  89. package/skills/cache-guide/SKILL.md +18 -17
  90. package/skills/caching/SKILL.md +40 -4
  91. package/skills/catalog.json +1 -1
  92. package/skills/cloudflare/SKILL.md +131 -0
  93. package/skills/comparison/references/framework-comparison.md +1 -1
  94. package/skills/deployment-caching/SKILL.md +33 -12
  95. package/skills/document-cache/SKILL.md +16 -14
  96. package/skills/hooks/SKILL.md +22 -0
  97. package/skills/hooks/handle-and-actions.md +3 -4
  98. package/skills/hooks/navigation.md +10 -0
  99. package/skills/hooks/state.md +5 -4
  100. package/skills/host-router/SKILL.md +18 -0
  101. package/skills/loader/SKILL.md +6 -4
  102. package/skills/middleware/SKILL.md +1 -1
  103. package/skills/migrate-nextjs/SKILL.md +82 -9
  104. package/skills/ppr/SKILL.md +31 -8
  105. package/skills/prerender/SKILL.md +645 -8
  106. package/skills/rango/SKILL.md +1 -0
  107. package/skills/response-routes/SKILL.md +5 -5
  108. package/skills/route/SKILL.md +29 -0
  109. package/skills/testing/SKILL.md +2 -1
  110. package/skills/testing/cache-prerender.md +18 -17
  111. package/skills/testing/serve-shell-request.md +104 -8
  112. package/skills/use-cache/SKILL.md +9 -5
  113. package/skills/vercel/SKILL.md +2 -0
  114. package/skills/view-transitions/SKILL.md +9 -4
  115. package/src/browser/event-controller.ts +69 -25
  116. package/src/browser/link-interceptor.ts +2 -2
  117. package/src/browser/navigation-bridge.ts +75 -35
  118. package/src/browser/navigation-store.ts +26 -0
  119. package/src/browser/navigation-transaction.ts +9 -2
  120. package/src/browser/network-error-handler.ts +2 -0
  121. package/src/browser/partial-update.ts +48 -12
  122. package/src/browser/prefetch/default-strategy.ts +22 -42
  123. package/src/browser/react/Link.tsx +37 -42
  124. package/src/browser/react/NavigationProvider.tsx +63 -3
  125. package/src/browser/react/context.ts +17 -0
  126. package/src/browser/react/location-state.ts +5 -19
  127. package/src/browser/react/use-action.ts +4 -1
  128. package/src/browser/react/use-handle.ts +4 -20
  129. package/src/browser/react/use-link-status.ts +4 -3
  130. package/src/browser/react/use-navigation.ts +4 -1
  131. package/src/browser/react/use-params.ts +4 -1
  132. package/src/browser/react/use-pathname.ts +4 -1
  133. package/src/browser/react/use-search-params.ts +5 -3
  134. package/src/browser/react/use-segments.ts +36 -54
  135. package/src/browser/rsc-router.tsx +79 -39
  136. package/src/browser/segment-reconciler.ts +3 -3
  137. package/src/browser/server-action-bridge.ts +26 -6
  138. package/src/browser/types.ts +11 -0
  139. package/src/build/generate-manifest.ts +17 -0
  140. package/src/build/merge-full-manifests.ts +5 -0
  141. package/src/build/route-trie.ts +1 -0
  142. package/src/cache/background-task.ts +61 -0
  143. package/src/cache/cache-key-utils.ts +91 -12
  144. package/src/cache/cache-policy.ts +11 -2
  145. package/src/cache/cache-runtime.ts +23 -23
  146. package/src/cache/cache-scope.ts +38 -36
  147. package/src/cache/cf/cf-cache-store.ts +16 -11
  148. package/src/cache/document-cache.ts +35 -14
  149. package/src/cache/handle-snapshot.ts +50 -0
  150. package/src/cache/index.ts +1 -0
  151. package/src/cache/invalidation-order.ts +53 -0
  152. package/src/cache/memory-segment-store.ts +52 -1
  153. package/src/cache/shell-snapshot.ts +17 -0
  154. package/src/cache/store-scope.ts +36 -0
  155. package/src/cache/tag-invalidation.ts +7 -5
  156. package/src/cache/types.ts +69 -14
  157. package/src/cache/vercel/vercel-cache-store.ts +20 -6
  158. package/src/client.tsx +40 -3
  159. package/src/errors.ts +13 -6
  160. package/src/handles/deferred-resolution.ts +7 -2
  161. package/src/internal-suspense-audit.ts +12 -0
  162. package/src/loader-store.ts +4 -9
  163. package/src/outlet-provider.tsx +25 -1
  164. package/src/prerender/cloudflare.ts +130 -0
  165. package/src/prerender/create-prerender-trigger.ts +780 -0
  166. package/src/prerender/index.ts +39 -0
  167. package/src/prerender/memory-prerender-store.ts +115 -0
  168. package/src/prerender/on-demand.ts +339 -0
  169. package/src/prerender/producer-guard.ts +66 -0
  170. package/src/prerender/shell-manifest-key.ts +12 -17
  171. package/src/prerender/warm-request.ts +154 -0
  172. package/src/prerender/warm.ts +193 -0
  173. package/src/prerender/writable-store.ts +278 -0
  174. package/src/prerender.ts +29 -0
  175. package/src/reverse.ts +1 -1
  176. package/src/route-content-wrapper.tsx +107 -13
  177. package/src/router/error-handling.ts +12 -12
  178. package/src/router/find-match.ts +1 -0
  179. package/src/router/handler-context.ts +2 -0
  180. package/src/router/intercept-resolution.ts +15 -8
  181. package/src/router/loader-resolution.ts +2 -10
  182. package/src/router/match-middleware/background-revalidation.ts +2 -0
  183. package/src/router/match-middleware/cache-lookup.ts +230 -38
  184. package/src/router/pattern-matching.ts +14 -0
  185. package/src/router/prefetch-default.ts +4 -4
  186. package/src/router/prerender-match.ts +460 -352
  187. package/src/router/route-trie-builder.ts +9 -0
  188. package/src/router/router-context.ts +1 -0
  189. package/src/router/router-interfaces.ts +33 -0
  190. package/src/router/router-options.ts +30 -0
  191. package/src/router/segment-resolution/fresh.ts +20 -19
  192. package/src/router/segment-resolution/loader-cache.ts +15 -17
  193. package/src/router/segment-resolution/revalidation.ts +34 -24
  194. package/src/router/segment-resolution/streamed-handler-boundary.ts +133 -0
  195. package/src/router/trie-matching.ts +3 -0
  196. package/src/router.ts +107 -1
  197. package/src/rsc/handler.ts +51 -4
  198. package/src/rsc/progressive-enhancement.ts +5 -1
  199. package/src/rsc/response-cache-serve.ts +10 -4
  200. package/src/rsc/rsc-rendering.ts +53 -11
  201. package/src/rsc/shell-build-manifest.ts +10 -8
  202. package/src/rsc/shell-capture-constants.ts +39 -7
  203. package/src/rsc/shell-capture.ts +108 -19
  204. package/src/rsc/shell-serve.ts +12 -6
  205. package/src/rsc/ssr-module-loader.ts +26 -0
  206. package/src/rsc/types.ts +8 -0
  207. package/src/segment-boundary-content.ts +25 -0
  208. package/src/segment-loader-promise.ts +28 -38
  209. package/src/segment-system.tsx +117 -57
  210. package/src/server/build-version-table.ts +15 -0
  211. package/src/server/context.ts +40 -17
  212. package/src/server/cookie-store.ts +5 -0
  213. package/src/server/handle-store.ts +13 -0
  214. package/src/server/loader-registry.ts +6 -1
  215. package/src/server/request-context.ts +87 -1
  216. package/src/ssr/ssr-root.tsx +20 -13
  217. package/src/suspense-audit-react.tsx +98 -0
  218. package/src/suspense-audit.ts +1030 -0
  219. package/src/testing/action-form.ts +66 -0
  220. package/src/testing/flight-runtime.d.ts +27 -0
  221. package/src/testing/flight.entry.ts +3 -0
  222. package/src/testing/index.ts +19 -0
  223. package/src/testing/internal/server-modules.ts +32 -0
  224. package/src/testing/render-route.tsx +25 -12
  225. package/src/testing/serve-shell-request.ts +190 -34
  226. package/src/testing/shell-status.ts +17 -6
  227. package/src/testing/vitest-stubs/plugin-rsc.ts +37 -11
  228. package/src/thenable-status.ts +10 -0
  229. package/src/types/handler-context.ts +7 -0
  230. package/src/types/route-entry.ts +6 -0
  231. package/src/urls/path-helper.ts +69 -9
  232. package/src/use-loader.tsx +57 -30
  233. package/src/vite/discovery/bundle-postprocess.ts +4 -0
  234. package/src/vite/discovery/discover-routers.ts +15 -0
  235. package/src/vite/discovery/shell-prerender-phase.ts +5 -5
  236. package/src/vite/discovery/state.ts +17 -1
  237. package/src/vite/inject-client-debug.ts +42 -20
  238. package/src/vite/plugins/virtual-entries.ts +8 -1
  239. package/src/vite/router-discovery.ts +14 -0
  240. package/src/vite/utils/bundle-analysis.ts +39 -7
  241. package/dist/types/browser/react/subscribe-to-nothing.d.ts +0 -7
  242. package/dist/types/segment-content-promise.d.ts +0 -13
  243. package/src/browser/react/subscribe-to-nothing.ts +0 -7
  244. package/src/segment-content-promise.ts +0 -67
@@ -3,6 +3,7 @@ import { AsyncLocalStorage } from "node:async_hooks";
3
3
  import type { ReactNode } from "react";
4
4
  import type { PartialCacheOptions, ErrorBoundaryHandler, Handler, LoaderDefinition, MiddlewareFn, NotFoundBoundaryHandler, ShouldRevalidateFn, TransitionConfig } from "../types";
5
5
  import type { ContextVar } from "../context-var.js";
6
+ import { type WarmContext } from "../prerender/warm-request.js";
6
7
  /**
7
8
  * Performance metric entry for a single measured operation
8
9
  *
@@ -219,8 +220,11 @@ export type EntryData = ({
219
220
  getParams?: (ctx: any) => Promise<any[]> | any[];
220
221
  options?: {
221
222
  concurrency?: number;
223
+ onDemand?: import("../prerender/on-demand.js").OnDemandOption;
222
224
  };
223
225
  };
226
+ /** Set when the route opted into on-demand prerender (Prerender(..., { onDemand })) */
227
+ isOnDemand?: true;
224
228
  /** Set when route is wrapped with Passthrough() — has a separate live handler */
225
229
  isPassthrough?: true;
226
230
  /** Live handler for runtime fallback (only set on Passthrough routes) */
@@ -341,6 +345,7 @@ export declare const getContext: () => {
341
345
  getStore: () => HelperContext;
342
346
  getParent: () => EntryData | null;
343
347
  getOrCreateStore: (forRoute?: string) => HelperContext;
348
+ runIsolated: <T>(forRoute: string, callback: () => T) => T;
344
349
  getNextIndex: (type: (string & {}) | "layout" | "parallel" | "middleware" | "revalidate") => string;
345
350
  getShortCode: (type: "layout" | "parallel" | "route" | "loader" | "cache") => string;
346
351
  run: <T>(namespace: string, parent: EntryData | null, callback: (...args: any[]) => T) => T;
@@ -483,12 +488,12 @@ export interface IdentityReadWording {
483
488
  * response directives (invalidateClientCache(), keepClientCache()) record
484
489
  * nothing and are not captured reads: they take refuseInCacheScope alone.
485
490
  */
486
- export declare function guardIdentityRead(ctx: unknown, surface: string, wording: IdentityReadWording): void;
491
+ export declare function guardIdentityRead(ctx: WarmContext | null | undefined, surface: string, wording: IdentityReadWording): void;
487
492
  /**
488
493
  * Steps 2 and 3 of guardIdentityRead: throw when a "use cache" body or a
489
494
  * cache() boundary would store what `surface` produces.
490
495
  */
491
- export declare function refuseInCacheScope(surface: string, { verb, fix }: IdentityReadWording): void;
496
+ export declare function refuseInCacheScope(surface: string, { verb, fix }: IdentityReadWording, ctx?: WarmContext | null): void;
492
497
  /**
493
498
  * The capture refusal for a request-scoped read (cookies(), headers(), a
494
499
  * { cache: false } variable): the message after the surface, and the fix the
@@ -502,7 +507,7 @@ export declare function requestReadCaptureFix(what: string): Pick<IdentityReadWo
502
507
  * scope lookups. `requestCtx` is the ambient request context, whose capture
503
508
  * flag the guard reads.
504
509
  */
505
- export declare function assertNonCacheableReadAllowed(keyOrVar: string | ContextVar<unknown>, requestCtx?: unknown): void;
510
+ export declare function assertNonCacheableReadAllowed(keyOrVar: string | ContextVar<unknown>, requestCtx?: WarmContext | null): void;
506
511
  /** How the identity error words a read: a function call or a property read. */
507
512
  export type LoaderIdentityReadVerb = "called" | "read";
508
513
  /** A request-identity read a loader execution made (recordLoaderIdentityRead). */
@@ -598,7 +603,10 @@ export declare function latchCachedHeaderScope(kind: CachedHeaderScope["kind"],
598
603
  export declare function isInsideAnyLoaderScope(): boolean;
599
604
  /**
600
605
  * The one ppr opt-in predicate: a page route entry that DECLARED `ppr`
601
- * (`false` and undefined mean plain axis 1). Shared by the serve path
606
+ * (`false`, undefined, and on-demand routes mean plain axis 1). A writable
607
+ * prerender refresh cannot atomically replace the separate document shell, so
608
+ * combining those lanes would pair a fresh tail with a stale prelude. Shared by
609
+ * the serve path
602
610
  * (rsc/shell-serve.ts resolvePprConfig) and the header-write latch below so
603
611
  * the two layers can never drift on what counts as a ppr route.
604
612
  */
@@ -218,6 +218,8 @@ export interface HandleStore {
218
218
  * value are omitted; undefined when none has one.
219
219
  */
220
220
  getRecordOwners(segmentId: string): Record<string, (string | null)[]> | undefined;
221
+ /** The segment ids for which getRecordOwners returns an entry. */
222
+ getOwnedSegmentIds(): string[];
221
223
  /**
222
224
  * Replay cached handle data back into the store (for cache hits).
223
225
  * Used to restore handle data when serving cached segments.
@@ -19,7 +19,7 @@ import type { Handle } from "../handle.js";
19
19
  import { type ContextVar } from "../context-var.js";
20
20
  import { type HandleStore, type HandleData } from "./handle-store.js";
21
21
  import { type MetricsStore } from "./context.js";
22
- import type { SegmentCacheStore } from "../cache/types.js";
22
+ import type { SegmentCacheStore, ShellSnapshotHandlesValue } from "../cache/types.js";
23
23
  import type { RouterVersions } from "../router-versions.js";
24
24
  import type { Theme, ResolvedThemeConfig } from "../theme/types.js";
25
25
  import type { ExecutionContext, RequestScope } from "../types/request-scope.js";
@@ -204,6 +204,25 @@ export interface RequestContext<TEnv = DefaultEnv, TParams = Record<string, stri
204
204
  _pprReplayPostMatchReason?: "prerender-store" | "intercept";
205
205
  /** @internal Cache store for segment caching (optional, used by CacheScope) */
206
206
  _cacheStore?: SegmentCacheStore;
207
+ /**
208
+ * @internal Env-scoped writable prerender store (the durable overlay) plus the
209
+ * deployment identity for build-scoped keys, resolved per request from the
210
+ * `prerender` router option. Read by the serve-path overlay lookup before the
211
+ * bundled build manifest. Absent when no prerender store is configured.
212
+ */
213
+ _prerender?: import("../prerender/on-demand.js").ResolvedPrerender<any>;
214
+ /**
215
+ * @internal Set only on the requestless on-demand producer's context. When
216
+ * present, the standalone request-scoped READS — `cookies()`, `headers()`,
217
+ * `invalidateClientCache()`, `keepClientCache()` — throw a
218
+ * PrerenderPersonalizationError so a personalized render is skipped rather than
219
+ * baked into a shared payload. Response MUTATIONS on the producer context
220
+ * (`ctx.setStatus`/`header`/`setCookie`) stay silent no-op stubs, matching
221
+ * build-time prerender (the design's "throw or skip" contract permits skip):
222
+ * they write to a discarded synthetic response, so there is nothing to leak.
223
+ * Never set on the live request path.
224
+ */
225
+ _onDemandProducer?: true;
207
226
  /**
208
227
  * @internal Compiled `cache.searchParams` filter for default cache-key
209
228
  * generation (undefined = "all", the byte-stable unfiltered format). Set
@@ -272,6 +291,20 @@ export interface RequestContext<TEnv = DefaultEnv, TParams = Record<string, stri
272
291
  * cache miss.
273
292
  */
274
293
  _shellForcedMiss?: true;
294
+ /**
295
+ * @internal This request is a router.prerender() warm
296
+ * (prerender/warm-request.ts). In `replace` mode every runtime cache read
297
+ * misses (isWarmReplace); the layers report into the record. A capture's
298
+ * derived context inherits it through the prototype.
299
+ */
300
+ _prerenderWarm?: import("../prerender/warm-request.js").PrerenderWarmRecord;
301
+ /**
302
+ * @internal Runs a callback in the async context this request entered the
303
+ * handler with (captureRequestEntryContext). router.prerender() dispatches
304
+ * a warm through it when called from inside a request. Absent where the
305
+ * runtime has no AsyncLocalStorage.snapshot.
306
+ */
307
+ _runAtRequestEntry?: <T>(fn: () => T) => T;
275
308
  /**
276
309
  * @internal The freshness window of the route cache() records this
277
310
  * request read or wrote (cache-scope.ts noteRouteRecordWindow), as ms
@@ -361,6 +394,8 @@ export interface RequestContext<TEnv = DefaultEnv, TParams = Record<string, stri
361
394
  * host) and miss the record.
362
395
  */
363
396
  fixedDocKey?: string;
397
+ /** @internal The snapshot's `handles` record (ShellSnapshotHandlesValue). */
398
+ prerenderHandles?: ShellSnapshotHandlesValue;
364
399
  /**
365
400
  * @internal Set ONLY by matchPartialWithPprReplay on the navigation-replay
366
401
  * serve path. Its presence arms the explicit-scope composition in
@@ -672,6 +707,17 @@ export interface RequestContext<TEnv = DefaultEnv, TParams = Record<string, stri
672
707
  * deriveShellCaptureContext gives the capture its own.
673
708
  */
674
709
  _renderErrors?: unknown[];
710
+ /**
711
+ * @internal Rejections of streamed (loading()) handlers that were recovered
712
+ * into a declared errorBoundary()/notFoundBoundary() fallback, by segment id.
713
+ * The recovered promise resolves, so Flight reports nothing; cacheRoute
714
+ * (cache-scope.ts) refuses a write only when one of ITS segments is listed,
715
+ * the granularity a Flight error row has. The same errors are also pushed to
716
+ * `_renderErrors` for the document cache and PPR shell capture. A render
717
+ * under a derived context (shell capture, SWR re-render) owns its own map.
718
+ * Allocated on the first recovery.
719
+ */
720
+ _recoveredHandlerErrors?: Map<string, unknown>;
675
721
  /**
676
722
  * @internal Report a non-fatal background error through the router's
677
723
  * onError callback. Wired by the RSC handler / router during request
@@ -757,7 +803,7 @@ export interface RouteRecordWindow {
757
803
  * This is the type exported to library consumers. Internal code should
758
804
  * use the full RequestContext interface directly.
759
805
  */
760
- export type PublicRequestContext<TEnv = DefaultEnv, TParams = Record<string, string>> = Omit<RequestContext<TEnv, TParams>, "cookie" | "cookies" | "_readCookie" | "_readCookies" | "setCookie" | "deleteCookie" | "_handleStore" | "_transitionWhenRefs" | "_pprReplayPostMatchReason" | "_cacheStore" | "_searchParamsFilter" | "_shellCaptureRun" | "_shellImplicitCache" | "_shellLoaderSeed" | "_shellCaptureLoaderRecords" | "_shellCaptureUnrecordedLoaderPush" | "_shellCaptureLoaderLanes" | "_resolvedCacheKeys" | "_shellKey" | "_shellForcedMiss" | "_routeRecordWindow" | "_shellFragmentPayload" | "_shellCaptureGuardTripped" | "_shellCaptureGuardTrippedLoaderId" | "_explicitTaggedStores" | "_requestTags" | "_recordTagOwners" | "_recordLoaderTags" | "_cacheProfiles" | "_onResponseCallbacks" | "_pendingBackgroundTasks" | "_themeConfig" | "_readTheme" | "_documentCacheRender" | "_payloadVisitorTheme" | "_locationState" | "_routeName" | "_prevRouteKey" | "_inActionRevalidation" | "_reportedErrors" | "_renderErrors" | "_renderBarrier" | "_resolveRenderBarrier" | "_renderBarrierSegmentOrder" | "_treeHasStreaming" | "_renderBarrierWaiters" | "_handlerLoaderDeps" | "_awaitBeforeFlushLoaderIds" | "_renderBarrierHandleSnapshot" | "_renderBarrierGuardClosed" | "_reportBackgroundError" | "_debugPerformance" | "_metricsStore" | "_renderForeground" | "_activeRoutine" | "_renderDiagnosticsEnabled" | "_handlerStart" | "_tracing" | "_basename" | "_routerId" | "_versions" | "_setStatus" | "_rotateStateCookie" | "_setKeepCacheDirective" | "_freshReads" | "_setFreshReadsCookie" | "_requestRoot" | "_requestStart" | "_responseSent" | "_variables" | "_classifiedRoute" | "_requestMode" | "_cacheSignal" | "_dynamic" | "res">;
806
+ export type PublicRequestContext<TEnv = DefaultEnv, TParams = Record<string, string>> = Omit<RequestContext<TEnv, TParams>, "cookie" | "cookies" | "_readCookie" | "_readCookies" | "setCookie" | "deleteCookie" | "_handleStore" | "_transitionWhenRefs" | "_pprReplayPostMatchReason" | "_cacheStore" | "_prerender" | "_onDemandProducer" | "_searchParamsFilter" | "_shellCaptureRun" | "_shellImplicitCache" | "_shellLoaderSeed" | "_shellCaptureLoaderRecords" | "_shellCaptureUnrecordedLoaderPush" | "_shellCaptureLoaderLanes" | "_resolvedCacheKeys" | "_shellKey" | "_shellForcedMiss" | "_prerenderWarm" | "_runAtRequestEntry" | "_routeRecordWindow" | "_shellFragmentPayload" | "_shellCaptureGuardTripped" | "_shellCaptureGuardTrippedLoaderId" | "_explicitTaggedStores" | "_requestTags" | "_recordTagOwners" | "_recordLoaderTags" | "_cacheProfiles" | "_onResponseCallbacks" | "_pendingBackgroundTasks" | "_themeConfig" | "_readTheme" | "_documentCacheRender" | "_payloadVisitorTheme" | "_locationState" | "_routeName" | "_prevRouteKey" | "_inActionRevalidation" | "_reportedErrors" | "_renderErrors" | "_recoveredHandlerErrors" | "_renderBarrier" | "_resolveRenderBarrier" | "_renderBarrierSegmentOrder" | "_treeHasStreaming" | "_renderBarrierWaiters" | "_handlerLoaderDeps" | "_awaitBeforeFlushLoaderIds" | "_renderBarrierHandleSnapshot" | "_renderBarrierGuardClosed" | "_reportBackgroundError" | "_debugPerformance" | "_metricsStore" | "_renderForeground" | "_activeRoutine" | "_renderDiagnosticsEnabled" | "_handlerStart" | "_tracing" | "_basename" | "_routerId" | "_versions" | "_setStatus" | "_rotateStateCookie" | "_setKeepCacheDirective" | "_freshReads" | "_setFreshReadsCookie" | "_requestRoot" | "_requestStart" | "_responseSent" | "_variables" | "_classifiedRoute" | "_requestMode" | "_cacheSignal" | "_dynamic" | "res">;
761
807
  /**
762
808
  * Marker for a waitUntil-scheduled fn whose task promise must NOT enter
763
809
  * _pendingBackgroundTasks. Used by the PPR shell capture for its own task:
@@ -766,6 +812,15 @@ export type PublicRequestContext<TEnv = DefaultEnv, TParams = Record<string, str
766
812
  * @internal
767
813
  */
768
814
  export declare const UNTRACKED_BACKGROUND_TASK: unique symbol;
815
+ /**
816
+ * Capture the current async context (RequestContext._runAtRequestEntry), taken
817
+ * on handler entry before any router scope, so a nested dispatch (a
818
+ * router.prerender() warm called from a handler, loader or action) does not
819
+ * inherit the caller's route-definition, loader and cache-scope state. Per
820
+ * request: workerd refuses a snapshot outside the request that created it.
821
+ * See docs/design/prerender-every-route.md.
822
+ */
823
+ export declare function captureRequestEntryContext(): (<T>(fn: () => T) => T) | undefined;
769
824
  /**
770
825
  * Run a function within a request context
771
826
  * Used by the RSC handler to provide context to server actions
@@ -813,6 +868,12 @@ export interface CreateRequestContextOptions<TEnv> {
813
868
  initialResponse?: Response;
814
869
  /** Optional cache store for segment caching (used by CacheScope) */
815
870
  cacheStore?: SegmentCacheStore;
871
+ /**
872
+ * Optional env-scoped writable prerender store + deployment identity (the
873
+ * durable overlay), resolved per request by the RSC handler from the
874
+ * `prerender` router option.
875
+ */
876
+ prerender?: import("../prerender/on-demand.js").ResolvedPrerender<any>;
816
877
  /**
817
878
  * Compiled `cache.searchParams` filter from the resolved handler cache
818
879
  * config (handler.ts). Stored as _searchParamsFilter.
@@ -0,0 +1,26 @@
1
+ import { type ReactNode } from "react";
2
+ import { type BoundaryAudit } from "./suspense-audit.js";
3
+ /**
4
+ * One boundary: what each render hands it, what each commit carries, and its
5
+ * mount. Called above the Suspense, which commits while the content waits.
6
+ * `streams` and `nodes` are what else its content waits for (I3 only).
7
+ */
8
+ export declare function useBoundaryAudit(boundary: string, segmentId: string, value: unknown, streams?: Record<string, unknown>, nodes?: unknown): BoundaryAudit;
9
+ /** The boundary's content is on screen. */
10
+ export declare function useRevealAudit(rec: BoundaryAudit): void;
11
+ /**
12
+ * One `useLoader` read of a per-loader stream (I1, I2 at the read site). A
13
+ * read that suspends while mounting keeps no record: only a reader on screen
14
+ * is audited.
15
+ */
16
+ export declare function useReadAudit(loaderId: string, stream: unknown): void;
17
+ /** Wraps a boundary's fallback: mounted means the fallback is on screen. */
18
+ export declare function AuditedFallback({ audit, children, }: {
19
+ audit: BoundaryAudit;
20
+ children: ReactNode;
21
+ }): ReactNode;
22
+ /** Wraps a segment's outlet children: the segment's mount (I4). */
23
+ export declare function AuditedOutlet({ segmentId, children, }: {
24
+ segmentId: string;
25
+ children: ReactNode;
26
+ }): ReactNode;
@@ -0,0 +1,157 @@
1
+ export type SuspenseAuditKind = "swap" | "untracked" | "idle-fallback" | "resuspended" | "remount" | "drift" | "uncaused" | "mutated";
2
+ /** What may hand React a tree (I6). */
3
+ export type TreeUpdateCause = "navigation" | "popstate" | "stale-revalidation" | "action" | "error" | "hmr";
4
+ export interface SuspenseAuditEvent {
5
+ /** I1 swap, I2 untracked, I3 idle-fallback and resuspended, I4 remount, I5 drift, I6 uncaused, I7 mutated. */
6
+ kind: SuspenseAuditKind;
7
+ /** `content:<segment id>`, `loaders:<outlet key>`, `outlet:<segment id>`, `read:<loader id>`, `segment:<segment id>`. */
8
+ boundary: string;
9
+ detail: string;
10
+ url: string;
11
+ at: number;
12
+ }
13
+ export interface SuspenseAuditReport {
14
+ swaps: number;
15
+ untracked: number;
16
+ idleFallbacks: number;
17
+ resuspended: number;
18
+ remounts: number;
19
+ drifts: number;
20
+ uncaused: number;
21
+ mutations: number;
22
+ /**
23
+ * Not a violation: fallbacks the router cannot attribute. Nothing it handed
24
+ * the boundary was a promise React read, so the content's own suspension
25
+ * put the fallback up: an app's use(), a React.lazy, a client reference.
26
+ */
27
+ unattributedFallbacks: number;
28
+ /** Not a violation: tree updates React was handed, by cause ("none" without one). */
29
+ treeUpdates: Record<string, number>;
30
+ /**
31
+ * Not a violation: the distinct thenables each boundary or read was handed.
32
+ * One per value it waited for; two for one navigation is a gate replaced
33
+ * by the real promise.
34
+ */
35
+ readonly handed: Record<string, number>;
36
+ /**
37
+ * Not a violation: fallbacks shown over content on screen while what the
38
+ * boundary waits for was pending. An urgent commit does that by design.
39
+ */
40
+ shownWhilePending: number;
41
+ /** Mounts per boundary since the document loaded. */
42
+ readonly mounts: Record<string, number>;
43
+ /** The last events, oldest first. */
44
+ events: SuspenseAuditEvent[];
45
+ reset(): void;
46
+ }
47
+ /** One boundary, or one `useLoader` read. Lives as long as its fiber. */
48
+ export interface BoundaryAudit {
49
+ boundary: string;
50
+ /** Segment id, for the place in the tree. */
51
+ segmentId: string;
52
+ /** The React key the segment was mounted under (I4 identity). */
53
+ key: string;
54
+ /** 0 until the boundary is on screen. */
55
+ mountId: number;
56
+ /** Last value a render ran with, and the URL it ran under. */
57
+ seen: unknown;
58
+ hasSeen: boolean;
59
+ seenUrl: string;
60
+ /** Last value a commit carried. */
61
+ committed: unknown;
62
+ hasCommitted: boolean;
63
+ /** What else the boundary's content waits for: per-loader streams, nodes. */
64
+ streams: Record<string, unknown> | undefined;
65
+ nodes: unknown;
66
+ /** The content has been on screen. */
67
+ revealed: boolean;
68
+ /** The fallback's effect was cleaned up in this task (auditFallback). */
69
+ fallbackGone: boolean;
70
+ }
71
+ export declare function createBoundaryAudit(boundary: string, segmentId: string): BoundaryAudit;
72
+ /**
73
+ * Render phase: the value this render hands the boundary.
74
+ *
75
+ * I1: a boundary on screen was handed a thenable that is still pending, and
76
+ * is handed another one that is not already settled, for the same URL. React
77
+ * starts waiting again. A settled replacement is read at once, and another
78
+ * URL is another navigation: both are exempt.
79
+ * I2: a boundary whose content has been on screen is handed a native promise
80
+ * React has not read, and React finds it settled when it reads it. A render
81
+ * that cannot wait suspends on it and commits the fallback over the content,
82
+ * which React keeps up for 300 ms. Judged from the status React leaves on
83
+ * the promise when it reads it in the same task.
84
+ */
85
+ export declare function auditHandover(rec: BoundaryAudit, value: unknown): void;
86
+ /** Commit phase: what the boundary on screen now reads. */
87
+ export declare function auditCommit(rec: BoundaryAudit, value: unknown, streams?: Record<string, unknown>, nodes?: unknown): void;
88
+ export declare function auditReveal(rec: BoundaryAudit): void;
89
+ /** One `useLoader` read on screen: it has a mount and reveals with it. */
90
+ export declare function auditRead(rec: BoundaryAudit, stream: unknown): void;
91
+ /**
92
+ * How the audit learns that a payload is still streaming: the event
93
+ * controller, set at boot (browser/rsc-router.tsx). Not read from
94
+ * NavigationStoreContext in AuditedFallback: see the header.
95
+ */
96
+ export declare function setSuspenseAuditStreamProbe(probe: () => boolean): void;
97
+ /**
98
+ * I3: the boundary's fallback is on screen with nothing to wait for: nothing
99
+ * the router handed the boundary is pending, no payload is streaming and no
100
+ * client reference is loading. `idle-fallback` on a boundary new to the page,
101
+ * `resuspended` when the fallback replaced content that had been on screen.
102
+ *
103
+ * Reported only when the router owns the fallback: React suspended on a
104
+ * promise the router handed unread that was already settled (`needless`).
105
+ * Any other fallback is the content's own (an app's use(), a React.lazy, a
106
+ * client reference) and is counted as `unattributedFallbacks`, not reported,
107
+ * also when the boundary holds a promise React has read.
108
+ *
109
+ * A fallback over content on screen while something is pending is counted
110
+ * (`shownWhilePending`) and not reported: an urgent commit does that by
111
+ * design (transition({ when }) gated off, #995), and so does dev while a
112
+ * client reference loads.
113
+ */
114
+ export declare function auditFallback(rec: BoundaryAudit): () => void;
115
+ /** One renderSegments call, collected while it builds. */
116
+ export interface TreeAudit {
117
+ /** Segment ids, leaf first while building, root first once finished. */
118
+ order: string[];
119
+ keys: Map<string, string>;
120
+ types: Map<string, string>;
121
+ chains: Map<string, string[]>;
122
+ /** Segments this tree remounts by the documented rules. */
123
+ replaced: Set<string>;
124
+ /** The segment objects the tree is built from (I7), until it is held. */
125
+ segments: object[];
126
+ }
127
+ export declare function startTreeAudit(): TreeAudit | undefined;
128
+ /** The element renderSegments built for one segment, leaf first. */
129
+ export declare function auditSegmentElement(tree: TreeAudit | undefined, id: string, type: string, key: string, element: unknown, outletTransition: boolean, slots: readonly string[], segments: readonly object[]): void;
130
+ /**
131
+ * A finished tree. The first is the document's, which React holds from
132
+ * hydration; any later one is held when an emit hands its root to React
133
+ * (auditTreeUpdate).
134
+ */
135
+ export declare function finishTreeAudit(tree: TreeAudit | undefined, root: unknown): void;
136
+ /**
137
+ * I4: a boundary that stays on the page under the same React key is
138
+ * unmounted and mounted again. A different key is the documented remount.
139
+ */
140
+ export declare function auditMount(rec: BoundaryAudit): void;
141
+ export declare function auditUnmount(rec: BoundaryAudit): void;
142
+ /**
143
+ * I6: the cause of the tree update the caller emits next, in the same task.
144
+ * It does not outlive the task: an emit after an await names its cause again,
145
+ * or a cause left behind would cover an emitter that names none.
146
+ */
147
+ export declare function auditTreeCause(cause: TreeUpdateCause): void;
148
+ /**
149
+ * I6: a tree update reached React (the store.onUpdate subscriber in
150
+ * browser/react/NavigationProvider.tsx), with the root it hands React. Only a
151
+ * navigation, a back/forward, its stale revalidation, an action, an error and
152
+ * HMR hand React a tree; anything else updates in place through a store its
153
+ * readers subscribe to, or through a pending promise read with use().
154
+ */
155
+ export declare function auditTreeUpdate(root?: unknown): void;
156
+ /** Forget every tree and mount seen so far. For tests of the audit itself. */
157
+ export declare function forgetSuspenseAudit(): void;
@@ -0,0 +1,34 @@
1
+ /**
2
+ * createActionForm: the FormData a browser posts when a form is submitted
3
+ * before hydration (no JavaScript), for a server action.
4
+ *
5
+ * React's server render writes hidden fields on the form so the POST names its
6
+ * action and carries the bound state; decodeAction / decodeFormState read them
7
+ * back. A test cannot render those fields (react-dom/server does not load under
8
+ * the react-server condition), so this builds the same wire format and
9
+ * registers the action where the plugin-rsc stub's decodeAction resolves it,
10
+ * tagged as a server reference like a built "use server" export so the bound
11
+ * action keeps its `$$id` (onError's actionId, the action phase label).
12
+ * Post the result with `serveShellRequest(router, url, { form })`.
13
+ */
14
+ /** Options for {@link createActionForm}. */
15
+ export interface CreateActionFormOptions {
16
+ /**
17
+ * The action's reference id, `<module id>#<export name>`, as the build gives
18
+ * a "use server" export. Any unique string of that shape in a test.
19
+ */
20
+ id: string;
21
+ /**
22
+ * A `useActionState` form (default): the action runs as `(prevState, formData)`
23
+ * and the POST carries the state key. false: a plain `<form action={fn}>`
24
+ * ($ACTION_ID_), the action runs as `(formData)`.
25
+ */
26
+ useActionState?: boolean;
27
+ /** The form's own fields, as the person filled them. */
28
+ fields?: Record<string, string>;
29
+ }
30
+ /**
31
+ * Build a no-JS form POST body for `action` and register it so a handler
32
+ * decodes it back to `action`.
33
+ */
34
+ export declare function createActionForm(action: (...args: any[]) => unknown, options: CreateActionFormOptions): FormData;
@@ -30,5 +30,7 @@ export { renderServerTree, findClientBoundaries, findElements, textContent, asse
30
30
  export type { RenderServerTreeOptions, RenderServerTreeResult, ClientBoundary, BoundarySelector, FoundElement, ElementSelector, } from "./flight-tree.js";
31
31
  export { renderHandler } from "./render-handler.js";
32
32
  export type { TestableHandler, RenderHandlerOptions, RenderHandlerResult, StateCookieSeed, } from "./render-handler.js";
33
+ export { createActionForm } from "./action-form.js";
34
+ export type { CreateActionFormOptions } from "./action-form.js";
33
35
  export { serveShellRequest, resetShellTestState, } from "./serve-shell-request.js";
34
36
  export type { ServeShellRequestOptions, ServeShellRequestResult, ShellRequestHandles, } from "./serve-shell-request.js";
@@ -43,6 +43,10 @@ export { runClientRevalidate } from "./run-client-revalidate.js";
43
43
  export type { RunClientRevalidateOptions } from "./run-client-revalidate.js";
44
44
  export { dispatch } from "./dispatch.js";
45
45
  export type { DispatchOptions } from "./dispatch.js";
46
+ export { createMemoryPrerenderStore } from "../prerender/memory-prerender-store.js";
47
+ export type { MemoryPrerenderStore, MemoryPrerenderStoreOptions, } from "../prerender/memory-prerender-store.js";
48
+ export type { WritablePrerenderStore, PrerenderStoredEntry, PrerenderKey, } from "../prerender/writable-store.js";
49
+ export type { PrerenderConfig, PrerenderResult, PrerenderWarmCaches, } from "../prerender/on-demand.js";
46
50
  export { assertCacheStatus, assertCacheDecision, parseCacheHeader, createCacheSink, filterCacheDecisions, } from "./cache-status.js";
47
51
  export type { ExpectedCacheStatus, CacheStatusTarget, CacheSink, } from "./cache-status.js";
48
52
  export type { TelemetryEvent, TelemetrySink, CacheDecisionEvent, CacheSegmentSignal, CacheSegmentStatus, } from "../router/telemetry.js";
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Register `exports` as the server module `id`, merging into an earlier call.
3
+ * The module object is kept and mutated: the stub's require is memoized per
4
+ * id, so a later export must appear on the object it first returned.
5
+ */
6
+ export declare function registerServerModule(id: string, exports: Record<string, unknown>): void;
7
+ /** The module a decoded server reference resolves to. */
8
+ export declare function loadServerModule(id: string): Record<string, unknown>;
@@ -27,6 +27,14 @@
27
27
  * capture refuses for a root postpone (a live loader read with no boundary
28
28
  * above it) or an SSR render error is captured here.
29
29
  *
30
+ * A `Prerender` route serves its handler layer from the artifact
31
+ * `router.matchForPrerender` bakes for the URL (on its first request, kept
32
+ * until resetShellTestState), through the production prerender store. Its
33
+ * first `ppr` request is a MISS with a runtime capture, as a URL without a
34
+ * build-time shell is in production; build-time shells are e2e-only. An
35
+ * on-demand route (`Prerender(..., { onDemand })`) bakes nothing here: it
36
+ * serves from its prerender store, as a refresh-only page does in production.
37
+ *
30
38
  * Must run under the `react-server` condition (the rsc Vitest project), with
31
39
  * `rangoTestAliases()` resolving `@vitejs/plugin-rsc/rsc/server` to the stub
32
40
  * that carries the Flight runtime.
@@ -60,6 +68,14 @@ export interface ServeShellRequestOptions<TEnv = any> {
60
68
  from?: string;
61
69
  segments?: readonly string[];
62
70
  };
71
+ /**
72
+ * POST this form as a submit before hydration: the no-JS request the
73
+ * progressive-enhancement path runs the action for and re-renders the page
74
+ * after. Build it with `createActionForm`. The response is the document
75
+ * (`flight` is its payload, `formState` what the SSR render received).
76
+ * Not with `partial`.
77
+ */
78
+ form?: FormData;
63
79
  }
64
80
  /** A response's handle data, as the browser consumes it. */
65
81
  export interface ShellRequestHandles {
@@ -117,17 +133,31 @@ export interface ServeShellRequestResult {
117
133
  * values resolved. Undefined when no Flight was rendered.
118
134
  */
119
135
  readHandles(): Promise<ShellRequestHandles | undefined>;
136
+ /**
137
+ * The form state the SSR render received (the `formState` render option),
138
+ * or undefined when it received none.
139
+ */
140
+ formState: unknown;
141
+ /**
142
+ * The payload's `formState` as the browser reads it, the value the entry
143
+ * hands `hydrateRoot`. Undefined when the payload carries none (or no
144
+ * Flight was rendered). Rejects when the action's state is not
145
+ * Flight-serializable: the document still renders, and the entry
146
+ * hydrates without it.
147
+ */
148
+ readPayloadFormState(): Promise<unknown>;
120
149
  }
121
150
  /**
122
151
  * Reset the per-isolate PPR state requests leave behind, so the next test
123
152
  * starts as a fresh worker would: the capture's stampede guard, its backoff
124
153
  * (a refused capture backs its URL off for later tests too), the shell path's
125
154
  * once-per-key warnings, the build-shell manifest memo, the "use cache"
126
- * in-flight leaders and warnings, and CFCacheStore's isolate memos (shells,
155
+ * in-flight leaders and warnings, CFCacheStore's isolate memos (shells,
127
156
  * tag markers, tag hints) and warnings, which every CFCacheStore in the
128
- * process shares by namespace and URL. Call it in `beforeEach`, never while
129
- * a request is in flight. VercelCacheStore's memos live on the `cache`
130
- * handle it is given: a new handle starts empty.
157
+ * process shares by namespace and URL, and the Prerender artifacts baked so
158
+ * far. Call it in `beforeEach`, never while a request is in flight.
159
+ * VercelCacheStore's memos live on the `cache` handle it is given: a new
160
+ * handle starts empty.
131
161
  */
132
162
  export declare function resetShellTestState(): Promise<void>;
133
163
  /**
@@ -15,7 +15,7 @@
15
15
  * 2. **Smallest status signals:** `x-rango-shell` (`HIT` | `MISS`) on document
16
16
  * GETs, and `x-rango-ppr-replay` on partial requests to ppr routes. A replay
17
17
  * HIT is reported only when matching consumes the captured segment record.
18
- * Secondary unit signal: `store.getShell(shellCacheKey(url))` after a real
18
+ * Secondary unit signal: `store.getShell(shellCacheKey(router, url))` after a real
19
19
  * capture flush (background `putShell`). There is no Flight flag for shell HIT.
20
20
  *
21
21
  * 3. **A real MISS → capture → HIT in unit tests:** `serveShellRequest`
@@ -68,10 +68,16 @@ export interface ShellCachePartition {
68
68
  generated?: readonly string[];
69
69
  }
70
70
  /**
71
- * Shell store key for a document URL: the production `buildShellKey` (host +
72
- * pathname + sorted search + `:shell`) and `partitionShellKey`, both from the
73
- * React-free leaf `rsc/shell-capture-constants.ts`, so the testing barrel never
74
- * imports the shell-serve module (it pulls React).
71
+ * Shell store key of `router` for a document URL: the production
72
+ * `buildShellKey` (router id + host + pathname + sorted search + `:shell`)
73
+ * and `partitionShellKey`, both from the React-free leaf
74
+ * `rsc/shell-capture-constants.ts`, so the testing barrel never imports the
75
+ * shell-serve module (it pulls React).
76
+ *
77
+ * `router` is the router that serves the URL, or anything carrying its `id`
78
+ * (`{ id }` where the router cannot be imported, as in a Playwright test). A
79
+ * shell belongs to one router: two routers serving one host and path keep
80
+ * two entries.
75
81
  *
76
82
  * Accepts a `URL` or an absolute/relative request URL string (relative strings
77
83
  * resolve against `http://localhost`). Pass the router's `cache.searchParams`
@@ -86,7 +92,9 @@ export interface ShellCachePartition {
86
92
  * - store `keyGenerator` results, with or without `key()` results, as
87
93
  * `{ keys, generated }` (see {@link ShellCachePartition}).
88
94
  */
89
- export declare function shellCacheKey(url: URL | string, searchParams?: CacheSearchParams, partition?: string | readonly string[] | ShellCachePartition): string;
95
+ export declare function shellCacheKey(router: {
96
+ readonly id: string;
97
+ }, url: URL | string, searchParams?: CacheSearchParams, partition?: string | readonly string[] | ShellCachePartition): string;
90
98
  /**
91
99
  * Read `x-rango-shell` from a response. Returns `null` when the header is
92
100
  * absent (axis-1 / non-ppr / ineligible request).
@@ -16,8 +16,11 @@
16
16
  *
17
17
  * Without the condition React's server build refuses to load, so the codec
18
18
  * functions throw and a "use cache" call runs uncached. Any other load failure
19
- * (a moved vendored path, a missing peer) throws. The action entries stay
20
- * inert either way: no primitive decodes an action request.
19
+ * (a moved vendored path, a missing peer) throws. decodeAction and
20
+ * decodeFormState decode a no-JS form POST under the condition, resolving its
21
+ * action from the modules createActionForm() registered, as do
22
+ * `loadServerAction` and `decodeReply` for a plain form action. Without the
23
+ * condition they stay inert.
21
24
  */
22
25
  export declare const renderToReadableStream: (value: unknown, options?: object) => ReadableStream<Uint8Array>;
23
26
  export declare const createTemporaryReferenceSet: () => unknown;
@@ -26,7 +29,7 @@ export declare const createFromReadableStream: (stream: ReadableStream<Uint8Arra
26
29
  }) => Promise<unknown>;
27
30
  export declare const encodeReply: (value: unknown, options?: object) => Promise<string | FormData>;
28
31
  export declare const createClientTemporaryReferenceSet: () => unknown;
29
- export declare const loadServerAction: () => undefined;
30
- export declare const decodeReply: () => undefined;
31
- export declare const decodeAction: () => undefined;
32
- export declare const decodeFormState: () => undefined;
32
+ export declare const loadServerAction: (id: string) => Promise<unknown> | undefined;
33
+ export declare const decodeReply: (body: string | FormData, options?: object) => Promise<unknown> | undefined;
34
+ export declare const decodeAction: (body: FormData) => Promise<unknown> | null | undefined;
35
+ export declare const decodeFormState: (actionResult: unknown, body: FormData) => Promise<unknown> | undefined;
@@ -7,3 +7,9 @@
7
7
  * isLoading registration (loader-store.ts), which must agree on "settled".
8
8
  */
9
9
  export declare function unwrapsSynchronously(stream: Promise<unknown>): boolean;
10
+ /**
11
+ * A resolved thenable for a use() whose result is ignored: React reads the
12
+ * "fulfilled" stamp and returns at once. `_debugInfo` keeps React DevTools from
13
+ * listing a 0 ms promise on every reader that calls it.
14
+ */
15
+ export declare const SETTLED_THENABLE: Promise<undefined>;
@@ -329,6 +329,13 @@ export type InternalHandlerContext<TParams = {}, TEnv = DefaultEnv, TSearch exte
329
329
  _responseType?: string;
330
330
  /** Route name for cache key scoping (prevents cross-route collisions). */
331
331
  _routeName?: string;
332
+ /**
333
+ * @internal The on-demand overlay holds a "removed" marker for this request
334
+ * (tryPrerenderLookup, cache-lookup.ts); gateOnDemandProducer reads it. On
335
+ * this context, not the request's: withCacheLookup documents that the
336
+ * request ALS can be lost mid-pipeline on workerd.
337
+ */
338
+ _prerenderRemoved?: true;
332
339
  /**
333
340
  * @internal Loader-cache override table: loaderId -> memoized data promise.
334
341
  * A single stable ctx.use interceptor consults this instead of chaining one
@@ -72,6 +72,11 @@ export interface RouteEntry<TEnv = any> {
72
72
  * Used by the non-trie match path to set the `pt` flag.
73
73
  */
74
74
  passthroughRouteKeys?: Set<string>;
75
+ /**
76
+ * Route keys in this entry that opted into on-demand prerender.
77
+ * Used by the non-trie match path to set the `od` flag.
78
+ */
79
+ onDemandRouteKeys?: Set<string>;
75
80
  /**
76
81
  * Whether this entry is lazily evaluated.
77
82
  * When true, routes are populated on first matching request.