@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,121 @@
1
+ /**
2
+ * `router.prerender()` trigger factory.
3
+ *
4
+ * Builds the `router.prerender` binder: `(runtime) => runner`, the runner being
5
+ * the per-target callable (plus `.many` / `.remove` / `.markStale`) from router-supplied
6
+ * deps. Pure and testable: all router internals (reverse, match, the producer,
7
+ * the request handler) arrive as injected functions, so this module has no RSC
8
+ * imports and can be unit-tested with fakes.
9
+ *
10
+ * One verb, dispatched by route after the match:
11
+ * - A `Prerender(..., { onDemand })` route takes the requestless render into
12
+ * the prerender store (docs/design/ondemand-prerender.md), then, when the
13
+ * app cache store is shared and an origin resolves, a warm request that
14
+ * rebuilds the runtime caches on top of the new entry.
15
+ * - Every other route is warmed: a cookie-free GET through the router's own
16
+ * handler in which every runtime cache read misses and every write replaces
17
+ * the entry (docs/design/prerender-every-route.md).
18
+ *
19
+ * Contract highlights:
20
+ * - Requestless: the producer render never inherits the caller's request state.
21
+ * - A refresh always renders and replaces; `onlyIfStale` is the cron-sweep
22
+ * opt-in and the only path that returns `already-fresh`.
23
+ * - A failed render/store keeps the previous entry (replace-on-success).
24
+ * - A `notFound()` in the render is not a failure: it stores a "removed"
25
+ * marker in place of the page, stamped with the route's ttl and tags so it
26
+ * is rechecked like a page. `prerender.remove()` stores one without
27
+ * rendering, which never goes stale. A Passthrough build handler that
28
+ * declines (`ctx.passthrough()`) stores one too, so its live handler answers.
29
+ * - A page and a render's marker (a `notFound()` or a decline) carry the
30
+ * render's start as `storedAt`, so a `markStale()` that lands during the
31
+ * render marks the result; `staleAt` counts the ttl from the write. A
32
+ * `remove()` marker renders nothing and carries its write time.
33
+ * - An on-demand target is path-only (its key carries no search); a warm
34
+ * target keeps its search params, which cache keys carry.
35
+ * - A warm writes only to a store shared beyond the place the call runs.
36
+ */
37
+ import type { SerializedSegmentData } from "../cache/types.js";
38
+ import type { HandlerCacheConfig } from "../rsc/types.js";
39
+ import type { ExecutionContext } from "../types/request-scope.js";
40
+ import type { OnDemandRouteConfig, PrerenderConfig, PrerenderFn, PrerenderResult } from "./on-demand.js";
41
+ /** Route metadata the trigger needs, resolved from a pathname by the router. */
42
+ export interface PrerenderMatchInfo {
43
+ routeName: string;
44
+ params: Record<string, string>;
45
+ /** True when the route opted into on-demand (Prerender(..., { onDemand })). */
46
+ isOnDemand: boolean;
47
+ /** True when the route is wrapped in Passthrough(). */
48
+ isPassthrough: boolean;
49
+ }
50
+ /**
51
+ * Raw producer output (matchForPrerender's main-variant shape). `onDemandConfig`
52
+ * is the route's `onDemand: { ttl, tags }` object (the `tags` callback can't
53
+ * ride the serialized trie), read from the loaded route entry during production.
54
+ */
55
+ export interface ProducerOutput {
56
+ segments: SerializedSegmentData[];
57
+ handles: string;
58
+ routeName: string;
59
+ params: Record<string, string>;
60
+ passthrough?: true;
61
+ /** The render hit `notFound()` and nothing else failed: no page to store. */
62
+ notFound?: true;
63
+ onDemandConfig?: OnDemandRouteConfig;
64
+ }
65
+ export interface PrerenderTriggerDeps<TEnv = any> {
66
+ routerId: string;
67
+ /**
68
+ * The key version: the router's data version (resolvePrerenderVersion),
69
+ * resolved per refresh (once per `.many()` batch), as the request handler
70
+ * resolves it per request.
71
+ */
72
+ resolveVersion: () => string;
73
+ /** True when running under Vite dev (drives the producer context's `dev`). */
74
+ isDev: () => boolean;
75
+ /**
76
+ * True under the Vite dev server (isViteDevServer): the one place the
77
+ * memory store counts as shared, and so where the not-shared warning fires.
78
+ */
79
+ isViteDevServer: () => boolean;
80
+ /** Load the per-router manifest/trie before matching, as router.fetch does. */
81
+ ensureManifest: () => Promise<void>;
82
+ /** Resolve the env-scoped prerender config (factory or object); undefined when unconfigured. */
83
+ resolveConfig: (env: TEnv, ctx: ExecutionContext | undefined) => PrerenderConfig<TEnv> | undefined;
84
+ /** Reverse an object target to a pathname; undefined when the route name is unknown. */
85
+ reverse: (route: string, params: Record<string, string>) => string | undefined;
86
+ /** Match a pathname to route metadata; null when nothing matches. */
87
+ matchRoute: (pathname: string) => PrerenderMatchInfo | null | Promise<PrerenderMatchInfo | null>;
88
+ /** Run the requestless producer (matchForPrerender with onDemand). May throw. */
89
+ runProducer: (input: {
90
+ pathname: string;
91
+ isPassthrough: boolean;
92
+ env: TEnv;
93
+ dev: boolean;
94
+ }) => Promise<ProducerOutput | null>;
95
+ /**
96
+ * Resolve `createRouter({ cache })` (factory or object) for a warm;
97
+ * undefined when the router has none. May throw, like the prerender factory.
98
+ */
99
+ resolveCacheConfig: (env: TEnv, ctx: ExecutionContext) => HandlerCacheConfig | undefined;
100
+ /** The router's own request handler (`router.fetch`): where a warm is dispatched. */
101
+ fetch: (request: Request, input: {
102
+ env: TEnv;
103
+ ctx: ExecutionContext;
104
+ }) => Promise<Response>;
105
+ /** The origin of the request the runner is called from, when there is one. */
106
+ ambientOrigin: () => string | undefined;
107
+ }
108
+ /** Thrown by the trigger when `throwOnError: true` and the operation did not succeed. */
109
+ export declare class PrerenderError extends Error {
110
+ readonly result: Extract<PrerenderResult, {
111
+ ok: false;
112
+ }>;
113
+ /** `operation` is the runner call that failed; the message names it. */
114
+ constructor(result: Extract<PrerenderResult, {
115
+ ok: false;
116
+ }>, operation?: "prerender" | "remove");
117
+ }
118
+ /**
119
+ * Create the `router.prerender` callable. See {@link PrerenderTriggerDeps}.
120
+ */
121
+ export declare function createPrerenderTrigger<TEnv = any, TRoutes = {}>(deps: PrerenderTriggerDeps<TEnv>): PrerenderFn<TEnv, TRoutes>;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Public on-demand prerender surface (`@rangojs/router/prerender`).
3
+ *
4
+ * Store contracts, config/result/target types, and the in-memory store (usable
5
+ * as a durable overlay in single-process node/Vercel functions, and as a test
6
+ * fake). The Cloudflare KV adapter lives in `@rangojs/router/prerender/cloudflare`.
7
+ */
8
+ export { PrerenderError } from "./create-prerender-trigger.js";
9
+ export { createMemoryPrerenderStore, type MemoryPrerenderStore, type MemoryPrerenderStoreOptions, } from "./memory-prerender-store.js";
10
+ export { serializePrerenderKey, type PrerenderKey, type PrerenderStoredEntry, type WritablePrerenderStore, } from "./writable-store.js";
11
+ export type { OnDemandOption, OnDemandRouteConfig, PrerenderConfig, PrerenderFn, PrerenderResult, PrerenderWarmCaches, PrerenderManyOptions, PrerenderRunner, PrerenderRunOptions, PrerenderRuntime, PrerenderTarget, PrerenderTargetObject, } from "./on-demand.js";
12
+ export type { PrerenderEntry } from "./store.js";
@@ -0,0 +1,29 @@
1
+ /**
2
+ * In-memory writable prerender store.
3
+ *
4
+ * Backs the zero-config dev overlay and the `@rangojs/router/testing` fake.
5
+ * A plain Map — no per-isolate null memoization (a later set() must be visible
6
+ * after an earlier miss), no React/RSC deps.
7
+ */
8
+ import { type PrerenderKey, type PrerenderStoredEntry, type WritablePrerenderStore } from "./writable-store.js";
9
+ export interface MemoryPrerenderStore extends WritablePrerenderStore {
10
+ delete(key: PrerenderKey): Promise<void>;
11
+ markStale(routerId: string, tags: string[]): Promise<void>;
12
+ /** Synchronous read by structured key (tests). */
13
+ peek(key: PrerenderKey): PrerenderStoredEntry | null;
14
+ /** All stored [serializedKey, entry] pairs (tests). */
15
+ entries(): [string, PrerenderStoredEntry][];
16
+ /** Drop every entry. */
17
+ clear(): void;
18
+ readonly size: number;
19
+ }
20
+ export interface MemoryPrerenderStoreOptions {
21
+ /** Injectable clock for `markStale` in deterministic tests. Defaults to Date.now. */
22
+ now?: () => number;
23
+ }
24
+ /**
25
+ * Create an in-memory {@link WritablePrerenderStore} with test-inspection
26
+ * helpers. Invalidation is mark-stale (sets `staleAt`), never delete — matching
27
+ * the durable v1 contract so the entry keeps serving until refreshed.
28
+ */
29
+ export declare function createMemoryPrerenderStore(options?: MemoryPrerenderStoreOptions): MemoryPrerenderStore;
@@ -0,0 +1,272 @@
1
+ /**
2
+ * Public type surface for on-demand (ISR-style) prerender: the router
3
+ * `prerender` config option, the `router.prerender({ env, ctx })` runner, its typed
4
+ * target, and the inspectable result union.
5
+ *
6
+ * Types only — safe to import from router options, the public interfaces, the
7
+ * testing barrel, and platform adapters without pulling RSC deps.
8
+ */
9
+ import type { ExecutionContext } from "../types/request-scope.js";
10
+ import type { IsEmptyObject, ParamsFor } from "../reverse.js";
11
+ import type { WritablePrerenderStore } from "./writable-store.js";
12
+ import type { ShellCaptureRefusal } from "../rsc/shell-capture-constants.js";
13
+ declare const prerenderTargetBrand: unique symbol;
14
+ /**
15
+ * The target `onRevalidate` receives: a plain `{ route, params }` that goes
16
+ * straight into a queue message. The type-only brand (never present at
17
+ * runtime) lets the runner (`router.prerender({ env })(target)` / `.many()`) accepts it with no cast on a
18
+ * router typed with named routes, while a hand-written `{ route: "typo" }`
19
+ * stays a type error there. After a JSON round trip, assert it back:
20
+ * `JSON.parse(raw) as PrerenderTargetObject`.
21
+ */
22
+ export interface PrerenderTargetObject {
23
+ route: string;
24
+ params: Record<string, string>;
25
+ readonly [prerenderTargetBrand]: true;
26
+ }
27
+ /**
28
+ * Route map used to type the object target. When the fluent `TRoutes` phantom is
29
+ * empty (the common `createRouter().routes(...)` case leaves it `{}`), fall back
30
+ * to the generated global route map — the same fallback `Prerender<"name">` and
31
+ * `reverse` use — so `router.prerender({ route, params })` stays typed without an
32
+ * explicit route-type parameter.
33
+ */
34
+ type PrerenderRouteMap<TRoutes> = keyof TRoutes extends never ? keyof Rango.GeneratedRouteMap extends never ? {} : Rango.GeneratedRouteMap : TRoutes;
35
+ /**
36
+ * Typed object target, derived from the router's route map the same way
37
+ * `reverse` is. No-param routes may omit `params`.
38
+ */
39
+ type PrerenderRouteTarget<TRoutes> = {
40
+ [TName in keyof PrerenderRouteMap<TRoutes> & string]: IsEmptyObject<ParamsFor<PrerenderRouteMap<TRoutes>, TName>> extends true ? {
41
+ route: TName;
42
+ params?: Record<string, never>;
43
+ } : {
44
+ route: TName;
45
+ params: ParamsFor<PrerenderRouteMap<TRoutes>, TName>;
46
+ };
47
+ }[keyof PrerenderRouteMap<TRoutes> & string];
48
+ /**
49
+ * A prerender target: a typed named-route object, a path-like string/URL
50
+ * (`router.reverse()` output composes for free), or the `onRevalidate` target.
51
+ */
52
+ export type PrerenderTarget<TRoutes = {}> = string | URL | PrerenderRouteTarget<TRoutes> | PrerenderTargetObject;
53
+ /** Env + platform capabilities bound once by `router.prerender({ env, ctx })`. */
54
+ export interface PrerenderRuntime<TEnv = any> {
55
+ /** The live env: the producer's `ctx.env` (not the build's `buildEnv`). */
56
+ env: TEnv;
57
+ /** Cloudflare `ExecutionContext`; absent on node/Vercel. */
58
+ ctx?: ExecutionContext;
59
+ /**
60
+ * The origin a warm requests a path or `{ route, params }` target on, for
61
+ * example `"https://shop.example"`. Cache keys carry the host, so a warm
62
+ * must request the host visitors use. A full-URL target uses its own
63
+ * origin; without this option the runner uses the origin of the request it
64
+ * is called from (an action, a route handler, `onRevalidate`), and with
65
+ * neither a warm returns `skipped-no-origin`. An on-demand route's
66
+ * requestless render never needs it.
67
+ */
68
+ origin?: string;
69
+ }
70
+ /** Per-call options of the bound runner. */
71
+ export interface PrerenderRunOptions {
72
+ /** Throw on failure instead of returning an `{ ok: false }` result. */
73
+ throwOnError?: boolean;
74
+ /**
75
+ * Only refresh what is cold or stale (cron-sweep opt-in), and the only
76
+ * path that can return `already-fresh`. On an on-demand route the stored
77
+ * entry is rendered only when stale. On any other route the warm reads its
78
+ * caches normally instead of forcing misses, so a fresh entry is left
79
+ * alone, a stale one refreshes and a missing one is written.
80
+ */
81
+ onlyIfStale?: boolean;
82
+ }
83
+ /** Per-call options of the bound runner's `.many()`. */
84
+ export interface PrerenderManyOptions extends PrerenderRunOptions {
85
+ /** Max concurrent renders in a batch (default 1; any invalid value is 1). */
86
+ concurrency?: number;
87
+ }
88
+ /**
89
+ * What a warm request wrote, by cache. On a result of the warm path, and on
90
+ * an on-demand result whose route was also warmed.
91
+ */
92
+ export interface PrerenderWarmCaches {
93
+ /**
94
+ * Store writes that landed, by store family: `record` (route `cache()`),
95
+ * `item` (`"use cache"` and loader `cache()`), `response` (the document
96
+ * cache and response routes), `shell` (the `ppr` shell).
97
+ */
98
+ writes: {
99
+ record: number;
100
+ item: number;
101
+ response: number;
102
+ shell: number;
103
+ };
104
+ /**
105
+ * The `ppr` shell; absent on a route without `ppr`. `fresh` is
106
+ * `onlyIfStale` finding a servable shell; `not-eligible` is a request the
107
+ * shell path passed on (an active nonce, a store without shells, the
108
+ * route's own `cache()` opt-out, a buffered response).
109
+ */
110
+ shell?: "stored" | "fresh" | "refused" | "no-shell" | "not-eligible" | "skipped-capacity" | "skipped-queue-timeout" | "error";
111
+ /** Why the capture refused to store the shell. */
112
+ refusal?: ShellCaptureRefusal;
113
+ /** The document cache, when `createDocumentCacheMiddleware` ran. */
114
+ document?: "stored" | "not-cacheable";
115
+ }
116
+ /**
117
+ * Inspectable result, one per target. The trigger does not force callers
118
+ * into try/catch; `throwOnError: true` opts into throwing for admin/CI
119
+ * endpoints. `path` says what ran: `on-demand` is the requestless render of a
120
+ * `Prerender(..., { onDemand })` route into the prerender store, `warm` is a
121
+ * request through the router's handler that rewrites the route's runtime
122
+ * caches.
123
+ */
124
+ export type PrerenderResult = {
125
+ ok: true;
126
+ path: "on-demand";
127
+ /**
128
+ * `already-fresh` only occurs with `onlyIfStale: true`.
129
+ *
130
+ * `removed`: a "removed" marker is stored in place of the page, and
131
+ * requests no longer get the page or its build-time entry. It is
132
+ * `ok: true`, so count it in sweep results. Written by
133
+ * `prerender.remove()` (permanent until a refresh without
134
+ * `onlyIfStale`; `tags` empty, no `ttl`), or by a refresh in which a
135
+ * `notFound()` was the only thing that went wrong, anywhere in the
136
+ * render: the route handler, a layout or parallel slot handler, any
137
+ * server component. That marker has the route's `tags` and `ttl` and is
138
+ * rechecked like a page. `onlyIfStale` finding a fresh marker reports
139
+ * `removed` too. When the data source may be failing, throw anything
140
+ * else: that is `render-failed` and keeps the page.
141
+ */
142
+ status: "rendered" | "already-fresh" | "removed";
143
+ target: string;
144
+ routeName: string;
145
+ /** Opaque, for debugging/logs only — not a stable format. */
146
+ key: string;
147
+ tags: string[];
148
+ /** Absent = never stale. */
149
+ ttl?: number;
150
+ /**
151
+ * The warm request that followed the store write. Absent when the app
152
+ * store is not shared, no origin resolved, or nothing was rendered.
153
+ */
154
+ caches?: PrerenderWarmCaches;
155
+ } | {
156
+ ok: true;
157
+ path: "warm";
158
+ /** `already-fresh` only occurs with `onlyIfStale: true`. */
159
+ status: "warmed" | "already-fresh";
160
+ /** The absolute URL that was requested. */
161
+ target: string;
162
+ routeName: string;
163
+ /** The warm response's HTTP status. */
164
+ responseStatus: number;
165
+ caches: PrerenderWarmCaches;
166
+ } | {
167
+ ok: false;
168
+ /** Absent when no route matched: every other result is past the match. */
169
+ path?: "on-demand" | "warm";
170
+ status: "no-match" | "no-store" | "skipped-personalized" | "skipped-unsupported-target" | "skipped-passthrough" | "skipped-not-on-demand" | "skipped-store-not-shared" | "skipped-no-origin" | "shell-not-stored" | "skipped-uncached" | "render-failed" | "store-failed";
171
+ target: string;
172
+ routeName?: string;
173
+ /** The warm response's HTTP status, when a warm request ran. */
174
+ responseStatus?: number;
175
+ caches?: PrerenderWarmCaches;
176
+ error?: unknown;
177
+ };
178
+ /**
179
+ * Per-route on-demand opt-in, carried inside `PrerenderOptions.onDemand`. Any
180
+ * truthy spelling works (literal, spread, imported const): producer retention
181
+ * is driven by the evaluated route manifest, not a textual scan of the call.
182
+ */
183
+ export interface OnDemandRouteConfig {
184
+ /** Soft TTL (seconds) for entries refreshed for this route. Overrides the router `ttl`. */
185
+ ttl?: number;
186
+ /**
187
+ * Tags stamped on the stored entry, addressable via
188
+ * the runner's `markStale()`. A separate namespace from `cacheTag()` /
189
+ * `updateTag()` / `revalidateTag()`, which never reach the prerender store.
190
+ */
191
+ tags?: string[] | ((target: {
192
+ params: Record<string, string>;
193
+ }) => string[]);
194
+ }
195
+ export type OnDemandOption = boolean | OnDemandRouteConfig;
196
+ /**
197
+ * Env-resolved prerender store configuration. Same union shape as the `cache`
198
+ * option: a plain object or a factory resolved per request/per call, never at
199
+ * `createRouter()` time.
200
+ */
201
+ export interface PrerenderConfig<TEnv = any> {
202
+ store: WritablePrerenderStore;
203
+ /**
204
+ * Default soft TTL (seconds) for a route whose `onDemand` sets none. Absent
205
+ * = never stale. Soft: a stale entry keeps serving; staleness only decides
206
+ * whether `onRevalidate` is scheduled. Entries never expire.
207
+ */
208
+ ttl?: number;
209
+ /**
210
+ * Stale-while-revalidate: its presence is the opt-in. Scheduled through
211
+ * `waitUntil` on a stale overlay hit (the stale entry still serves) with
212
+ * the JSON-serializable target and the live env. Runs at most once per
213
+ * stale key per isolate while one is in flight (one running longer than
214
+ * 15 s counts as finished, so a hung call cannot block the key), so
215
+ * `(target, env, ctx) => router.prerender({ env, ctx })(target, { onlyIfStale: true })`
216
+ * is safe in a single process; across isolates, point it at a queue, which
217
+ * owns dedup. Pass `onlyIfStale` in the call that runs later: a job queued
218
+ * for a page that `prerender.remove()` then removed finds the marker and
219
+ * renders nothing, where a plain refresh would bring the page back.
220
+ */
221
+ onRevalidate?: (target: PrerenderTargetObject, env: TEnv,
222
+ /** The stale request's execution context; absent where none exists. */
223
+ ctx?: ExecutionContext) => void | Promise<void>;
224
+ }
225
+ /**
226
+ * @internal Per-request resolution of the prerender config plus the key
227
+ * version. Threaded onto the request context by the RSC handler and read by
228
+ * the serve-path overlay lookup.
229
+ */
230
+ export interface ResolvedPrerender<TEnv = any> {
231
+ /** The resolved config; the store is `config.store`. */
232
+ config: PrerenderConfig<TEnv>;
233
+ routerId: string;
234
+ /** The router's cache version for overlay keys (resolvePrerenderVersion). */
235
+ version: string;
236
+ }
237
+ /**
238
+ * The runner `router.prerender({ env, ctx })` returns: callable per target,
239
+ * plus `.many` / `.remove` / `.markStale`. Typing rides the phantom `TRoutes`
240
+ * accumulator, like `reverse`.
241
+ */
242
+ export interface PrerenderRunner<TRoutes = {}> {
243
+ (target: PrerenderTarget<TRoutes>, options?: PrerenderRunOptions): Promise<PrerenderResult>;
244
+ many(targets: ReadonlyArray<PrerenderTarget<TRoutes>>, options?: PrerenderManyOptions): Promise<PrerenderResult[]>;
245
+ /**
246
+ * Remove an on-demand route's page without rendering: a "removed" marker is
247
+ * stored in place of it. Requests then get a 404 (a `Passthrough` route: its
248
+ * live handler), never the build-time entry. Permanent: no `ttl`, no
249
+ * `onRevalidate`, left by `onlyIfStale`, until a `prerender(target)`
250
+ * without `onlyIfStale` renders the page again. It is a write like a
251
+ * refresh's, and the last write wins. `{ ok: true, status: "removed" }`; a
252
+ * route that is not on-demand is `skipped-not-on-demand`. `.many` is the
253
+ * batch form.
254
+ */
255
+ remove: {
256
+ (target: PrerenderTarget<TRoutes>, options?: Pick<PrerenderRunOptions, "throwOnError">): Promise<PrerenderResult>;
257
+ many(targets: ReadonlyArray<PrerenderTarget<TRoutes>>, options?: Omit<PrerenderManyOptions, "onlyIfStale">): Promise<PrerenderResult[]>;
258
+ };
259
+ /**
260
+ * Mark this router's stored entries carrying one of `tags` stale. Marking only: the
261
+ * entries keep serving, and a stale hit schedules `onRevalidate` when one is
262
+ * configured. Nothing is deleted or re-rendered here.
263
+ */
264
+ markStale(tags: string[]): Promise<void>;
265
+ }
266
+ /**
267
+ * The `router.prerender` function: binds the runtime (`env`, `ctx`) and returns
268
+ * the {@link PrerenderRunner}. Binding does no work; the config factory is
269
+ * resolved per call.
270
+ */
271
+ export type PrerenderFn<TEnv = any, TRoutes = {}> = (runtime: PrerenderRuntime<TEnv>) => PrerenderRunner<TRoutes>;
272
+ export {};
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Requestless-prerender guard.
3
+ *
4
+ * The on-demand producer renders a route with only route params + env — never
5
+ * the triggering request's cookies, headers, geo, or session (see the design's
6
+ * Producer Semantics). Personalized reads during a producer render would bake
7
+ * one visitor's data into a payload shared with everyone for that route.
8
+ *
9
+ * The producer marks its own request context with `_onDemandProducer: true`. The
10
+ * standalone request-scoped helpers a handler reaches for — `cookies()`,
11
+ * `headers()`, `invalidateClientCache()`, `keepClientCache()` — call
12
+ * {@link assertNotInsidePrerenderProducer}, which throws
13
+ * {@link PrerenderPersonalizationError}; the producer catches it and maps to a
14
+ * `skipped-personalized` result, leaving any existing entry untouched. (The
15
+ * producer context's own `ctx.cookie`/`header`/etc. remain the pre-existing
16
+ * build-time no-op stubs over empty synthetic state — the guard covers the
17
+ * standalone helpers, which are the reads that would otherwise see real request
18
+ * data.)
19
+ */
20
+ /** Thrown when a producer render touches request-specific state. */
21
+ export declare class PrerenderPersonalizationError extends Error {
22
+ /** Cross-realm-safe brand (instanceof can fail across bundle/realm boundaries). */
23
+ readonly isPrerenderPersonalizationError: true;
24
+ constructor(api: string);
25
+ }
26
+ export declare function isPrerenderPersonalizationError(value: unknown): value is PrerenderPersonalizationError;
27
+ /**
28
+ * Throw {@link PrerenderPersonalizationError} when `ctx` is a requestless
29
+ * on-demand producer context. A no-op for the live request path and the
30
+ * build/dev prerender paths (which do not set `_onDemandProducer`), so existing
31
+ * callers are unaffected.
32
+ */
33
+ export declare function assertNotInsidePrerenderProducer(ctx: unknown, api: string): void;
@@ -1,18 +1 @@
1
- /**
2
- * Build shell manifest key, shared by the producer (the build's shell
3
- * prerender phase, vite/discovery/shell-prerender-phase.ts) and the consumer
4
- * (the runtime read-through, rsc/shell-build-manifest.ts) so the format
5
- * cannot drift. Dependency-free: the producer runs node-side in the plugin,
6
- * the consumer in the RSC runtime.
7
- *
8
- * PATHNAME-ONLY by design. Host-free because the build knows no request
9
- * host. Router-free because the router id ($$id) is a hash of
10
- * filePath:lineNumber of the TRANSFORMED source, which differs between the
11
- * discovery temp server's dev-style transform chain and the main build's —
12
- * a temp-realm router id can never be looked up by the shipped worker. The
13
- * producer instead detects pathname collisions across routers at build time
14
- * and declines both entries (loudly), keeping the key unambiguous. This is
15
- * a manifest namespace, never a store keyspace: the runtime shell key
16
- * (host + pathname + search + ":shell") stays untouched.
17
- */
18
- export declare function buildShellManifestKey(pathname: string): string;
1
+ export declare function buildShellManifestKey(routerId: string, pathname: string): string;
@@ -0,0 +1,65 @@
1
+ /**
2
+ * The mark and record of a `router.prerender()` warm request
3
+ * (docs/design/prerender-every-route.md, "The synthetic request"). The
4
+ * trigger marks the `Request` it dispatches, the handler puts the record on
5
+ * the request context (`RequestContext._prerenderWarm`), and each cache layer
6
+ * reads the context where it reads its store. No runtime imports: the cache
7
+ * layers import this without pulling the trigger.
8
+ */
9
+ import type { HandlerCacheConfig } from "../rsc/types.js";
10
+ import type { ShellCaptureDebugEvent } from "../rsc/shell-capture.js";
11
+ import type { PrerenderWarmCaches } from "./on-demand.js";
12
+ /** What one warm request did: written by the layers, read by the trigger. */
13
+ export interface PrerenderWarmRecord extends PrerenderWarmCaches {
14
+ /**
15
+ * `replace`: every runtime cache read misses and the writes replace the
16
+ * entries. `fill` (`onlyIfStale`): reads are normal; the record only reports.
17
+ */
18
+ mode: "replace" | "fill";
19
+ /**
20
+ * The `createRouter({ cache })` config the trigger gated on its scope. The
21
+ * handler uses it instead of resolving the option again, so the store that
22
+ * was checked is the store that is written.
23
+ */
24
+ cacheConfig: HandlerCacheConfig;
25
+ /** The surface an identity guard refused (server/context.ts guardIdentityRead). */
26
+ identity?: string;
27
+ /**
28
+ * The request's render errors: the handler adopts this list as
29
+ * `RequestContext._renderErrors`, so what the layers push lands here.
30
+ */
31
+ renderErrors: unknown[];
32
+ }
33
+ /** What the helpers read; a derived capture context inherits the record. */
34
+ export interface WarmContext {
35
+ _prerenderWarm?: PrerenderWarmRecord;
36
+ _shellCaptureRun?: boolean;
37
+ }
38
+ export declare function createWarmRecord(mode: PrerenderWarmRecord["mode"], cacheConfig: HandlerCacheConfig): PrerenderWarmRecord;
39
+ /** The mark is the object: a clone or a rebuilt request carries none. */
40
+ export declare function markWarmRequest(request: Request, record: PrerenderWarmRecord): void;
41
+ export declare function readWarmMark(request: Request): PrerenderWarmRecord | undefined;
42
+ /**
43
+ * True when this request must read every runtime cache as a miss. A flag read
44
+ * at each read point, not a store wrapper: a route's or loader's own
45
+ * `cache({ store })` bypasses the app store (CacheScope.getStore), and the
46
+ * "use cache" in-flight dedup lives outside the store, so a wrapper would
47
+ * miss both.
48
+ *
49
+ * False in the warm's own shell capture (`_shellCaptureRun`): it runs after
50
+ * the warm's writes settled and must replay them, or it would render every
51
+ * handler a second time and could bake another generation than the one just
52
+ * written.
53
+ */
54
+ export declare function isWarmReplace(ctx: WarmContext | null | undefined): boolean;
55
+ /** Count a store write that landed. No-op outside a warm. */
56
+ export declare function noteWarmWrite(ctx: WarmContext | null | undefined, family: keyof PrerenderWarmCaches["writes"]): void;
57
+ /** Record the document cache's outcome; its store write counts through noteWarmWrite. */
58
+ export declare function noteWarmDocument(ctx: WarmContext | null | undefined, outcome: NonNullable<PrerenderWarmCaches["document"]>): void;
59
+ /** Record the surface an identity guard (guardIdentityRead) is about to refuse. */
60
+ export declare function noteWarmIdentityRead(ctx: WarmContext | null | undefined, surface: string): void;
61
+ /**
62
+ * Fold one capture event into the record: the last attempt's outcome stands.
63
+ * The skips a forced capture never takes and the `backoff` notice leave it.
64
+ */
65
+ export declare function noteWarmShellEvent(record: PrerenderWarmRecord, event: ShellCaptureDebugEvent): void;
@@ -0,0 +1,65 @@
1
+ /**
2
+ * One `router.prerender()` warm request: build the synthetic visitor request,
3
+ * dispatch it through the router's own handler, wait for everything it
4
+ * scheduled, and read back what it wrote
5
+ * (docs/design/prerender-every-route.md, "The synthetic request"). The handler
6
+ * is an injected `fetch`, so unit tests drive this with a fake.
7
+ */
8
+ import { type CollectingExecutionContext } from "../cache/background-task.js";
9
+ import type { HandlerCacheConfig } from "../rsc/types.js";
10
+ import type { ExecutionContext } from "../types/request-scope.js";
11
+ import type { PrerenderResult, PrerenderWarmCaches } from "./on-demand.js";
12
+ import { type PrerenderWarmRecord } from "./warm-request.js";
13
+ /**
14
+ * How long a warm waits for its response body and background work: a capture
15
+ * that waited out the queue budget, then ran to its hard cap.
16
+ */
17
+ export declare const WARM_SETTLE_BUDGET_MS: number;
18
+ export interface WarmRequestInput<TEnv> {
19
+ /** The absolute URL a visitor requests. */
20
+ url: URL;
21
+ mode: PrerenderWarmRecord["mode"];
22
+ /** The gated `createRouter({ cache })` config; the handler writes to its store. */
23
+ cacheConfig: HandlerCacheConfig;
24
+ env: TEnv;
25
+ /** The context `cacheConfig` was resolved with. */
26
+ ctx: CollectingExecutionContext;
27
+ /** `router.fetch`. */
28
+ fetch: (request: Request, input: {
29
+ env: TEnv;
30
+ ctx: ExecutionContext;
31
+ }) => Promise<Response>;
32
+ /** Tests shorten the wait. */
33
+ settleBudgetMs?: number;
34
+ }
35
+ export interface WarmRequestOutcome {
36
+ record: PrerenderWarmRecord;
37
+ /** Absent when the handler threw before answering. */
38
+ responseStatus?: number;
39
+ /** The handler's throw, a body that failed mid-stream, or a wait that ran out. */
40
+ error?: unknown;
41
+ }
42
+ /**
43
+ * Dispatch one warm: a GET with `accept: text/html` and nothing else, so it
44
+ * lands in the partition a header-less visitor lands in. The response is
45
+ * drained and dropped: what it rendered reaches a visitor only through a
46
+ * store.
47
+ */
48
+ export declare function runWarmRequest<TEnv>(input: WarmRequestInput<TEnv>): Promise<WarmRequestOutcome>;
49
+ /** The per-cache detail a result carries. */
50
+ export declare function warmCaches(record: PrerenderWarmRecord): PrerenderWarmCaches;
51
+ /**
52
+ * A warm's status from what it recorded, first match wins. An identity
53
+ * refusal comes ahead of the render failure it usually also causes (the guard
54
+ * throws). `record.shell` is set only for a `ppr` route (shellServePlan).
55
+ */
56
+ export declare function warmStatus(outcome: WarmRequestOutcome): WarmStatus;
57
+ type WarmStatus = Extract<PrerenderResult, {
58
+ ok: true;
59
+ path: "warm";
60
+ }>["status"] | Extract<PrerenderResult, {
61
+ ok: false;
62
+ }>["status"];
63
+ /** The `PrerenderResult` of a finished warm. */
64
+ export declare function composeWarmResult(outcome: WarmRequestOutcome, target: string, routeName: string): PrerenderResult;
65
+ export {};