@rangojs/router 0.26.6 → 0.28.0

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 (234) hide show
  1. package/dist/bin/rango.js +1 -1
  2. package/dist/types/__internal.d.ts +7 -1
  3. package/dist/types/browser/action-coordinator.d.ts +0 -4
  4. package/dist/types/browser/action-fence.d.ts +11 -3
  5. package/dist/types/browser/event-controller.d.ts +20 -1
  6. package/dist/types/browser/history-state.d.ts +7 -9
  7. package/dist/types/browser/intercept-utils.d.ts +23 -0
  8. package/dist/types/browser/navigation-store.d.ts +8 -6
  9. package/dist/types/browser/react/filter-segment-order.d.ts +2 -2
  10. package/dist/types/browser/rsc-router.d.ts +1 -1
  11. package/dist/types/browser/segment-reconciler.d.ts +6 -4
  12. package/dist/types/browser/types.d.ts +2 -1
  13. package/dist/types/browser/wire-protocol.d.ts +25 -0
  14. package/dist/types/cache/cache-key-utils.d.ts +11 -5
  15. package/dist/types/cache/cache-scope.d.ts +34 -22
  16. package/dist/types/cache/cf/cf-cache-constants.d.ts +2 -2
  17. package/dist/types/cache/cf/cf-cache-store.d.ts +47 -65
  18. package/dist/types/cache/cf/cf-kv-utils.d.ts +1 -1
  19. package/dist/types/cache/handle-snapshot.d.ts +9 -4
  20. package/dist/types/cache/segment-codec.d.ts +14 -0
  21. package/dist/types/cache/shell-snapshot.d.ts +1 -1
  22. package/dist/types/cache/tag-invalidation.d.ts +10 -0
  23. package/dist/types/cache/taint.d.ts +5 -3
  24. package/dist/types/cache/types.d.ts +7 -3
  25. package/dist/types/cache/vercel/vercel-cache-store.d.ts +8 -6
  26. package/dist/types/prefetch-markers.d.ts +4 -6
  27. package/dist/types/prerender/store.d.ts +31 -0
  28. package/dist/types/prerender/warm-request.d.ts +3 -3
  29. package/dist/types/prerender.d.ts +2 -2
  30. package/dist/types/route-name.d.ts +5 -0
  31. package/dist/types/router/error-handling.d.ts +4 -4
  32. package/dist/types/router/instrument.d.ts +4 -3
  33. package/dist/types/router/intercept-resolution.d.ts +25 -9
  34. package/dist/types/router/match-context.d.ts +12 -11
  35. package/dist/types/router/match-handlers.d.ts +2 -6
  36. package/dist/types/router/match-middleware/background-revalidation.d.ts +10 -20
  37. package/dist/types/router/match-middleware/cache-lookup.d.ts +35 -87
  38. package/dist/types/router/match-middleware/cache-store.d.ts +2 -2
  39. package/dist/types/router/match-middleware/segment-resolution.d.ts +8 -6
  40. package/dist/types/router/prerender-match.d.ts +21 -6
  41. package/dist/types/router/preview-match.d.ts +2 -7
  42. package/dist/types/router/request-classification.d.ts +5 -0
  43. package/dist/types/router/revalidation.d.ts +6 -1
  44. package/dist/types/router/router-context.d.ts +9 -63
  45. package/dist/types/router/router-interfaces.d.ts +8 -14
  46. package/dist/types/router/segment-resolution/loader-cache.d.ts +3 -3
  47. package/dist/types/router/segment-resolution/loader-mask.d.ts +4 -4
  48. package/dist/types/router/segment-resolution/prefetch-deferral.d.ts +20 -18
  49. package/dist/types/router/segment-resolution/resolution-pass.d.ts +93 -0
  50. package/dist/types/router/segment-resolution/revalidation.d.ts +82 -31
  51. package/dist/types/router/segment-resolution.d.ts +1 -2
  52. package/dist/types/router/segment-wrappers.d.ts +11 -48
  53. package/dist/types/router/telemetry.d.ts +2 -0
  54. package/dist/types/router/types.d.ts +10 -0
  55. package/dist/types/rsc/full-payload.d.ts +2 -2
  56. package/dist/types/rsc/handler-context.d.ts +6 -6
  57. package/dist/types/rsc/json-route-result.d.ts +5 -9
  58. package/dist/types/rsc/request-stages.d.ts +203 -0
  59. package/dist/types/rsc/response-cache-serve.d.ts +7 -12
  60. package/dist/types/rsc/response-route-handler.d.ts +2 -2
  61. package/dist/types/rsc/routine-plan.d.ts +1 -1
  62. package/dist/types/rsc/server-action.d.ts +2 -2
  63. package/dist/types/rsc/shell-capture-constants.d.ts +2 -2
  64. package/dist/types/rsc/shell-capture.d.ts +132 -15
  65. package/dist/types/rsc/shell-serve.d.ts +37 -12
  66. package/dist/types/server/context.d.ts +9 -8
  67. package/dist/types/server/handle-store.d.ts +16 -0
  68. package/dist/types/server/render-pass.d.ts +72 -0
  69. package/dist/types/server/request-context.d.ts +24 -104
  70. package/dist/types/server/rewrite-trail.d.ts +2 -2
  71. package/dist/types/testing/dispatch.d.ts +35 -18
  72. package/dist/types/testing/serve-shell-request.d.ts +7 -0
  73. package/dist/types/types/cache-types.d.ts +6 -3
  74. package/dist/types/types/error-types.d.ts +9 -1
  75. package/dist/types/types/handler-context.d.ts +18 -7
  76. package/dist/types/types/segments.d.ts +1 -1
  77. package/dist/types/vite/discovery/build-discovery-phase.d.ts +40 -0
  78. package/dist/types/vite/discovery/build-env.d.ts +22 -0
  79. package/dist/types/vite/discovery/bundle-scan-phase.d.ts +12 -0
  80. package/dist/types/vite/discovery/dev-prerender-cache.d.ts +3 -2
  81. package/dist/types/vite/discovery/dev-server-phase.d.ts +26 -0
  82. package/dist/types/vite/discovery/discovery-errors.d.ts +1 -1
  83. package/dist/types/vite/discovery/state.d.ts +12 -0
  84. package/dist/types/vite/plugins/version-plugin.d.ts +1 -1
  85. package/dist/types/vite/plugins/virtual-entries.d.ts +1 -1
  86. package/dist/types/vite/router-discovery.d.ts +8 -47
  87. package/dist/types/vite/temp-rsc-server.d.ts +39 -0
  88. package/dist/types/vite/utils/prerender-utils.d.ts +9 -0
  89. package/dist/types/wire-params.d.ts +34 -0
  90. package/dist/vite/index.js +3809 -3710
  91. package/package.json +1 -1
  92. package/skills/cache-guide/SKILL.md +8 -5
  93. package/skills/caching/SKILL.md +37 -18
  94. package/skills/client-urls/SKILL.md +2 -1
  95. package/skills/document-cache/SKILL.md +18 -1
  96. package/skills/handler-use/SKILL.md +1 -1
  97. package/skills/intercept/SKILL.md +53 -13
  98. package/skills/layout/SKILL.md +11 -7
  99. package/skills/loader/SKILL.md +30 -13
  100. package/skills/middleware/SKILL.md +19 -1
  101. package/skills/migrate-nextjs/SKILL.md +5 -4
  102. package/skills/parallel/SKILL.md +5 -4
  103. package/skills/ppr/SKILL.md +19 -3
  104. package/skills/prerender/SKILL.md +39 -15
  105. package/skills/rango/SKILL.md +23 -11
  106. package/skills/server-actions/SKILL.md +51 -6
  107. package/skills/testing/predicates.md +1 -1
  108. package/skills/testing/response-routes.md +6 -4
  109. package/skills/testing/serve-shell-request.md +7 -7
  110. package/skills/use-cache/SKILL.md +46 -8
  111. package/skills/ux-architecture/SKILL.md +3 -2
  112. package/src/__internal.ts +7 -1
  113. package/src/browser/action-coordinator.ts +2 -13
  114. package/src/browser/action-fence.ts +11 -3
  115. package/src/browser/connection-warmup.ts +3 -1
  116. package/src/browser/event-controller.ts +42 -3
  117. package/src/browser/history-state.ts +15 -13
  118. package/src/browser/intercept-utils.ts +51 -0
  119. package/src/browser/navigation-bridge.ts +34 -30
  120. package/src/browser/navigation-client.ts +21 -33
  121. package/src/browser/navigation-store.ts +25 -27
  122. package/src/browser/navigation-transaction.ts +7 -11
  123. package/src/browser/partial-update.ts +8 -0
  124. package/src/browser/prefetch/cache.ts +2 -1
  125. package/src/browser/prefetch/fetch.ts +8 -16
  126. package/src/browser/prefetch/queue.ts +4 -4
  127. package/src/browser/react/NavigationProvider.tsx +2 -1
  128. package/src/browser/react/filter-segment-order.ts +2 -2
  129. package/src/browser/rsc-router.tsx +17 -2
  130. package/src/browser/segment-reconciler.ts +66 -53
  131. package/src/browser/server-action-bridge.ts +265 -127
  132. package/src/browser/types.ts +2 -1
  133. package/src/browser/wire-protocol.ts +41 -0
  134. package/src/cache/cache-key-utils.ts +11 -5
  135. package/src/cache/cache-runtime.ts +110 -50
  136. package/src/cache/cache-scope.ts +110 -64
  137. package/src/cache/cf/cf-cache-constants.ts +2 -2
  138. package/src/cache/cf/cf-cache-store.ts +486 -874
  139. package/src/cache/cf/cf-kv-utils.ts +1 -1
  140. package/src/cache/document-cache.ts +42 -15
  141. package/src/cache/handle-snapshot.ts +19 -7
  142. package/src/cache/segment-codec.ts +55 -0
  143. package/src/cache/shell-snapshot.ts +2 -2
  144. package/src/cache/tag-invalidation.ts +21 -2
  145. package/src/cache/taint.ts +5 -3
  146. package/src/cache/types.ts +7 -3
  147. package/src/cache/vercel/vercel-cache-store.ts +243 -310
  148. package/src/handles/pushed-by.ts +1 -1
  149. package/src/prefetch-markers.ts +4 -7
  150. package/src/prerender/store.ts +76 -0
  151. package/src/prerender/warm-request.ts +8 -4
  152. package/src/prerender.ts +2 -2
  153. package/src/route-name.ts +8 -0
  154. package/src/router/error-handling.ts +4 -4
  155. package/src/router/instrument.ts +4 -3
  156. package/src/router/intercept-resolution.ts +77 -75
  157. package/src/router/loader-resolution.ts +5 -4
  158. package/src/router/match-api.ts +4 -5
  159. package/src/router/match-context.ts +33 -12
  160. package/src/router/match-handlers.ts +8 -20
  161. package/src/router/match-middleware/background-revalidation.ts +41 -53
  162. package/src/router/match-middleware/cache-lookup.ts +680 -720
  163. package/src/router/match-middleware/cache-store.ts +14 -12
  164. package/src/router/match-middleware/intercept-resolution.ts +3 -23
  165. package/src/router/match-middleware/segment-resolution.ts +34 -61
  166. package/src/router/match-result.ts +1 -20
  167. package/src/router/middleware.ts +69 -11
  168. package/src/router/navigation-snapshot.ts +4 -3
  169. package/src/router/prerender-match.ts +100 -44
  170. package/src/router/preview-match.ts +6 -41
  171. package/src/router/request-classification.ts +13 -7
  172. package/src/router/revalidation.ts +33 -7
  173. package/src/router/route-snapshot.ts +2 -5
  174. package/src/router/router-context.ts +14 -117
  175. package/src/router/router-interfaces.ts +8 -14
  176. package/src/router/segment-resolution/loader-cache.ts +14 -9
  177. package/src/router/segment-resolution/loader-mask.ts +5 -5
  178. package/src/router/segment-resolution/prefetch-deferral.ts +30 -27
  179. package/src/router/segment-resolution/resolution-pass.ts +137 -0
  180. package/src/router/segment-resolution/revalidation.ts +897 -776
  181. package/src/router/segment-resolution.ts +4 -13
  182. package/src/router/segment-wrappers.ts +51 -269
  183. package/src/router/telemetry.ts +5 -0
  184. package/src/router/types.ts +10 -0
  185. package/src/router.ts +12 -13
  186. package/src/rsc/full-payload.ts +6 -5
  187. package/src/rsc/handler-context.ts +8 -14
  188. package/src/rsc/handler.ts +126 -507
  189. package/src/rsc/json-route-result.ts +5 -9
  190. package/src/rsc/loader-fetch.ts +6 -5
  191. package/src/rsc/progressive-enhancement.ts +5 -0
  192. package/src/rsc/request-stages.ts +757 -0
  193. package/src/rsc/response-cache-serve.ts +10 -14
  194. package/src/rsc/response-route-handler.ts +13 -5
  195. package/src/rsc/rewrite-passes.ts +2 -1
  196. package/src/rsc/routine-plan.ts +1 -1
  197. package/src/rsc/rsc-rendering.ts +172 -349
  198. package/src/rsc/server-action.ts +87 -113
  199. package/src/rsc/shell-capture-constants.ts +3 -1
  200. package/src/rsc/shell-capture.ts +212 -169
  201. package/src/rsc/shell-serve.ts +121 -18
  202. package/src/rsc/ssr-setup.ts +5 -4
  203. package/src/segment-system.tsx +3 -3
  204. package/src/server/context.ts +22 -25
  205. package/src/server/handle-store.ts +55 -0
  206. package/src/server/render-pass.ts +291 -0
  207. package/src/server/request-context.ts +26 -115
  208. package/src/server/rewrite-trail.ts +6 -4
  209. package/src/ssr/index.tsx +1 -1
  210. package/src/ssr/ssr-root.tsx +1 -0
  211. package/src/testing/dispatch.ts +223 -710
  212. package/src/testing/e2e/page-helpers.ts +2 -2
  213. package/src/testing/serve-shell-request.ts +37 -32
  214. package/src/types/cache-types.ts +6 -3
  215. package/src/types/error-types.ts +9 -1
  216. package/src/types/handler-context.ts +21 -7
  217. package/src/types/segments.ts +1 -1
  218. package/src/vite/discovery/build-discovery-phase.ts +204 -0
  219. package/src/vite/discovery/build-env.ts +148 -0
  220. package/src/vite/discovery/bundle-scan-phase.ts +128 -0
  221. package/src/vite/discovery/dev-prerender-cache.ts +8 -3
  222. package/src/vite/discovery/dev-server-phase.ts +1776 -0
  223. package/src/vite/discovery/discover-routers.ts +1 -1
  224. package/src/vite/discovery/discovery-errors.ts +1 -1
  225. package/src/vite/discovery/prerender-collection.ts +24 -39
  226. package/src/vite/discovery/state.ts +15 -0
  227. package/src/vite/plugins/version-plugin.ts +1 -1
  228. package/src/vite/plugins/virtual-entries.ts +1 -1
  229. package/src/vite/router-discovery.ts +29 -2428
  230. package/src/vite/temp-rsc-server.ts +223 -0
  231. package/src/vite/utils/prerender-utils.ts +47 -0
  232. package/src/wire-params.ts +35 -0
  233. package/dist/types/router/segment-resolution/fresh.d.ts +0 -72
  234. package/src/router/segment-resolution/fresh.ts +0 -945
