@rangojs/router 0.20.0 → 0.22.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 (331) hide show
  1. package/dist/bin/rango.js +39 -15
  2. package/dist/types/browser/event-controller.d.ts +49 -0
  3. package/dist/types/browser/history-state.d.ts +16 -4
  4. package/dist/types/browser/navigation-store.d.ts +9 -0
  5. package/dist/types/browser/navigation-transaction.d.ts +7 -0
  6. package/dist/types/browser/partial-update.d.ts +2 -1
  7. package/dist/types/browser/prefetch/default-strategy.d.ts +16 -20
  8. package/dist/types/browser/rango-state.d.ts +3 -1
  9. package/dist/types/browser/react/NavigationProvider.d.ts +14 -1
  10. package/dist/types/browser/react/context.d.ts +25 -0
  11. package/dist/types/browser/react/location-state-shared.d.ts +84 -10
  12. package/dist/types/browser/react/location-state.d.ts +7 -2
  13. package/dist/types/browser/rsc-router.d.ts +13 -2
  14. package/dist/types/browser/types.d.ts +10 -0
  15. package/dist/types/build/generate-manifest.d.ts +2 -0
  16. package/dist/types/cache/background-task.d.ts +22 -0
  17. package/dist/types/cache/cache-key-utils.d.ts +50 -5
  18. package/dist/types/cache/cache-policy.d.ts +17 -0
  19. package/dist/types/cache/cache-scope.d.ts +5 -6
  20. package/dist/types/cache/cf/cf-cache-store.d.ts +34 -7
  21. package/dist/types/cache/cf/cf-cache-types.d.ts +12 -4
  22. package/dist/types/cache/document-cache.d.ts +8 -5
  23. package/dist/types/cache/handle-snapshot.d.ts +59 -50
  24. package/dist/types/cache/index.d.ts +3 -1
  25. package/dist/types/cache/invalidation-order.d.ts +14 -0
  26. package/dist/types/cache/memory-segment-store.d.ts +33 -1
  27. package/dist/types/cache/shell-snapshot.d.ts +29 -4
  28. package/dist/types/cache/store-scope.d.ts +22 -0
  29. package/dist/types/cache/tag-invalidation.d.ts +7 -6
  30. package/dist/types/cache/types.d.ts +84 -24
  31. package/dist/types/cache/vercel/vercel-cache-store.d.ts +35 -17
  32. package/dist/types/client-urls/navigation.d.ts +7 -1
  33. package/dist/types/client-urls/optimistic-location.d.ts +10 -4
  34. package/dist/types/errors.d.ts +6 -0
  35. package/dist/types/handles/deferred-resolution.d.ts +18 -1
  36. package/dist/types/internal-suspense-audit.d.ts +1 -0
  37. package/dist/types/loader-store.d.ts +4 -8
  38. package/dist/types/outlet-provider.d.ts +2 -1
  39. package/dist/types/prerender/build-shell-capture.d.ts +8 -6
  40. package/dist/types/prerender/cloudflare.d.ts +49 -0
  41. package/dist/types/prerender/create-prerender-trigger.d.ts +121 -0
  42. package/dist/types/prerender/index.d.ts +12 -0
  43. package/dist/types/prerender/memory-prerender-store.d.ts +29 -0
  44. package/dist/types/prerender/on-demand.d.ts +272 -0
  45. package/dist/types/prerender/producer-guard.d.ts +33 -0
  46. package/dist/types/prerender/shell-manifest-key.d.ts +1 -18
  47. package/dist/types/prerender/warm-request.d.ts +65 -0
  48. package/dist/types/prerender/warm.d.ts +65 -0
  49. package/dist/types/prerender/writable-store.d.ts +170 -0
  50. package/dist/types/prerender.d.ts +27 -0
  51. package/dist/types/reverse.d.ts +1 -1
  52. package/dist/types/route-content-wrapper.d.ts +3 -1
  53. package/dist/types/router/error-handling.d.ts +2 -0
  54. package/dist/types/router/handler-context.d.ts +1 -1
  55. package/dist/types/router/match-middleware/cache-lookup.d.ts +15 -1
  56. package/dist/types/router/navigation-snapshot.d.ts +8 -1
  57. package/dist/types/router/pattern-matching.d.ts +2 -0
  58. package/dist/types/router/prefetch-default.d.ts +4 -4
  59. package/dist/types/router/prerender-match.d.ts +23 -1
  60. package/dist/types/router/route-trie-builder.d.ts +8 -1
  61. package/dist/types/router/router-context.d.ts +1 -0
  62. package/dist/types/router/router-interfaces.d.ts +32 -1
  63. package/dist/types/router/router-options.d.ts +39 -5
  64. package/dist/types/router/segment-resolution/fresh.d.ts +7 -0
  65. package/dist/types/router/segment-resolution/loader-cache.d.ts +23 -10
  66. package/dist/types/router/segment-resolution/revalidation.d.ts +5 -1
  67. package/dist/types/router/segment-resolution/streamed-handler-boundary.d.ts +43 -0
  68. package/dist/types/router/trie-matching.d.ts +2 -0
  69. package/dist/types/router-versions.d.ts +39 -0
  70. package/dist/types/rsc/handler-context.d.ts +5 -0
  71. package/dist/types/rsc/shell-build-manifest.d.ts +9 -6
  72. package/dist/types/rsc/shell-capture-constants.d.ts +20 -5
  73. package/dist/types/rsc/shell-capture.d.ts +14 -1
  74. package/dist/types/rsc/shell-serve.d.ts +2 -2
  75. package/dist/types/rsc/ssr-module-loader.d.ts +15 -0
  76. package/dist/types/rsc/types.d.ts +12 -6
  77. package/dist/types/segment-boundary-content.d.ts +17 -0
  78. package/dist/types/segment-loader-promise.d.ts +2 -6
  79. package/dist/types/server/build-version-table.d.ts +53 -0
  80. package/dist/types/server/context.d.ts +12 -4
  81. package/dist/types/server/handle-store.d.ts +89 -46
  82. package/dist/types/server/request-context.d.ts +84 -9
  83. package/dist/types/suspense-audit-react.d.ts +26 -0
  84. package/dist/types/suspense-audit.d.ts +157 -0
  85. package/dist/types/testing/action-form.d.ts +34 -0
  86. package/dist/types/testing/build-versions.d.ts +32 -0
  87. package/dist/types/testing/dom.entry.d.ts +1 -1
  88. package/dist/types/testing/flight.entry.d.ts +3 -1
  89. package/dist/types/testing/index.d.ts +6 -0
  90. package/dist/types/testing/internal/seed-vars.d.ts +7 -2
  91. package/dist/types/testing/internal/server-modules.d.ts +8 -0
  92. package/dist/types/testing/location-state-key.d.ts +5 -1
  93. package/dist/types/testing/render-route.d.ts +77 -6
  94. package/dist/types/testing/serve-shell-request.d.ts +55 -4
  95. package/dist/types/testing/shell-status.d.ts +14 -6
  96. package/dist/types/testing/vitest-stubs/plugin-rsc.d.ts +9 -6
  97. package/dist/types/testing/vitest-stubs/version.d.ts +1 -0
  98. package/dist/types/thenable-status.d.ts +6 -0
  99. package/dist/types/types/handler-context.d.ts +15 -5
  100. package/dist/types/types/route-entry.d.ts +5 -0
  101. package/dist/types/urls/path-helper.d.ts +18 -0
  102. package/dist/types/vite/discovery/build-versions.d.ts +393 -0
  103. package/dist/types/vite/discovery/installed-packages.d.ts +26 -0
  104. package/dist/types/vite/discovery/router-versions-phase.d.ts +52 -0
  105. package/dist/types/vite/discovery/shell-prerender-phase.d.ts +7 -17
  106. package/dist/types/vite/discovery/state.d.ts +25 -0
  107. package/dist/types/vite/encryption-key.d.ts +18 -0
  108. package/dist/types/vite/inject-client-debug.d.ts +6 -25
  109. package/dist/types/vite/plugin-types.d.ts +27 -0
  110. package/dist/types/vite/plugins/{version-injector.d.ts → entry-bootstrap-injector.d.ts} +4 -6
  111. package/dist/types/vite/plugins/expose-ids/export-analysis.d.ts +1 -0
  112. package/dist/types/vite/plugins/expose-ids/router-transform.d.ts +14 -0
  113. package/dist/types/vite/plugins/stable-rsc-output.d.ts +51 -0
  114. package/dist/types/vite/plugins/version-plugin.d.ts +9 -11
  115. package/dist/types/vite/plugins/virtual-entries.d.ts +18 -1
  116. package/dist/types/vite/router-discovery.d.ts +6 -0
  117. package/dist/types/vite/utils/bundle-analysis.d.ts +6 -2
  118. package/dist/types/vite/utils/client-chunks.d.ts +2 -0
  119. package/dist/types/vite/utils/compare-strings.d.ts +7 -0
  120. package/dist/types/vite/utils/package-resolution.d.ts +8 -0
  121. package/dist/types/vite/utils/prerender-utils.d.ts +2 -0
  122. package/dist/vite/index.js +1707 -931
  123. package/package.json +12 -3
  124. package/skills/cache-guide/SKILL.md +102 -37
  125. package/skills/caching/SKILL.md +56 -9
  126. package/skills/catalog.json +1 -1
  127. package/skills/client-urls/SKILL.md +16 -0
  128. package/skills/cloudflare/SKILL.md +133 -1
  129. package/skills/comparison/references/framework-comparison.md +17 -13
  130. package/skills/deployment-caching/SKILL.md +33 -12
  131. package/skills/document-cache/SKILL.md +16 -14
  132. package/skills/hooks/SKILL.md +25 -0
  133. package/skills/hooks/handle-and-actions.md +5 -0
  134. package/skills/hooks/navigation.md +38 -0
  135. package/skills/hooks/state.md +213 -11
  136. package/skills/hooks/urls.md +40 -1
  137. package/skills/host-router/SKILL.md +31 -0
  138. package/skills/intercept/SKILL.md +6 -0
  139. package/skills/loader/SKILL.md +25 -5
  140. package/skills/middleware/SKILL.md +2 -0
  141. package/skills/migrate-nextjs/SKILL.md +82 -9
  142. package/skills/ppr/SKILL.md +150 -48
  143. package/skills/prerender/SKILL.md +651 -10
  144. package/skills/rango/SKILL.md +9 -7
  145. package/skills/response-routes/SKILL.md +5 -5
  146. package/skills/route/SKILL.md +29 -0
  147. package/skills/router-setup/SKILL.md +15 -1
  148. package/skills/testing/SKILL.md +32 -27
  149. package/skills/testing/cache-prerender.md +18 -17
  150. package/skills/testing/client-components.md +193 -8
  151. package/skills/testing/handles.md +2 -0
  152. package/skills/testing/loader.md +1 -1
  153. package/skills/testing/middleware.md +1 -1
  154. package/skills/testing/render-handler.md +18 -18
  155. package/skills/testing/serve-shell-request.md +167 -22
  156. package/skills/testing/server-actions.md +17 -17
  157. package/skills/testing/server-tree.md +1 -1
  158. package/skills/typesafety/generated-files-and-cli.md +1 -1
  159. package/skills/use-cache/SKILL.md +25 -10
  160. package/skills/vercel/SKILL.md +5 -3
  161. package/skills/view-transitions/SKILL.md +9 -4
  162. package/src/bin/rango.ts +1 -1
  163. package/src/browser/event-controller.ts +119 -2
  164. package/src/browser/history-state.ts +39 -17
  165. package/src/browser/link-interceptor.ts +2 -2
  166. package/src/browser/navigation-bridge.ts +104 -41
  167. package/src/browser/navigation-store.ts +26 -0
  168. package/src/browser/navigation-transaction.ts +24 -12
  169. package/src/browser/network-error-handler.ts +2 -0
  170. package/src/browser/partial-update.ts +58 -14
  171. package/src/browser/prefetch/default-strategy.ts +22 -42
  172. package/src/browser/rango-state.ts +3 -1
  173. package/src/browser/react/Link.tsx +37 -43
  174. package/src/browser/react/NavigationProvider.tsx +85 -1
  175. package/src/browser/react/context.ts +30 -0
  176. package/src/browser/react/location-state-shared.ts +200 -34
  177. package/src/browser/react/location-state.ts +28 -77
  178. package/src/browser/react/use-action.ts +4 -1
  179. package/src/browser/react/use-handle.ts +3 -0
  180. package/src/browser/react/use-link-status.ts +4 -3
  181. package/src/browser/react/use-navigation.ts +4 -1
  182. package/src/browser/react/use-params.ts +4 -1
  183. package/src/browser/react/use-pathname.ts +4 -1
  184. package/src/browser/react/use-search-params.ts +5 -3
  185. package/src/browser/react/use-segments.ts +36 -54
  186. package/src/browser/rsc-router.tsx +93 -31
  187. package/src/browser/segment-reconciler.ts +3 -3
  188. package/src/browser/server-action-bridge.ts +43 -7
  189. package/src/browser/types.ts +11 -0
  190. package/src/build/generate-manifest.ts +17 -0
  191. package/src/build/merge-full-manifests.ts +5 -0
  192. package/src/build/route-trie.ts +1 -0
  193. package/src/cache/background-task.ts +61 -0
  194. package/src/cache/cache-key-utils.ts +91 -12
  195. package/src/cache/cache-policy.ts +11 -2
  196. package/src/cache/cache-runtime.ts +23 -23
  197. package/src/cache/cache-scope.ts +48 -36
  198. package/src/cache/cf/cf-cache-store.ts +91 -66
  199. package/src/cache/cf/cf-cache-types.ts +12 -4
  200. package/src/cache/document-cache.ts +35 -14
  201. package/src/cache/handle-snapshot.ts +118 -86
  202. package/src/cache/index.ts +5 -0
  203. package/src/cache/invalidation-order.ts +53 -0
  204. package/src/cache/memory-segment-store.ts +52 -1
  205. package/src/cache/shell-snapshot.ts +52 -5
  206. package/src/cache/store-scope.ts +36 -0
  207. package/src/cache/tag-invalidation.ts +7 -5
  208. package/src/cache/types.ts +90 -24
  209. package/src/cache/vercel/vercel-cache-store.ts +68 -26
  210. package/src/client-urls/client-root.tsx +1 -0
  211. package/src/client-urls/navigation.ts +10 -0
  212. package/src/client-urls/optimistic-location.ts +10 -4
  213. package/src/client.tsx +40 -3
  214. package/src/errors.ts +13 -6
  215. package/src/handles/deferred-resolution.ts +36 -0
  216. package/src/internal-suspense-audit.ts +12 -0
  217. package/src/loader-store.ts +4 -9
  218. package/src/outlet-provider.tsx +25 -1
  219. package/src/prerender/build-shell-capture.ts +11 -9
  220. package/src/prerender/cloudflare.ts +130 -0
  221. package/src/prerender/create-prerender-trigger.ts +780 -0
  222. package/src/prerender/index.ts +39 -0
  223. package/src/prerender/memory-prerender-store.ts +115 -0
  224. package/src/prerender/on-demand.ts +339 -0
  225. package/src/prerender/producer-guard.ts +66 -0
  226. package/src/prerender/shell-manifest-key.ts +12 -17
  227. package/src/prerender/warm-request.ts +154 -0
  228. package/src/prerender/warm.ts +193 -0
  229. package/src/prerender/writable-store.ts +278 -0
  230. package/src/prerender.ts +29 -0
  231. package/src/reverse.ts +1 -1
  232. package/src/route-content-wrapper.tsx +107 -13
  233. package/src/router/error-handling.ts +12 -12
  234. package/src/router/find-match.ts +1 -0
  235. package/src/router/handler-context.ts +2 -0
  236. package/src/router/intercept-resolution.ts +15 -8
  237. package/src/router/loader-resolution.ts +2 -10
  238. package/src/router/manifest.ts +2 -0
  239. package/src/router/match-middleware/background-revalidation.ts +2 -0
  240. package/src/router/match-middleware/cache-lookup.ts +239 -54
  241. package/src/router/navigation-snapshot.ts +25 -6
  242. package/src/router/pattern-matching.ts +14 -0
  243. package/src/router/prefetch-default.ts +4 -4
  244. package/src/router/prerender-match.ts +460 -352
  245. package/src/router/request-classification.ts +15 -12
  246. package/src/router/route-trie-builder.ts +9 -0
  247. package/src/router/router-context.ts +1 -0
  248. package/src/router/router-interfaces.ts +35 -1
  249. package/src/router/router-options.ts +45 -5
  250. package/src/router/segment-resolution/fresh.ts +20 -19
  251. package/src/router/segment-resolution/loader-cache.ts +110 -77
  252. package/src/router/segment-resolution/revalidation.ts +34 -24
  253. package/src/router/segment-resolution/streamed-handler-boundary.ts +133 -0
  254. package/src/router/trie-matching.ts +3 -0
  255. package/src/router-versions.ts +60 -0
  256. package/src/router.ts +107 -1
  257. package/src/rsc/full-payload.ts +6 -2
  258. package/src/rsc/handler-context.ts +5 -0
  259. package/src/rsc/handler.ts +64 -7
  260. package/src/rsc/progressive-enhancement.ts +5 -1
  261. package/src/rsc/response-cache-serve.ts +10 -4
  262. package/src/rsc/rsc-rendering.ts +65 -12
  263. package/src/rsc/shell-build-manifest.ts +17 -12
  264. package/src/rsc/shell-capture-constants.ts +39 -7
  265. package/src/rsc/shell-capture.ts +144 -45
  266. package/src/rsc/shell-serve.ts +12 -6
  267. package/src/rsc/ssr-module-loader.ts +26 -0
  268. package/src/rsc/types.ts +12 -6
  269. package/src/segment-boundary-content.ts +25 -0
  270. package/src/segment-loader-promise.ts +28 -38
  271. package/src/segment-system.tsx +117 -57
  272. package/src/server/build-version-table.ts +130 -0
  273. package/src/server/context.ts +40 -17
  274. package/src/server/cookie-store.ts +5 -0
  275. package/src/server/handle-store.ts +357 -127
  276. package/src/server/request-context.ts +149 -12
  277. package/src/ssr/ssr-root.tsx +32 -13
  278. package/src/suspense-audit-react.tsx +98 -0
  279. package/src/suspense-audit.ts +1030 -0
  280. package/src/testing/action-form.ts +66 -0
  281. package/src/testing/build-versions.ts +64 -0
  282. package/src/testing/dispatch.ts +6 -0
  283. package/src/testing/dom.entry.ts +1 -0
  284. package/src/testing/flight-runtime.d.ts +27 -0
  285. package/src/testing/flight.entry.ts +4 -0
  286. package/src/testing/index.ts +22 -0
  287. package/src/testing/internal/context.ts +2 -1
  288. package/src/testing/internal/seed-vars.ts +14 -2
  289. package/src/testing/internal/server-modules.ts +32 -0
  290. package/src/testing/location-state-key.ts +18 -8
  291. package/src/testing/render-handler.ts +2 -1
  292. package/src/testing/render-route.tsx +293 -46
  293. package/src/testing/serve-shell-request.ts +262 -37
  294. package/src/testing/shell-status.ts +17 -6
  295. package/src/testing/vitest-stubs/plugin-rsc.ts +37 -11
  296. package/src/testing/vitest-stubs/version.ts +4 -1
  297. package/src/thenable-status.ts +10 -0
  298. package/src/types/handler-context.ts +15 -5
  299. package/src/types/route-entry.ts +6 -0
  300. package/src/urls/path-helper.ts +69 -9
  301. package/src/use-loader.tsx +57 -30
  302. package/src/vite/discovery/build-versions.ts +1281 -0
  303. package/src/vite/discovery/bundle-postprocess.ts +4 -0
  304. package/src/vite/discovery/discover-routers.ts +15 -0
  305. package/src/vite/discovery/installed-packages.ts +159 -0
  306. package/src/vite/discovery/prerender-collection.ts +38 -1
  307. package/src/vite/discovery/router-versions-phase.ts +336 -0
  308. package/src/vite/discovery/shell-prerender-phase.ts +33 -43
  309. package/src/vite/discovery/state.ts +36 -1
  310. package/src/vite/discovery/virtual-module-codegen.ts +7 -2
  311. package/src/vite/encryption-key.ts +101 -9
  312. package/src/vite/inject-client-debug.ts +42 -20
  313. package/src/vite/plugin-types.ts +28 -0
  314. package/src/vite/plugins/{version-injector.ts → entry-bootstrap-injector.ts} +7 -22
  315. package/src/vite/plugins/expose-ids/export-analysis.ts +1 -1
  316. package/src/vite/plugins/expose-ids/router-transform.ts +36 -15
  317. package/src/vite/plugins/expose-internal-ids.ts +12 -3
  318. package/src/vite/plugins/stable-rsc-output.ts +147 -0
  319. package/src/vite/plugins/version-plugin.ts +19 -24
  320. package/src/vite/plugins/version.d.ts +17 -2
  321. package/src/vite/plugins/virtual-entries.ts +39 -6
  322. package/src/vite/rango.ts +28 -3
  323. package/src/vite/router-discovery.ts +96 -13
  324. package/src/vite/utils/bundle-analysis.ts +39 -7
  325. package/src/vite/utils/client-chunks.ts +5 -1
  326. package/src/vite/utils/compare-strings.ts +9 -0
  327. package/src/vite/utils/package-resolution.ts +9 -0
  328. package/src/vite/utils/prerender-utils.ts +4 -1
  329. package/src/vite/utils/shared-utils.ts +1 -1
  330. package/dist/types/segment-content-promise.d.ts +0 -13
  331. package/src/segment-content-promise.ts +0 -67
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Streamed handlers (loading()): track, observe, and recover a rejection into
3
+ * the declared errorBoundary() / notFoundBoundary() fallback.
4
+ *
5
+ * Invariants:
6
+ * - No status write and no onError call: the status is committed at first
7
+ * flush, and trackHandler already reports each rejection once.
8
+ * - The recovered promise resolves, so Flight reports no error. The writers
9
+ * that refuse a failed render through Flight errors need the failure
10
+ * recorded here: `_renderErrors` for the document cache and a prerender
11
+ * warm, `_recoveredHandlerErrors` for cacheRoute (cache(), and the doc record
12
+ * a PPR capture needs). The shell capture and the SWR re-render own both,
13
+ * so a derived render never writes into (or reads) the foreground's.
14
+ * - Nothing to render to rethrows the original rejection: no boundary found, a
15
+ * thrown Response (redirect), a Skip, a throwing fallback.
16
+ * - No recovery when `recover` is false (prerender throwOnError, the intercept
17
+ * background re-render): those lanes must reject so nothing is baked/cached.
18
+ */
19
+ import type { ReactNode } from "react";
20
+ import type { EntryData } from "../../server/context.js";
21
+ import type { ErrorInfo, HandlerContext } from "../../types/index.js";
22
+ import type { SegmentResolutionDeps } from "../types.js";
23
+ export interface StreamedHandlerOptions<TEnv> {
24
+ /**
25
+ * Entry to look boundaries up from: the matched chain entry being resolved
26
+ * (where the sync path catches, whatever routeless layout owns a slot), or
27
+ * the intercept's declaring entry.
28
+ */
29
+ boundaryEntry: EntryData;
30
+ segmentId: string;
31
+ segmentType: ErrorInfo["segmentType"];
32
+ context: HandlerContext<any, TEnv>;
33
+ routeKey?: string;
34
+ params: Record<string, string>;
35
+ /** False where the rejection must reach the caller (prerender, intercept background re-render). */
36
+ recover: boolean;
37
+ }
38
+ /**
39
+ * Track a streamed handler promise and return the segment component: the
40
+ * tracked promise itself when there is nothing to recover to, else a promise
41
+ * that resolves to the declared fallback on rejection.
42
+ */
43
+ export declare function trackStreamedHandler<TEnv>(deps: SegmentResolutionDeps<TEnv>, result: Promise<unknown>, options: StreamedHandlerOptions<TEnv>): ReactNode;
@@ -18,6 +18,8 @@ export interface TrieMatchResult {
18
18
  pr?: true;
19
19
  /** Passthrough: handler kept for live fallback on unknown params */
20
20
  pt?: true;
21
+ /** On-demand prerender eligible (router.prerender() refreshable) */
22
+ od?: true;
21
23
  /** Response type for non-RSC routes (json, text, image, any) */
22
24
  responseType?: string;
23
25
  /** Negotiate variants: response-type routes sharing this path */
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Per-router cache versions: the shapes shared by the build (which computes
3
+ * them, vite/discovery/build-versions.ts) and the runtime (which reads them,
4
+ * server/build-version-table.ts). No imports, so the Vite plugin bundle can
5
+ * use it without resolving the `@rangojs/router:version` virtual module.
6
+ *
7
+ * Design: docs/design/per-app-cache-version.md.
8
+ */
9
+ /**
10
+ * The two versions one `createRouter()` serves with.
11
+ *
12
+ * - `data` keys cached RSC data: segment entries, `"use cache"` values and
13
+ * loader data. It follows the router's server code only.
14
+ * - `document` keys stored HTML (PPR shells, document-cache responses), is the
15
+ * value the browser echoes as `_rsc_v`, and stamps `ShellCacheEntry.buildVersion`.
16
+ * It follows the server code and the client asset URLs.
17
+ */
18
+ export interface RouterVersions {
19
+ readonly data: string;
20
+ readonly document: string;
21
+ }
22
+ /**
23
+ * The build's table as the `@rangojs/router:version` module exports it:
24
+ * router id to `[data, document]`. {@link DEFAULT_ROUTER_VERSIONS_KEY} holds
25
+ * the whole-build pair, used for a router the build could not attribute.
26
+ */
27
+ export type RouterVersionsTable = Readonly<Record<string, readonly [data: string, document: string]>>;
28
+ /** Table key of the whole-build versions. Not a legal `$$id` (8 hex chars). */
29
+ export declare const DEFAULT_ROUTER_VERSIONS_KEY = "*";
30
+ /** One value for both versions: a consumer-set `version`, or the dev stamp. */
31
+ export declare function uniformVersions(version: string): RouterVersions;
32
+ /**
33
+ * The versions a router serves with under `table`: a consumer-set `version`
34
+ * for both; else the router's own entry; else the whole-build pair, for a
35
+ * router the table does not list. `undefined` when there is no table (dev,
36
+ * tests) or it has neither entry. One function for the request handler and
37
+ * the build's shell capture, which must stamp what the handler will check.
38
+ */
39
+ export declare function resolveVersionsFrom(table: RouterVersionsTable | undefined, routerId: string | undefined, override: string | undefined): RouterVersions | undefined;
@@ -12,6 +12,11 @@ import type { RSCDependencies, LoadSSRModule } from "./types.js";
12
12
  import type { SSRStreamMode } from "../router/router-options.js";
13
13
  export interface HandlerContext<TEnv = unknown> {
14
14
  router: RangoInternal<TEnv, any>;
15
+ /**
16
+ * The router's DOCUMENT version (router-versions.ts): payload metadata, the
17
+ * `_rsc_v` reload check, and PPR shell stamping and gating. The data version
18
+ * is not here; cache stores read it from the request context.
19
+ */
15
20
  version: string;
16
21
  devDiscoveryEpoch?: number;
17
22
  renderToReadableStream: RSCDependencies["renderToReadableStream"];
@@ -11,8 +11,9 @@
11
11
  * served through the same serveShellHit as a captured one.
12
12
  *
13
13
  * Lifecycle:
14
- * - No expiry until the next deploy — the buildVersion gate retires entries
15
- * the moment a new build ships (a new manifest replaces them anyway).
14
+ * - No expiry until the next deploy: every build ships its own manifest, and
15
+ * its entries carry that build's document version for the router, which is
16
+ * what the buildVersion gate compares.
16
17
  * - `ppr.ttl` drives STALENESS ONLY: past createdAt + ttl the entry still
17
18
  * serves, but a runtime recapture is scheduled — SWR is the UPGRADE path
18
19
  * from build entry to fresher runtime entry, not the bootstrap path. The
@@ -35,7 +36,7 @@ export interface BuildShellEntry {
35
36
  tags?: string[];
36
37
  }
37
38
  interface ShellManifestModule {
38
- /** Manifest key (pathname — see shell-manifest-key.ts) -> asset specifier. */
39
+ /** Manifest key (router id + pathname, shell-manifest-key.ts) -> asset specifier. */
39
40
  default: Record<string, string>;
40
41
  loadShellAsset: (spec: string) => Promise<{
41
42
  default: BuildShellEntry;
@@ -79,11 +80,13 @@ export interface DevShellLookup {
79
80
  * request is search-less, like the entries lookupBuildShell serves): a
80
81
  * manifest entry for the path in production, a Prerender route in dev. No
81
82
  * asset load and no dev capture: it only decides whether a request that
82
- * cannot read the build shell deserves a warning.
83
+ * cannot read the build shell deserves a warning. The manifest entry is the
84
+ * one staged for the router `routerId` (shell-manifest-key.ts).
83
85
  */
84
- export declare function hasBuildShell(pathname: string, dev?: DevShellLookup): Promise<boolean>;
86
+ export declare function hasBuildShell(routerId: string, pathname: string, dev?: DevShellLookup): Promise<boolean>;
85
87
  /**
86
88
  * Look up the baked shell entry for a request, applying every serve gate:
89
+ * the serving router's own entry only (`routerId`, shell-manifest-key.ts),
87
90
  * search-less requests only (the build captured the bare pathname; a
88
91
  * search-bearing URL has its own shell identity owned by runtime capture),
89
92
  * version validity, payload integrity, and tag-invalidation markers. Returns
@@ -95,5 +98,5 @@ export declare function hasBuildShell(pathname: string, dev?: DevShellLookup): P
95
98
  * exclusion) matches the baked shell — ad-click traffic is exactly the
96
99
  * traffic the shell was prerendered for.
97
100
  */
98
- export declare function lookupBuildShell(url: URL, buildVersion: string, store: SegmentCacheStore, dev?: DevShellLookup, filter?: SearchParamsFilter): Promise<BuildShellHit | null>;
101
+ export declare function lookupBuildShell(routerId: string, url: URL, buildVersion: string, store: SegmentCacheStore, dev?: DevShellLookup, filter?: SearchParamsFilter): Promise<BuildShellHit | null>;
99
102
  export {};
@@ -56,13 +56,14 @@ export type PprReplayBypassReason = (typeof PPR_REPLAY_BYPASS_REASONS)[number];
56
56
  */
57
57
  export declare function shellSearchSeed(url: URL, filter?: SearchParamsFilter): string;
58
58
  /**
59
- * Shell cache key: host + pathname + sorted search + a `:shell` namespace suffix
60
- * (so it can never collide with a document-cache key; the store further isolates
61
- * the shell family internally).
59
+ * Shell cache key: router id + host + pathname + sorted search + a `:shell`
60
+ * namespace suffix (so it can never collide with a document-cache key; the
61
+ * store further isolates the shell family internally).
62
62
  *
63
63
  * The key includes the request HOST: in a multi-tenant host-router deployment
64
64
  * (one worker, one shared KV/runtime-cache store) a host-less key would serve
65
- * tenant A's captured shell to tenant B's users.
65
+ * tenant A's captured shell to tenant B's users. It starts with the serving
66
+ * ROUTER for the same reason (cache-key-utils.ts, the router rule).
66
67
  *
67
68
  * `filter` is the request's compiled `cache.searchParams` config
68
69
  * (ctx._searchParamsFilter): excluded params collapse onto one shell slot.
@@ -70,7 +71,7 @@ export declare function shellSearchSeed(url: URL, filter?: SearchParamsFilter):
70
71
  * and lookup makes every shell request a permanent miss. The testing helper
71
72
  * `shellCacheKey` (testing/shell-status.ts) builds its key with it too.
72
73
  */
73
- export declare function buildShellKey(url: URL, filter?: SearchParamsFilter): string;
74
+ export declare function buildShellKey(routerId: string, url: URL, filter?: SearchParamsFilter): string;
74
75
  /**
75
76
  * The shell key for one request partition (cache-scope.ts
76
77
  * resolveShellPartition): the route cache() `key()` or store keyGenerator
@@ -133,6 +134,20 @@ export declare const POST_QUIESCE_TASK_HOPS = 16;
133
134
  * check on the stampede guard, keyed off the same constant.
134
135
  */
135
136
  export declare const SHELL_CAPTURE_TASK_HARD_CAP_MS = 25000;
137
+ /**
138
+ * Why a capture attempt refused to store its shell
139
+ * (ShellCaptureDebugEvent.refusal, `caches.refusal` on a `router.prerender()`
140
+ * result): `identity` (a request-scoped read: cookies(), headers(), the
141
+ * theme, a `{ cache: false }` variable), `dynamic` (`ctx.dynamic()`),
142
+ * `loader` (a `{ ssr: false }` loader rejected, or settled with
143
+ * redirect()/notFound()), `no-record` (no doc record: the route's cache()
144
+ * refused the write, or the handler output failed), `handles` (a pushed
145
+ * handle value did not finish encoding), `size` (over the store's value
146
+ * limit), `record-expired` (the route cache() record the capture wrote ran
147
+ * out first), `invalidated` (a tag of the shell was invalidated after the
148
+ * capture started), `uncacheable` (the store cannot hold the entry).
149
+ */
150
+ export type ShellCaptureRefusal = "identity" | "dynamic" | "loader" | "no-record" | "handles" | "size" | "record-expired" | "invalidated" | "uncacheable";
136
151
  /**
137
152
  * Shared PPR loader-lane hint, appended to every capture warning or error
138
153
  * whose fix depends on the lane (shell-capture.ts warnings, the cookie-store.ts
@@ -19,6 +19,7 @@
19
19
  * (variables, cache store). Guarding is serve-time.
20
20
  */
21
21
  import { type MatchResult } from "../types.js";
22
+ import { type ShellCaptureRefusal } from "./shell-capture-constants.js";
22
23
  import { type RequestContext, type RouteRecordWindow } from "../server/request-context.js";
23
24
  import { type HandleStore } from "../server/handle-store.js";
24
25
  import type { SegmentCacheStore } from "../cache/types.js";
@@ -170,6 +171,8 @@ export interface ShellCaptureDebugEvent {
170
171
  untaggedBake?: true;
171
172
  /** Outcome reported by a store that supports shell-write acknowledgements. */
172
173
  storeWrite?: "stored" | "invalidated" | "uncacheable";
174
+ /** Why a `refused` attempt refused. */
175
+ refusal?: ShellCaptureRefusal;
173
176
  /** Consecutive failure count in the key's backoff entry, when one exists. */
174
177
  backoffFailures?: number;
175
178
  /** Ms remaining in the key's backoff window, when one exists. */
@@ -335,6 +338,15 @@ export interface ShellCaptureDescriptor {
335
338
  * re-render the page and drop the isolate's memo of the shell just stored.
336
339
  */
337
340
  storedSeqAtRead?: number;
341
+ /**
342
+ * A `router.prerender()` warm's capture (rsc-rendering.ts shellServePlan):
343
+ * one explicit call that must store, so the guards that shed a herd of
344
+ * visitors do not apply to it. scheduleShellCapture skips the in-flight
345
+ * dedup and the refused-capture backoff, and attemptCapture waits longer on
346
+ * the write barrier. Every correctness guard, the inert-store skip and the
347
+ * queue limits still decide.
348
+ */
349
+ force?: true;
338
350
  }
339
351
  /** The latest stored capture's sequence number for `key` in this isolate, or 0. */
340
352
  export declare function lastStoredCaptureSeq(key: string): number;
@@ -400,7 +412,7 @@ type CaptureTaskOutcome = "stored" | "redirect" | "no-shell";
400
412
  * the warning. A plain mutable bag, not a return value: captureAndStoreShell's
401
413
  * outcome type stays a string union producer B consumes unchanged.
402
414
  */
403
- type CaptureAttemptStats = Pick<ShellCaptureDebugEvent, "barrierWaitMs" | "writeSettleMs" | "preludeBytes" | "snapshotBytes" | "snapshotSkipped" | "untaggedBake" | "storeWrite" | "recordSettleMs" | "entryBytes"> & {
415
+ type CaptureAttemptStats = Pick<ShellCaptureDebugEvent, "barrierWaitMs" | "writeSettleMs" | "preludeBytes" | "snapshotBytes" | "snapshotSkipped" | "untaggedBake" | "storeWrite" | "refusal" | "recordSettleMs" | "entryBytes"> & {
404
416
  /**
405
417
  * Why a no-shell attempt ran out of ppr.captureTimeout (the match, the
406
418
  * handler output, or the prerender after them). A set cause skips the
@@ -462,6 +474,7 @@ export type CaptureRecordOutcome = {
462
474
  reason: string;
463
475
  } | {
464
476
  kind: "refused";
477
+ refusal: ShellCaptureRefusal;
465
478
  };
466
479
  /**
467
480
  * Record-first capture: the doc record is both the settle signal and the
@@ -274,7 +274,7 @@ export declare function warnPprWindowCappedOnce(routeName: string, ppr: {
274
274
  * Whether a partitioned request needs no build-shell probe: its route was
275
275
  * already warned, or its path was already found without one.
276
276
  */
277
- export declare function partitionBuildShellCheckDone(pathname: string, routeName: string | undefined): boolean;
277
+ export declare function partitionBuildShellCheckDone(routerId: string, pathname: string, routeName: string | undefined): boolean;
278
278
  /**
279
279
  * Record a partitioned request's build-shell probe, and warn once per route
280
280
  * when it found one: the route has a build-time shell its requests cannot
@@ -285,7 +285,7 @@ export declare function partitionBuildShellCheckDone(pathname: string, routeName
285
285
  * declared-intent-cannot-be-honored doctrine as the warnings above; a path
286
286
  * with no build shell stays silent.
287
287
  */
288
- export declare function notePartitionBuildShellCheck(pathname: string, routeName: string | undefined, found: boolean): void;
288
+ export declare function notePartitionBuildShellCheck(routerId: string, pathname: string, routeName: string | undefined, found: boolean): void;
289
289
  /**
290
290
  * @internal Reset the serve path's once-per-key warnings (the shell path's
291
291
  * one registry), its build-shell probe memo and buffered tail timings. Tests
@@ -0,0 +1,15 @@
1
+ /**
2
+ * The SSR module loader a handler created without `loadSSRModule` falls back
3
+ * to in tests (`router.fetch` creates its handler that way). A leaf with no
4
+ * runtime imports: testing/serve-shell-request.ts installs its stub here when
5
+ * it loads, without importing rsc/handler.ts (which binds build-only virtual
6
+ * modules a flight-only test config does not alias).
7
+ */
8
+ import type { LoadSSRModule } from "./types.js";
9
+ /**
10
+ * @internal Tests only: `router.prerender()` warms through `router.fetch`,
11
+ * and the rsc Vitest project cannot run `import.meta.viteRsc.loadModule`.
12
+ */
13
+ export declare function setDefaultSSRModuleLoaderForTests(loader: LoadSSRModule | undefined): void;
14
+ /** @internal Undefined everywhere but in tests. */
15
+ export declare function defaultSSRModuleLoaderForTests(): LoadSSRModule | undefined;
@@ -103,6 +103,14 @@ export interface RscPayload {
103
103
  ok: boolean;
104
104
  data: unknown;
105
105
  };
106
+ /**
107
+ * useActionState form state of a progressive-enhancement POST re-render
108
+ * (the value the SSR render received). Present only on that document; the
109
+ * browser entry passes it to hydrateRoot so the hydrated hook matches the
110
+ * server HTML instead of resetting to its initial state. A promise so a
111
+ * state Flight cannot serialize rejects this slot alone.
112
+ */
113
+ formState?: Promise<ReactFormState>;
106
114
  }
107
115
  /**
108
116
  * React form state type for useActionState progressive enhancement
@@ -321,13 +329,11 @@ export interface CreateRSCHandlerOptions<TEnv = unknown, TRoutes extends Record<
321
329
  */
322
330
  cache?: HandlerCacheConfig | ((env: TEnv) => HandlerCacheConfig);
323
331
  /**
324
- * RSC version string included in metadata.
325
- * The browser sends this back on partial requests to detect version mismatches.
326
- *
327
- * Defaults to the auto-generated VERSION from `rsc-router:version` virtual module.
328
- * Only set this if you need a custom versioning strategy.
332
+ * Version override for this handler. Same meaning as
333
+ * `createRouter({ version })`, which it takes precedence over: this exact
334
+ * value is used for both of the router's versions.
329
335
  *
330
- * @default VERSION from rsc-router:version
336
+ * @default createRouter({ version }), else the router's build versions
331
337
  */
332
338
  version?: string;
333
339
  /**
@@ -0,0 +1,17 @@
1
+ import type { ReactNode } from "react";
2
+ /**
3
+ * What a RouteContentWrapper receives as `content`.
4
+ *
5
+ * Browser: hand over the settled value. React treats a promise as fulfilled
6
+ * only after it has read it, and a boundary on screen handed a promise it has
7
+ * not read, in a render that cannot wait, shows its fallback. A component that
8
+ * is already a promise (a Flight-streamed segment) is returned as is, since
9
+ * it can really be pending.
10
+ *
11
+ * Server: a fresh `Promise.resolve(component)` per call, so Suspense emits the
12
+ * fallback in the streamed HTML. A shared resolved promise would carry React's
13
+ * `.status` across requests and skip it.
14
+ *
15
+ * @internal
16
+ */
17
+ export declare function getBoundaryContent(component: ReactNode): ReactNode;
@@ -10,12 +10,8 @@ import type { ResolvedSegment } from "./types.js";
10
10
  */
11
11
  export declare function buildLoaderPromise(loaders: ResolvedSegment[]): Promise<any[]>;
12
12
  /**
13
- * Memoize an aggregate Promise.all for a set of loader segments. Reusing the
14
- * same aggregate across renders — invalidated only when any underlying
15
- * loader.loaderData ref changes — keeps React's `use()` in "known fulfilled"
16
- * state and prevents a fresh Promise.all from suspending (and briefly
17
- * committing the Suspense fallback) on every partial update that doesn't
18
- * actually change loader data.
13
+ * The aggregate for a set of loader segments, memoized on their `loaderData`
14
+ * refs: the settled array once it has fulfilled, the promise before that.
19
15
  *
20
16
  * @internal
21
17
  */
@@ -0,0 +1,53 @@
1
+ import { type RouterVersions, type RouterVersionsTable } from "../router-versions.js";
2
+ /**
3
+ * The versions a router serves with, in order:
4
+ *
5
+ * 1. `override`, a consumer-set `version` (createRouter or createRSCHandler):
6
+ * that exact value for both.
7
+ * 2. The router's entry in the build table, else the whole-build entry.
8
+ * 3. `VERSION` for both: the dev stamp, bumped on every RSC module edit.
9
+ */
10
+ export declare function resolveRouterVersions(routerId: string | undefined, override?: string): RouterVersions;
11
+ /**
12
+ * The version an on-demand prerender overlay key carries: the router's data
13
+ * version, because the overlay stores segment data, as the segment cache does
14
+ * (a client-only deploy keeps both). The write side (`router.prerender`,
15
+ * router.ts) and the read side (rsc/handler.ts) both call this with
16
+ * `createRouter({ version })`, the only override the trigger can see, so they
17
+ * cannot disagree; a `createRSCHandler({ version })` does not move it.
18
+ */
19
+ export declare function resolvePrerenderVersion(routerId: string | undefined, routerVersion: string | undefined): string;
20
+ /**
21
+ * The versions of the router serving the current request, for a cache store
22
+ * building a key. A store operation with no request context (a detached task
23
+ * that lost the ALS) gets the whole-build versions: it misses the request's
24
+ * entries instead of reading another version's. When routers have versions of
25
+ * their own that is a write no router reads back, so it is logged, once per
26
+ * process: `ctx.waitUntil()` re-enters the request context for every task it
27
+ * runs (server/request-context.ts), and this line in a production log means
28
+ * some other path lost it.
29
+ *
30
+ * Public from `@rangojs/router/cache`, for a custom persistent store to key
31
+ * the way the built-in ones do. Prefix cached RSC data (segment entries, items) with
32
+ * `data` and stored HTML or responses (shells, document responses) with
33
+ * `document`, and leave tag-invalidation records unversioned; an empty string
34
+ * means "no version" (tests). Call it per operation, inside the request, not
35
+ * in the store's constructor: the cache factory runs before the request
36
+ * context exists.
37
+ */
38
+ export declare function getCacheVersions(): RouterVersions;
39
+ /** Which of the serving router's versions a store key carries; `null`: none. */
40
+ export type KeyVersion = keyof RouterVersions | null;
41
+ /**
42
+ * The `v/{version}/` prefix of a persistent store key, or "" for an
43
+ * unversioned one. A store-level `version` is used for every versioned kind;
44
+ * otherwise the version is the serving router's, read per operation because
45
+ * the cache factory builds the store before the request context exists.
46
+ */
47
+ export declare function versionKeyPrefix(storeVersion: string | undefined, kind: KeyVersion): string;
48
+ /**
49
+ * @internal Install a table, or restore the build's with `undefined`. A
50
+ * request handler resolves its router's versions once, when it is created, so
51
+ * a test that swaps the table (a "deploy") needs new handlers.
52
+ */
53
+ export declare function installRouterVersionsTable(next: RouterVersionsTable | undefined): void;
@@ -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
  */