@@ -5,23 +5,18 @@
5
5
  * matched entry tree, condition eval, key resolution (route key() > store
6
6
  * keyGenerator > default, CacheScope.resolveKeyFrom), tag resolution,
7
7
  * pre-handler-callback timing, and the fresh-hit / SWR-revalidate /
8
- * miss-write branches — for BOTH the production
9
- * response-route handler (rsc/response-route-handler.ts) and the dispatch testing
10
- * primitive (testing/dispatch.ts), so the two can never drift.
8
+ * miss-write branches — for the response-route handler
9
+ * (rsc/response-route-handler.ts), which the production handler and the
10
+ * dispatch testing primitive both run (rsc/request-stages.ts
11
+ * serveResponseRoute), so the two can never drift.
11
12
  *
12
- * Plugin-rsc hazard: cache-scope.ts pulls @vitejs/plugin-rsc (via segment-codec),
13
- * which the non-Vite unit-test runner cannot resolve, and this module is on the
14
- * testing barrel's EAGER graph (dispatch imports it). So `createCacheScope` is
15
- * NOT imported here at runtime — it is INJECTED by the caller (production
16
- * imports it statically; dispatch lazy-imports it only once a response route
17
- * matches). The only runtime imports here are plugin-rsc-free
18
- * (helpers' isCacheableStatus/finalizeResponse, traverseBack); cache-scope is a
19
- * type-only import (erased at build).
13
+ * `createCacheScope` is injected by the caller: the response-route handler
14
+ * passes the real one, unit tests a stub.
20
15
  */
21
16
  import type { CacheScope } from "../cache/cache-scope.js";
22
17
  import { type RequestContext } from "../server/request-context.js";
23
18
  import type { EntryCacheConfig, EntryData } from "../server/context.js";
24
- /** Injected cache-scope builder (kept off this module's runtime import graph). */
19
+ /** Injected cache-scope builder. */
25
20
  export interface CacheScopeDeps {
26
21
  createCacheScope: (config: EntryCacheConfig | undefined, parent?: CacheScope | null) => CacheScope | null;
27
22
  }
@@ -7,7 +7,7 @@
7
7
  */
8
8
  import type { MiddlewareFn } from "../router/middleware.js";
9
9
  import { type EntryData } from "../server/context.js";
10
- import type { HandlerContext } from "./handler-context.js";
10
+ import type { RequestStageContext } from "./request-stages.js";
11
11
  export interface ResponseRouteMatch {
12
12
  responseType: string;
13
13
  handler: Function;
@@ -26,4 +26,4 @@ export interface ResponseRouteMatch {
26
26
  * For partial (client-side navigation) requests, returns X-RSC-Reload
27
27
  * so the browser triggers a hard navigation to the response route URL.
28
28
  */
29
- export declare function handleResponseRoute<TEnv>(handlerCtx: HandlerContext<TEnv>, preview: ResponseRouteMatch, request: Request, env: TEnv, url: URL, variables: Record<string, any>): Promise<Response>;
29
+ export declare function handleResponseRoute<TEnv>(handlerCtx: Pick<RequestStageContext<TEnv>, "callOnError" | "getRequiredRouteMap" | "reportResponseRouteError">, preview: ResponseRouteMatch, request: Request, env: TEnv, url: URL, variables: Record<string, any>): Promise<Response>;
@@ -62,7 +62,7 @@ export declare function step<T>(name: string, execute: () => T | Promise<T>): Ro
62
62
  *
63
63
  * Settlement tracking follows the RETURNED value: a returned promise is
64
64
  * observed until it settles; a scheduler that fires-and-forgets internally
65
- * (e.g. scheduleShellCapture via runBackground) returns void, so its trace
65
+ * (e.g. planShellCapture via runBackground) returns void, so its trace
66
66
  * entry settles at the scheduling call, not at background completion. That is
67
67
  * deliberate — the plan's contract ends at the handoff.
68
68
  */
@@ -27,8 +27,8 @@ import type { MatchResult } from "../types.js";
27
27
  * the revalidation phase so it runs INSIDE the same route-middleware wrapper as
28
28
  * a successful revalidation — route middleware (context vars, headers, cookies)
29
29
  * must apply to the error render too, matching the module doc's "identical to a
30
- * normal render". When `errorBoundary` is set, `actionContext` is unused (the
31
- * boundary is already matched; no matchPartial is run).
30
+ * normal render". That render is the action's revalidation, cut at the
31
+ * boundary (ActionContext.boundary).
32
32
  */
33
33
  export interface ActionContinuation {
34
34
  returnValue: {
@@ -41,7 +41,7 @@ export declare const PPR_REPLAY_STATUS_HEADER: string;
41
41
  * producer (rsc-rendering.ts) and the parser (testing/shell-status.ts): a
42
42
  * reason the parser does not list parses as null.
43
43
  */
44
- export declare const PPR_REPLAY_BYPASS_REASONS: readonly ["method", "dynamic", "nonce", "store-unavailable", "passive-read-unsupported", "no-navigation-context", "prerender-store", "intercept", "cache-disabled", "read-error", "no-entry", "invalid-version", "corrupt-entry", "no-segment-snapshot", "snapshot-miss", "explicit-cache-hit", "rewritten"];
44
+ export declare const PPR_REPLAY_BYPASS_REASONS: readonly ["method", "stale", "dynamic", "nonce", "store-unavailable", "passive-read-unsupported", "no-navigation-context", "prerender-store", "intercept", "cache-disabled", "read-error", "no-entry", "invalid-version", "corrupt-entry", "no-segment-snapshot", "snapshot-miss", "explicit-cache-hit", "rewritten"];
45
45
  /** One of {@link PPR_REPLAY_BYPASS_REASONS}. */
46
46
  export type PprReplayBypassReason = (typeof PPR_REPLAY_BYPASS_REASONS)[number];
47
47
  /**
@@ -139,7 +139,7 @@ export declare const POST_QUIESCE_TASK_HOPS = 16;
139
139
  * short is an acceptable loss — every terminal path backs the key off anyway),
140
140
  * and below workerd's ~30s waitUntil grace so the cap timer still fires in a
141
141
  * live context. When workerd kills the context BEFORE the cap fires, the timer
142
- * dies with it — that stranding is healed by scheduleShellCapture's staleness
142
+ * dies with it — that stranding is healed by planShellCapture's staleness
143
143
  * check on the stampede guard, keyed off the same constant.
144
144
  */
145
145
  export declare const SHELL_CAPTURE_TASK_HARD_CAP_MS = 25000;
@@ -4,9 +4,9 @@
4
4
  * Capture does NOT flow through the HTTP middleware pipeline. The integrated PPR
5
5
  * serve path (rsc-rendering.ts + shell-serve.ts) builds a ShellCaptureDescriptor
6
6
  * from the route's `ppr` path option after the served response is built and calls
7
- * scheduleShellCapture. The capture then runs as a background task that re-derives
7
+ * planShellCapture with it (ShellCaptureInput). The capture then runs as a background task that re-derives
8
8
  * the page via `ctx.router.match()` under its OWN derived request context — fresh
9
- * handle store, `_shellCaptureRun: true` so live-lane loaders mask (loader-mask.ts)
9
+ * handle store, `_shellCaptureState.run` so live-lane loaders mask (loader-mask.ts)
10
10
  * and postpone at their loading() or inline <Suspense> boundary. The match is
11
11
  * MIXED-CHAIN: cache()'d segments replay from ring 3, uncached segments execute
12
12
  * their handlers. The record-first step (settleCaptureRecord) waits for the
@@ -20,7 +20,7 @@
20
20
  */
21
21
  import { type MatchResult } from "../types.js";
22
22
  import { type ShellCaptureRefusal } from "./shell-capture-constants.js";
23
- import { type RequestContext, type RouteRecordWindow } from "../server/request-context.js";
23
+ import { type RequestContext } from "../server/request-context.js";
24
24
  import { type HandleStore } from "../server/handle-store.js";
25
25
  import type { SegmentCacheStore } from "../cache/types.js";
26
26
  import type { HandlerContext } from "./handler-context.js";
@@ -128,7 +128,7 @@ export interface ShellCaptureDebugEvent {
128
128
  * - skip-stored: a capture for the key stored after the scheduling request
129
129
  * read the store and missed (ShellCaptureDescriptor.storedSeqAtRead), so
130
130
  * the shell that request missed exists now
131
- * - skip-in-flight: scheduleShellCapture found a capture already running for
131
+ * - skip-in-flight: planShellCapture found a capture already running for
132
132
  * the key (stampede guard) and scheduled nothing
133
133
  * - skip-backoff: the key is inside its refused-capture backoff window and
134
134
  * the capture was not attempted
@@ -268,7 +268,7 @@ export declare function gateFlightForCapture(source: ReadableStream<Uint8Array>)
268
268
  * The background shell-capture descriptor: everything the capture task needs to
269
269
  * store the shell. Built by the integrated PPR serve path (rsc-rendering.ts) from
270
270
  * the route's `ppr` path option (`PartialPrerenderProps`) and the app-level cache
271
- * store, and passed to scheduleShellCapture directly — it is NOT threaded through
271
+ * store, and passed to planShellCapture in its input — it is NOT threaded through
272
272
  * the request context. `tags` carries the route's OPERATIONAL `ppr.tags`; the
273
273
  * capture UNIONS them with the shell's own auto-collected (non-loader) request
274
274
  * tags from its derived render (the collected set stays authoritative). That
@@ -336,7 +336,7 @@ export interface ShellCaptureDescriptor {
336
336
  /**
337
337
  * Set for a shell keyed by the visitor's URL (`rewrite(...).shell("visitor")`,
338
338
  * visitorShellKey): the capture renders the visitor's location
339
- * (`RequestContext._shellCaptureForVisitor`) and stores it as
339
+ * (`ShellCaptureState.forVisitor`) and stores it as
340
340
  * ShellCacheEntry.pathname.
341
341
  */
342
342
  pathname?: string;
@@ -351,7 +351,7 @@ export interface ShellCaptureDescriptor {
351
351
  /**
352
352
  * A `router.prerender()` warm's capture (rsc-rendering.ts shellServePlan):
353
353
  * one explicit call that must store, so the guards that shed a herd of
354
- * visitors do not apply to it. scheduleShellCapture skips the in-flight
354
+ * visitors do not apply to it. planShellCapture skips the in-flight
355
355
  * dedup and the refused-capture backoff, and attemptCapture waits longer on
356
356
  * the write barrier. Every correctness guard, the inert-store skip and the
357
357
  * queue limits still decide.
@@ -360,6 +360,21 @@ export interface ShellCaptureDescriptor {
360
360
  }
361
361
  /** The latest stored capture's sequence number for `key` in this isolate, or 0. */
362
362
  export declare function lastStoredCaptureSeq(key: string): number;
363
+ /** One shell capture: the request it re-renders and what it stores. */
364
+ export interface ShellCaptureInput {
365
+ ctx: HandlerContext<any>;
366
+ request: Request;
367
+ env: any;
368
+ url: URL;
369
+ reqCtx: RequestContext<any>;
370
+ /**
371
+ * The SSR module for the capture's own request and URL, called once the
372
+ * capture leaves the queue. A document passes the module it already
373
+ * resolved, returned as is, so the capture awaits only a pending one.
374
+ */
375
+ ssr: (request: Request, url: URL) => SSRModule | null | Promise<SSRModule | null>;
376
+ descriptor: ShellCaptureDescriptor;
377
+ }
363
378
  /**
364
379
  * Schedule the background shell capture for a served document. Stampede-guarded:
365
380
  * one capture per key per isolate. Runs via runBackground (waitUntil on workerd,
@@ -367,15 +382,13 @@ export declare function lastStoredCaptureSeq(key: string): number;
367
382
  * error is routed through reportCacheError — capture is best-effort; a failure just
368
383
  * means the next request recaptures.
369
384
  *
370
- * Eligibility (nonce/partial/status/strategy) is decided by the caller. An SSR
371
- * module loader may be passed for cold partial requests; it runs only after the
372
- * capture enters the guarded background queue, never on response latency.
385
+ * Eligibility (nonce/partial/status/strategy) is decided by the caller.
373
386
  */
374
- export declare function scheduleShellCapture(ctx: HandlerContext<any>, request: Request, env: any, url: URL, reqCtx: RequestContext<any>, ssrModule: SSRModule | ((request: Request, url: URL) => Promise<SSRModule | null>), descriptor: ShellCaptureDescriptor): void;
387
+ export declare function planShellCapture(input: ShellCaptureInput): void;
375
388
  /**
376
389
  * The ttl/swr a shell is stored with: the route's ppr policy, capped by the
377
390
  * route cache() records the capture replayed or wrote
378
- * (RequestContext._routeRecordWindow). A HIT replays the capture's handler
391
+ * (ShellCaptureState.routeRecordWindow). A HIT replays the capture's handler
379
392
  * output, while a client navigation reads the route's cache() tier first, so
380
393
  * a shell that outlived its record would show a document request other
381
394
  * handler output than a navigation of the same URL. Freshness and total
@@ -456,9 +469,9 @@ type CaptureAttemptStats = Pick<ShellCaptureDebugEvent, "barrierWaitMs" | "write
456
469
  * several HTTP requests before a capture stuck. We retry ONLY on `no-shell`
457
470
  * that did not run out of ppr.captureTimeout (CaptureAttemptStats.noShellCause),
458
471
  * and on `expired`; a genuine render error is NOT retried — it propagates to
459
- * scheduleShellCapture's reportCacheError. See docs/design/ppr-shell-resume.md.
472
+ * planShellCapture's reportCacheError. See docs/design/ppr-shell-resume.md.
460
473
  *
461
- * `request` and `url` are the capture's own identity (scheduleShellCapture
474
+ * `request` and `url` are the capture's own identity (planShellCapture
462
475
  * strips a navigation-only capture's transport parameters). `retryDelayMs` is
463
476
  * a parameter (defaulting to the module const) so unit tests can drive the
464
477
  * retry without a real 400ms wall-clock wait.
@@ -511,9 +524,113 @@ export type CaptureRecordOutcome = {
511
524
  * a HIT restores, and its handle encode has its own 5s timeout that must
512
525
  * never be the one that fires); then the capture's onResponse callbacks fire
513
526
  * with a synthetic 200 (the implicit doc scope's cacheRoute, an explicit
514
- * scope's own write), and the deferred writes settle.
527
+ * scope's own write), and the deferred writes settle. A match holding an
528
+ * error or notFound boundary throws before the callbacks fire.
515
529
  */
516
530
  export declare function settleCaptureRecord(match: MatchResult, derivation: CaptureContextDerivation, capture: Pick<ShellCaptureDescriptor, "key">, deadline: number): Promise<CaptureRecordOutcome>;
531
+ /**
532
+ * @internal The freshness window of a route cache() record, as ms
533
+ * timestamps: fresh until `freshUntil` (its `expiresAt`), stale until
534
+ * `staleUntil` (plus its scope's swr). `ttl`, `swr` and `written` describe
535
+ * the record that sets `staleUntil`: its cache() config, and whether this
536
+ * capture wrote it (cacheRoute) rather than read it.
537
+ */
538
+ export interface RouteRecordWindow {
539
+ freshUntil: number;
540
+ staleUntil: number;
541
+ ttl: number;
542
+ swr: number;
543
+ written: boolean;
544
+ }
545
+ /**
546
+ * @internal What a shell capture's render records about itself, on its
547
+ * derived request context as `_shellCaptureState`.
548
+ * RENDER_PASS_FIELDS["shell-capture"] (server/render-pass.ts) creates it with
549
+ * every field present, so it keeps one shape while the capture writes it. A
550
+ * background re-render the capture starts has none: it writes a route
551
+ * record, under cache() rules (RENDER_PASS_FIELDS["background-revalidation"]).
552
+ */
553
+ export interface ShellCaptureState {
554
+ /**
555
+ * @internal PPR shell-capture ACTIVE marker. True ONLY inside the background
556
+ * capture task's derived request context (built by shell-capture.ts). This is
557
+ * the switch every capture-specific behavior reads: loader masking
558
+ * (loader-mask.ts isShellCaptureActive / resolveOwnerLoaders) and the
559
+ * identity-read capture guard (server/context.ts guardIdentityRead). The
560
+ * foreground render never sets it, so the
561
+ * served response is byte-identical to axis 1. The capture descriptor itself
562
+ * (key/ttl/swr/tags/store) is NOT threaded through the request context — the
563
+ * integrated PPR serve path (rsc/shell-serve.ts + rsc-rendering.ts) builds it
564
+ * locally and passes it to planShellCapture in its input.
565
+ */
566
+ readonly run: true;
567
+ /**
568
+ * @internal Set beside `run` when the shell is keyed by the
569
+ * visitor's URL (`rewrite(...).shell("visitor")`): the capture renders the
570
+ * visitor's location and `ctx.rewrittenFrom`; the theme stays the default.
571
+ */
572
+ forVisitor: boolean | undefined;
573
+ /**
574
+ * @internal Bake-lane loader containers collected DURING a shell capture:
575
+ * segment-key -> the loader's (pre-wrap) result promise. Populated by
576
+ * resolveLoaderData for bake-lane loaders (lane rule: see its JSDoc in
577
+ * loader-cache.ts). Drained by captureAndStoreShell after the shell
578
+ * quiesces: settled containers are promise-elided, Flight-serialized, and
579
+ * pinned into the snapshot's loader family; a REJECTED container refuses
580
+ * the capture (error UI must never bake into the shared shell).
581
+ */
582
+ loaderRecords: Map<string, Promise<unknown>>;
583
+ /**
584
+ * @internal Set when a loader pushed a
585
+ * value the record cannot keep (masked nested promises, a pushed promise
586
+ * that settled to one, a live-lane loader's push, or a push outside a
587
+ * loader body): the loader records then carry `runs: 1`, so a HIT still
588
+ * runs their bodies for those pushes.
589
+ */
590
+ unrecordedLoaderPush: true | undefined;
591
+ /**
592
+ * @internal The lane of each loader the capture's matched route registers
593
+ * (loader-cache.ts routeLoaderLanes), set by createMatchContextForFull
594
+ * before any loader runs. The capture's push funnel credits a push made
595
+ * under a live-lane loader's body, at any depth, to that loader
596
+ * (shell-capture.ts deriveShellCaptureContext).
597
+ */
598
+ loaderLanes: ReadonlyMap<string, "live" | "bake"> | undefined;
599
+ /**
600
+ * @internal Set by the capture guard (server/context.ts
601
+ * guardIdentityRead) when it throws DURING a capture render: the read
602
+ * ("cookies()", "headers()", `ctx.get() for a non-cacheable variable`,
603
+ * "ctx.theme") and the fix the
604
+ * refusal warning gives for it. Load-bearing for the
605
+ * bake lane: a guard throw inside an executing loader is swallowed by
606
+ * wrapLoaderPromise into per-loader error UI, which would otherwise bake
607
+ * silently into the shared shell — the capture checks this flag after the
608
+ * render and refuses instead. Deterministic, so the capture does not retry.
609
+ */
610
+ guardTripped: {
611
+ surface: string;
612
+ fix: string;
613
+ } | undefined;
614
+ /**
615
+ * @internal The loader $$id whose BODY was executing when the capture guard
616
+ * tripped (read off the loader-body ALS scope at trip time), or undefined
617
+ * when the read came from handler/render code. Only used to make the
618
+ * once-per-key refusal warning name the real source — the old text
619
+ * hardcoded "a bake-lane loader", which misattributed handler-land reads
620
+ * and sent users debugging the wrong lane (issue #672, secondary).
621
+ */
622
+ guardTrippedLoaderId: string | undefined;
623
+ /**
624
+ * @internal The freshness window of the route cache() records this
625
+ * capture read or wrote (cache-scope.ts noteRouteRecordWindow), as ms
626
+ * timestamps: `freshUntil` is the record's `expiresAt`, `staleUntil` adds
627
+ * its scope's swr. The narrowest record wins. A shell capture caps its
628
+ * entry's ttl and ttl+swr to it (shell-capture.ts capShellWindow), so a
629
+ * shell is never fresher, or served longer, than the route cache() entry
630
+ * it replayed or wrote.
631
+ */
632
+ routeRecordWindow: RouteRecordWindow | undefined;
633
+ }
517
634
  /**
518
635
  * The derived capture context and its fresh (mask-funneled) handle store,
519
636
  * shared by BOTH shell producers: the runtime background capture
@@ -17,6 +17,7 @@
17
17
  */
18
18
  import { type EntryData } from "../server/context.js";
19
19
  import type { DocumentShellCacheEntry, ShellCacheEntry, ShellDocumentRead, ShellEntryHead, ShellReadStats, ShellSnapshotFailure, ShellSnapshotRecord, SegmentCacheStore } from "../cache/types.js";
20
+ import type { MetricsStore } from "../server/context.js";
20
21
  export { PPR_REPLAY_STATUS_HEADER, SHELL_STATUS_HEADER, buildShellKey, navigationShellKey, partitionShellKey, shellSearchSeed, } from "./shell-capture-constants.js";
21
22
  /**
22
23
  * Default shell ttl (seconds) for `ppr: true` and for a PartialPrerenderProps
@@ -76,18 +77,13 @@ export declare function normalizeCaptureTimeout(value: unknown): number | undefi
76
77
  */
77
78
  export declare function resolvePprConfig(entry: EntryData | undefined | null): ResolvedPprConfig | null;
78
79
  /**
79
- * The forced-MISS query marker. A document HIT that cannot finish its tail
80
- * reloads the page with it (shellReloadScript); the serve gate renders a
81
- * request carrying it on axis 1, with no shell read and no capture, so the
82
- * reload can never be a HIT that degrades again. The handler drops it from
83
- * the request on entry (withoutShellMissMarker) and keeps only the flag
84
- * (RequestContext._shellForcedMiss), so nothing else sees it.
85
- */
86
- export declare const SHELL_MISS_PARAM: string;
87
- /**
88
- * The request without the forced-MISS marker, or undefined when it carries
89
- * none. The handler calls it before anything reads the request: left in, the
90
- * marker reached `ctx.request.url`, `originalUrl`, middleware, and the SSR
80
+ * The request without the forced-MISS marker (`WIRE.shellMiss`), or
81
+ * undefined when it carries none. A document HIT that cannot finish its tail
82
+ * reloads the page with the marker (shellReloadScript); the serve gate
83
+ * renders a request carrying it on axis 1, with no shell read and no
84
+ * capture, so the reload can never be a HIT that degrades again. The handler
85
+ * calls this on entry, before anything reads the request, and keeps only the
86
+ * flag (RequestContext._shellForcedMiss): left in, the marker reached `ctx.request.url`, `originalUrl`, middleware, and the SSR
91
87
  * search seed (so `useSearchParams` rendered it), and a `"use cache"` key
92
88
  * built from the request URL was never read again. GET and HEAD only: the
93
89
  * reload is a navigation, and a request with a body cannot be re-created
@@ -165,6 +161,35 @@ export interface ShellDocument {
165
161
  */
166
162
  export declare function openShellDocument(entry: ShellCacheEntry): ShellDocument | null;
167
163
  export declare function openShellDocument(entry: ShellEntryHead, read: Pick<ShellDocumentRead, "prelude" | "snapshot" | "snapshotFailure" | "stats">): ShellDocument | null;
164
+ /**
165
+ * A document serve's store read: a getShell entry, or a prelude-first read
166
+ * (readShellDocument) whose entry is the head and whose snapshot arrives on
167
+ * its own promise.
168
+ */
169
+ export type ShellRead = {
170
+ entry: ShellCacheEntry;
171
+ shouldRevalidate?: boolean;
172
+ read?: undefined;
173
+ } | {
174
+ entry: ShellEntryHead;
175
+ shouldRevalidate?: boolean;
176
+ read: ShellDocumentRead;
177
+ };
178
+ /**
179
+ * The document serve's shell read from `source`, the app cache store, that
180
+ * degrades to null on failure (axis 1 MISS, never a 500) and records the raw
181
+ * `ppr:shell-read` outcome (pre-validity-gates, so a version-mismatch
182
+ * lifecycle miss stays distinguishable from a store miss). A store with a
183
+ * prelude-first read (readShellDocument) resolves before the snapshot is
184
+ * read, so the HIT commits without waiting for it. A build shell is read by
185
+ * shell-build-manifest.ts lookupBuildShell, which imports this module.
186
+ */
187
+ export declare function readShell(source: SegmentCacheStore & {
188
+ getShell: NonNullable<SegmentCacheStore["getShell"]>;
189
+ }, key: string, opts: {
190
+ tagHints?: readonly string[];
191
+ metrics?: MetricsStore;
192
+ }): Promise<ShellRead | null>;
168
193
  /**
169
194
  * Prelude enqueue granularity. A streaming compressor in front of the worker
170
195
  * emits its first byte only after compressing the whole write it was handed:
@@ -121,7 +121,7 @@ export type LoaderEntry = {
121
121
  * thrown notFound()/redirect() deterministically precede first flush.
122
122
  * Resolved at DSL-evaluation time from ctx.isSSR — entries are cached
123
123
  * per-isSSR (router/manifest.ts cache key), so the flag is already
124
- * request-mode-correct when resolveLoaders (fresh.ts) reads it and never
124
+ * request-mode-correct when resolveOwnerLoaders reads it and never
125
125
  * appears on navigation-lane entries. Set by documentModeLoader, which
126
126
  * getDocumentModeEntry also applies to a lazy include's captured ancestors.
127
127
  */
@@ -566,7 +566,7 @@ export interface IdentityReadWording {
566
566
  * only observed; it is never rendered. A cached body, loader body or
567
567
  * funnel they start is guarded again.
568
568
  * 1. A PPR shell capture (`ctx` is the capture's derived context,
569
- * `_shellCaptureRun`) trips first: the capture context is flagged (so a
569
+ * `_shellCaptureState`) trips first: the capture context is flagged (so a
570
570
  * caught throw still refuses the capture) and the read throws. The shell is
571
571
  * shared per host+URL, and every HIT replays what the capture read. There
572
572
  * is no loader-body exemption here: a bake-lane loader and a loader a
@@ -737,12 +737,13 @@ export declare function latchPprHeaderScopeForEntries(entries: EntryData[], rout
737
737
  * ONLY the ppr (shell) axis is dropped — dynamic() does NOT opt off the CACHE
738
738
  * axis. Two cases:
739
739
  * - Pure ppr funnel (no cache() boundary): clear the latch → writes re-permit.
740
- * - ppr route nested under a cache() boundary: fresh.ts latches "ppr" at the
741
- * funnel top (first-wins), which MASKS the positional cache() latch, but the
742
- * handler still runs inside the cache scope (`insideCacheScope`). A cache()
743
- * HIT skips that handler, so the write is still non-deterministic — UNMASK to
744
- * "cache" instead of clearing, so the guard keeps throwing (accurate cache()
745
- * wording). This is why the check keys off `insideCacheScope`, not just kind.
740
+ * - ppr route nested under a cache() boundary: resolveAllSegments
741
+ * latches "ppr" at the funnel top (first-wins), which MASKS the positional
742
+ * cache() latch, but the handler still runs inside the cache scope
743
+ * (`insideCacheScope`). A cache() HIT skips that handler, so the write is
744
+ * still non-deterministic — UNMASK to "cache" instead of clearing, so the
745
+ * guard keeps throwing (accurate cache() wording). This is why the check keys
746
+ * off `insideCacheScope`, not just kind.
746
747
  *
747
748
  * A subsequent cache() entered AFTER dynamic() on a pure-ppr funnel re-latches
748
749
  * "cache" via latchCachedHeaderScope's `!store.cachedHeaderScope` guard (the
@@ -169,6 +169,22 @@ export interface HandleStore {
169
169
  * array, in order. Without placeholders it is a plain replay.
170
170
  */
171
171
  replacePlaceholders(loaderIds: readonly string[], deliver: () => void): void;
172
+ /**
173
+ * A cache() or Prerender() HIT restored its stored values into
174
+ * `segmentIds` (cache-lookup.ts serveStoredSegments). The MISS ran each
175
+ * segment's handler after it started the segment's loaders, so a push into
176
+ * a restored array goes in front of the stored values until
177
+ * releaseRecordedValues: a loader's push lands where the same loader timing
178
+ * puts it on the MISS.
179
+ */
180
+ holdRecordedValues(segmentIds: readonly string[]): void;
181
+ /**
182
+ * The HIT's loader walk reached the place where the MISS ran
183
+ * `segmentId`'s handler (revalidation.ts resolveLoadersOnly,
184
+ * intercept-resolution.ts resolveInterceptLoadersOnly): later pushes
185
+ * follow the record's values.
186
+ */
187
+ releaseRecordedValues(segmentId: string): void;
172
188
  /**
173
189
  * Get all collected handle data after all handlers have settled.
174
190
  * Waits for `settled` (handler lane), then returns the data at that point.
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Derived render passes over a request past its middleware (PPR shell
3
+ * capture, PPR HIT tail, background re-render, document cache re-run), each
4
+ * `Object.create(base)`.
5
+ * A field missing from a RENDER_PASS_FIELDS row reads the foreground
6
+ * request's value (#684). derived-context-shape.rsc-test.tsx pins each row's
7
+ * own keys.
8
+ */
9
+ import type { RecordingShellStore } from "../cache/shell-snapshot.js";
10
+ import type { HandleStore } from "./handle-store.js";
11
+ import { type RequestContext } from "./request-context.js";
12
+ type ShellImplicitCache = NonNullable<RequestContext["_shellImplicitCache"]>;
13
+ /** _handleStore, readonly on RequestContext, is written by the rows. */
14
+ type PassUnderConstruction = RequestContext<any> & {
15
+ _handleStore: HandleStore;
16
+ };
17
+ /** What each purpose's caller builds for its row. */
18
+ export interface RenderPassInit {
19
+ "shell-capture": {
20
+ handleStore: HandleStore;
21
+ /** A navigation-only capture renders this URL instead of the request's. */
22
+ identity: {
23
+ request: Request;
24
+ url: URL;
25
+ } | undefined;
26
+ /** Only with a cache store on the request. */
27
+ recording: {
28
+ store: RecordingShellStore;
29
+ implicitCache: ShellImplicitCache;
30
+ } | undefined;
31
+ };
32
+ "ppr-tail": {
33
+ /** The request's store, shared: the tail's handles are this response's. */
34
+ handleStore: HandleStore;
35
+ loaderSeed: RequestContext["_shellLoaderSeed"];
36
+ implicitCache: ShellImplicitCache;
37
+ };
38
+ "background-revalidation": {
39
+ handleStore: HandleStore;
40
+ /** Throwaway: the pass's response writes land here and are dropped. */
41
+ sink: RequestContext<any>;
42
+ };
43
+ "document-revalidation": {
44
+ /** The request's: the stale HIT pushed nothing into it. */
45
+ handleStore: HandleStore;
46
+ /**
47
+ * Holds the re-run's response writes, which make the response the
48
+ * document cache stores; starts from the request's response stub.
49
+ */
50
+ sink: RequestContext<any>;
51
+ };
52
+ }
53
+ export type RenderPurpose = keyof RenderPassInit;
54
+ interface PassFieldPolicy<I> {
55
+ /** "own": wireRenderBarrier(pass, init.handleStore) before `own` runs. */
56
+ readonly renderBarrier: "own" | "inherited";
57
+ /**
58
+ * Named stores, not one Object.assign: on Node 24 an Object.assign that
59
+ * takes the object to 20 own properties leaves it in V8 dictionary mode
60
+ * (#1109).
61
+ */
62
+ readonly own: (pass: PassUnderConstruction, init: I, base: RequestContext<any>) => void;
63
+ }
64
+ export declare const RENDER_PASS_FIELDS: {
65
+ readonly [P in RenderPurpose]: PassFieldPolicy<RenderPassInit[P]>;
66
+ };
67
+ /**
68
+ * `Object.create(base)` with RENDER_PASS_FIELDS[purpose]'s own fields. The only
69
+ * place a request context is derived (tools/check-internals.mjs).
70
+ */
71
+ export declare function deriveRenderPass<P extends RenderPurpose, TEnv>(base: RequestContext<TEnv>, purpose: P, init: RenderPassInit[P]): RequestContext<TEnv>;
72
+ export {};