@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
@@ -6,6 +6,7 @@
6
6
  * settleGrowing). When waitUntil is unavailable, falls back to blocking or
7
7
  * skipping.
8
8
  */
9
+ import type { ExecutionContext } from "../types/request-scope.js";
9
10
  interface WaitUntilHost {
10
11
  waitUntil?: (fn: () => Promise<void>) => void;
11
12
  }
@@ -40,4 +41,25 @@ export declare function raceDeadline<T>(work: Promise<T>, deadline: number): Pro
40
41
  * mutates `list`: a later call waits for what is still pending.
41
42
  */
42
43
  export declare function settleGrowing(list: readonly Promise<unknown>[], deadline: number): Promise<boolean>;
44
+ /** Macrotask turns `awaitLaterMillisecond` waits at most. */
45
+ export declare const LATER_MILLISECOND_MAX_TURNS = 10;
46
+ /**
47
+ * Yield until `Date.now()` passes `since`, so a store's whole-millisecond tag
48
+ * gate (putShell vs an invalidation made just before) sees a later
49
+ * millisecond. Bounded: on Cloudflare the clock stands still between I/O
50
+ * events, so an unbounded wait could hang. At the bound the caller proceeds
51
+ * and a store may refuse the write, which its result reports.
52
+ */
53
+ export declare function awaitLaterMillisecond(since: number, maxTurns?: number): Promise<void>;
54
+ /** An ExecutionContext that keeps every promise handed to `waitUntil`. */
55
+ export interface CollectingExecutionContext extends ExecutionContext {
56
+ readonly tasks: Promise<unknown>[];
57
+ }
58
+ /**
59
+ * Wrap the caller's ExecutionContext (or none) so a caller can wait for the
60
+ * request's background work: the deferred cache writes, the shell capture, a
61
+ * store's own writes. Every other member reads through to `inner`, bound to
62
+ * it (workerd's methods reject a foreign `this`).
63
+ */
64
+ export declare function createCollectingExecutionContext(inner?: ExecutionContext): CollectingExecutionContext;
43
65
  export {};
@@ -6,6 +6,7 @@
6
6
  * Used by cache-runtime, cache-scope, document-cache, and loader-cache.
7
7
  */
8
8
  import type { SearchParamsFilter } from "./search-params-filter.js";
9
+ export declare function isReservedSearchParam(key: string): boolean;
9
10
  /**
10
11
  * Build a sorted, deterministic query string from URLSearchParams,
11
12
  * excluding the router's reserved params (see isReservedSearchParam).
@@ -27,11 +28,40 @@ export declare function sortedSearchString(searchParams: URLSearchParams, filter
27
28
  */
28
29
  export declare function sortedRouteParams(params: Record<string, string> | undefined): string;
29
30
  /**
30
- * Host-namespaced cache key base: `${host}${pathname}[:params][?search]`.
31
+ * THE ROUTER RULE (#1065). A key that names one router's output for a host
32
+ * and path starts with the router part, `{encodeURIComponent(routerId)}@`. A
33
+ * key that names a function and its arguments, or a tag, does not.
31
34
  *
32
- * The ONE composition of the host-namespacing rule, shared by the segment tier
33
- * (cache-scope.ts) and the document tier (document-cache.ts) so the rule cannot
34
- * drift between them. Host prefixing matters because VercelCacheStore /
35
+ * - With the part: PPR shells and the build-shell manifest (buildShellKey,
36
+ * buildShellManifestKey); route cache() records, the document cache and
37
+ * response routes (cacheKeyBase); a loader's own cache() entry; a
38
+ * `"use cache"` entry of a call that takes a ctx (a ctx is one router's:
39
+ * its variables, env and reverse() map).
40
+ * - Without: a `"use cache"` entry of plain arguments or a bare Request
41
+ * (requestArgKey); tag markers; a key the app builds itself
42
+ * (`cache({ key })`, a `keyGenerator`).
43
+ *
44
+ * Why: a host and path do not name a router. A `hostOverride` cookie picks
45
+ * the router and forwards the request unmodified, and a `router.prerender()`
46
+ * warm requests whatever origin it resolved, so two routers on one store
47
+ * built one key and read each other's entry.
48
+ *
49
+ * The id (router.ts) has to be the same in every isolate and in build-time
50
+ * discovery: an explicit `id` and the injected `$$id` are, the `router_{n}`
51
+ * fallback is for a single router only (discover-routers.ts warns about
52
+ * more). Encoded, the id holds no `@` or `/`, and a host holds no `@`, so a
53
+ * key has a router part exactly when its first `@` comes before its first
54
+ * `/`: no id can be chosen to spell another router's key.
55
+ */
56
+ export declare function routerKeyPrefix(routerId: string): string;
57
+ /**
58
+ * Key base of one router's output (the router rule, routerKeyPrefix):
59
+ * `${routerId}@${host}${pathname}[:params][?search]`.
60
+ *
61
+ * The ONE composition of the namespacing rule, shared by the segment tier
62
+ * (cache-scope.ts), the document tier (document-cache.ts), the response tier
63
+ * (rsc/response-cache-serve.ts) and the loader cache so the rule cannot drift
64
+ * between them. Host prefixing matters because VercelCacheStore /
35
65
  * MemorySegmentCacheStore key by the raw string (only CFCacheStore adds host
36
66
  * internally) -- on a single function serving multiple domains an
37
67
  * un-namespaced key bleeds tenant A's cached response to tenant B.
@@ -40,7 +70,22 @@ export declare function sortedRouteParams(params: Record<string, string> | undef
40
70
  * invalidates every persisted cache entry on upgrade. Callers append their own
41
71
  * tier-specific suffixes (`:rsc`/`:html`, segment hash) after this base.
42
72
  */
43
- export declare function cacheKeyBase(host: string, pathname: string, searchParams?: URLSearchParams, params?: Record<string, string>, filter?: SearchParamsFilter): string;
73
+ export declare function cacheKeyBase(routerId: string, host: string, pathname: string, searchParams?: URLSearchParams, params?: Record<string, string>, filter?: SearchParamsFilter): string;
74
+ /**
75
+ * cacheKeyBase for the request context `ctx`. rsc/handler.ts sets `_routerId`
76
+ * before the request scope opens and derived contexts inherit it, so a served
77
+ * request always has one. Only a hand-built context has none (a unit test,
78
+ * the runLoader / renderHandler primitives): its key has no router part,
79
+ * which no router's key equals (routerKeyPrefix).
80
+ */
81
+ export declare function requestKeyBase(ctx: {
82
+ readonly _routerId?: string;
83
+ } | null | undefined, host: string, pathname: string, searchParams?: URLSearchParams, params?: Record<string, string>, filter?: SearchParamsFilter): string;
84
+ /**
85
+ * The `"use cache"` key part of a bare Request argument: its URL, with no
86
+ * router part (the router rule, routerKeyPrefix).
87
+ */
88
+ export declare function requestArgKey(url: URL, filter?: SearchParamsFilter): string;
44
89
  /**
45
90
  * The prefix of a `key()` result's part in a record key (#975), and of a
46
91
  * loader's own `key()` result after its `loader:<id>:` namespace (#1009,
@@ -7,6 +7,14 @@
7
7
  */
8
8
  import type { CacheDefaults, SegmentCacheStore } from "./types.js";
9
9
  import type { RequestContext } from "../server/request-context.js";
10
+ /**
11
+ * How long a follower trusts an in-flight leader before evicting it and
12
+ * running fresh. High enough that a slow-but-healthy upstream never triggers
13
+ * duplicate work (a legitimate cached call taking >15s is already pathological);
14
+ * low enough to bound the blast radius of a wedged leader to seconds, not the
15
+ * isolate lifetime. Aligned with SHELL_CAPTURE_MAX_WAIT_MS.
16
+ */
17
+ export declare const IN_FLIGHT_LEADER_MAX_WAIT_MS = 15000;
10
18
  /**
11
19
  * Default TTL for route-level cache() DSL and loader cache.
12
20
  * Applied when neither the cache options nor the store defaults specify a TTL.
@@ -73,4 +81,13 @@ export declare function computeExpiration(ttlSeconds: number, swrSeconds?: numbe
73
81
  };
74
82
  export declare function resolveCacheKey(keyFn: ((ctx: RequestContext) => string | Promise<string>) | undefined, store: SegmentCacheStore | null, defaultKey: string): Promise<string>;
75
83
  export declare function resolveTagsOption<TEnv>(tags: string[] | ((ctx: RequestContext<TEnv>) => string[]) | undefined, ctx: RequestContext<TEnv> | undefined, label: string): string[] | undefined;
84
+ /**
85
+ * Normalize a resolved tags array so the WRITE path matches the invalidate path:
86
+ * updateTag()/revalidateTag()/cacheTag() all drop empty/whitespace-only tags via
87
+ * normalizeTag(). Without this, an empty tag attached at write time would enter
88
+ * the store index but could never be invalidated (the verbs normalize it away),
89
+ * and on CFCacheStore would also cost a wasted KV marker read per request.
90
+ * Returns undefined when nothing usable remains, keeping the entry header-free.
91
+ */
92
+ export declare function normalizeTagList(tags: string[]): string[] | undefined;
76
93
  export declare function resolveCacheStore(explicitStore: SegmentCacheStore | undefined): SegmentCacheStore | null;
@@ -286,11 +286,10 @@ export declare class CacheScope {
286
286
  * @param pathname - URL pathname for cache key generation
287
287
  * @param params - Route params for cache key generation
288
288
  * @param isIntercept - Whether this is an intercept navigation (uses different cache key)
289
- * @param ownedPushes - How restoreHandles delivers the record's
290
- * loader-owned values (withCacheLookup: the handler context's
291
- * _claimLoaderPushes, and on a document HIT tail the owners it restores)
289
+ * @param ownedPushes - What the record is to each loader's handle pushes
290
+ * (withCacheLookup: loader-cache.ts loaderPins), called on a hit
292
291
  */
293
- lookupRoute(pathname: string, params: Record<string, string>, isIntercept?: boolean, ownedPushes?: OwnedPushDelivery): Promise<{
292
+ lookupRoute(pathname: string, params: Record<string, string>, isIntercept?: boolean, ownedPushes?: () => OwnedPushDelivery): Promise<{
294
293
  segments: ResolvedSegment[];
295
294
  shouldRevalidate: boolean;
296
295
  } | null>;
@@ -312,7 +311,7 @@ export declare class CacheScope {
312
311
  * A corrupt entry that was evicted reads as `miss`: the store was consulted
313
312
  * and holds nothing servable, which is the post-eviction truth.
314
313
  */
315
- lookupRouteDetailed(pathname: string, params: Record<string, string>, isIntercept?: boolean, ownedPushes?: OwnedPushDelivery): Promise<CacheRouteLookupOutcome>;
314
+ lookupRouteDetailed(pathname: string, params: Record<string, string>, isIntercept?: boolean, ownedPushes?: () => OwnedPushDelivery): Promise<CacheRouteLookupOutcome>;
316
315
  /**
317
316
  * Record this scope's segment-DSL cache({ tags }) into the request tag union
318
317
  * synchronously, under the same gate cacheRoute() uses for a write.
@@ -357,7 +356,7 @@ export declare class CacheScope {
357
356
  * enclosing scopes on another store (#974) and of a scope without its own
358
357
  * `key()` (#970)); with none, the app store's keyGenerator result
359
358
  * given the document default key. Undefined (synchronously, with no work)
360
- * when neither applies: the shell key stays host + path + filtered search.
359
+ * when neither applies: the shell key stays unpartitioned (buildShellKey).
361
360
  * Resolves to null when the partition is the default key itself (a
362
361
  * keyGenerator that returns it unchanged): that partitions nothing, so the
363
362
  * shell stays unpartitioned and keeps its build shell. The shell's key, its
@@ -3,7 +3,7 @@ declare global {
3
3
  readonly default: Cache;
4
4
  }
5
5
  }
6
- import type { SegmentCacheStore, CachedEntryData, CacheDefaults, CacheGetResult, CacheItemResult, CacheItemOptions, ShellCacheEntry, ShellDocumentRead, ShellDocumentReadOptions, CacheReadError } from "../types.js";
6
+ import type { CacheStoreScope, SegmentCacheStore, CachedEntryData, CacheDefaults, CacheGetResult, CacheItemResult, CacheItemOptions, ShellCacheEntry, ShellDocumentRead, ShellDocumentReadOptions, CacheReadError } from "../types.js";
7
7
  import { type RequestContext } from "../../server/request-context.js";
8
8
  import { CACHE_STALE_AT_HEADER, CACHE_STATUS_HEADER, CACHE_TAGS_HEADER, CACHE_TAGGED_AT_HEADER, TAG_MARKER_PREFIX, CACHE_REVALIDATING_AT_HEADER, MAX_REVALIDATION_INTERVAL, EDGE_LOOKUP_TIMEOUT_MS, EDGE_READ_TIMEOUT_MS, KV_READ_TIMEOUT_MS } from "./cf-cache-constants.js";
9
9
  export { CACHE_STALE_AT_HEADER, CACHE_STATUS_HEADER, CACHE_TAGS_HEADER, CACHE_TAGGED_AT_HEADER, TAG_MARKER_PREFIX, CACHE_REVALIDATING_AT_HEADER, MAX_REVALIDATION_INTERVAL, EDGE_LOOKUP_TIMEOUT_MS, EDGE_READ_TIMEOUT_MS, KV_READ_TIMEOUT_MS, };
@@ -21,7 +21,8 @@ export declare class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<T
21
21
  private readonly namespace?;
22
22
  private readonly explicitBaseUrl?;
23
23
  private readonly waitUntil?;
24
- private readonly version?;
24
+ /** The `version` option: used for every versioned family when set. */
25
+ private readonly explicitVersion?;
25
26
  private readonly edgeLookupTimeoutMs;
26
27
  private readonly edgeReadTimeoutMs;
27
28
  private readonly kvReadTimeoutMs;
@@ -42,6 +43,12 @@ export declare class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<T
42
43
  private readonly kv?;
43
44
  /** True when constructed without KV: no durable tag history (see ctor). */
44
45
  readonly tagHistoryInert?: boolean;
46
+ /**
47
+ * SegmentCacheStore.scope: with KV every colo's L1 miss reads the same L2
48
+ * entry ("global"); without it entries live in one colo's Cache API
49
+ * ("local"), purge mode included (a purge evicts, it stores nothing).
50
+ */
51
+ readonly scope: CacheStoreScope;
45
52
  private readonly onRevalidateTag?;
46
53
  private readonly tagPurge?;
47
54
  private readonly tagInvalidationTtl?;
@@ -327,9 +334,12 @@ export declare class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<T
327
334
  * on the same context) read their markers as before.
328
335
  */
329
336
  private recordShellHit;
330
- /** The isolate marker memo's key for a tag: markers are per namespace and version. */
337
+ /**
338
+ * The isolate marker memo's key for a tag: markers are per namespace, and
339
+ * shared by every version (tagMarkerKey).
340
+ */
331
341
  private markerMemoKey;
332
- /** The memo key: one per namespace, build version, base URL, and shell key. */
342
+ /** The memo key: one per namespace, document version, base URL, and shell key. */
333
343
  private shellMemoKey;
334
344
  /**
335
345
  * Serve a memoized shell (shell-memo.ts). The tag-marker check is the only
@@ -400,9 +410,16 @@ export declare class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<T
400
410
  private kvReadShellDocument;
401
411
  /** Promote a valid KV shell into the per-colo Cache API tier. */
402
412
  private promoteShellToL1;
413
+ private shellRequest;
414
+ private shellKVKey;
403
415
  /**
404
416
  * Convert string key to Request object for CF Cache API.
405
- * Includes version in URL if specified (for cache invalidation on code changes).
417
+ *
418
+ * `family` picks the version in the URL path, so a version change misses:
419
+ * `"data"` for segment entries and `fn:` items (cached RSC data, valid while
420
+ * the router's server code is unchanged), `"document"` for `doc:` responses
421
+ * and `shell2:` shells (their bytes carry asset URLs and the handler
422
+ * version), `null` for tag markers, which apply to every version.
406
423
  * @internal
407
424
  */
408
425
  private keyToRequest;
@@ -477,7 +494,13 @@ export declare class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<T
477
494
  * @internal
478
495
  */
479
496
  private kvGetOrEvict;
480
- /** KV key for a tag's invalidation marker. */
497
+ /**
498
+ * KV key for a tag's invalidation marker. Unversioned: a content-hash
499
+ * version can come back (deploy A, deploy B, roll back to A), and a marker
500
+ * written while B was live has to be visible to A's entries. The staleness
501
+ * check compares the marker time with the entry's taggedAt and nothing else
502
+ * (isGloballyInvalidated), so one marker per tag serves every version.
503
+ */
481
504
  private tagMarkerKey;
482
505
  /**
483
506
  * Header entries carrying an entry's tags (JSON-encoded, comma-safe) and the
@@ -554,7 +577,11 @@ export declare class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<T
554
577
  * @internal
555
578
  */
556
579
  private isL1Invalidated;
557
- /** Synthetic Cache API request for a tag's L1-cached invalidation marker. */
580
+ /**
581
+ * Synthetic Cache API request for a tag's L1-cached invalidation marker.
582
+ * Unversioned for the same reason as tagMarkerKey; the cache namespace
583
+ * (getCache) still scopes it.
584
+ */
558
585
  private tagMarkerRequest;
559
586
  /**
560
587
  * Read a tag's latest invalidation timestamp (or null if never invalidated)
@@ -276,11 +276,19 @@ export interface CFCacheStoreOptions<TEnv = unknown> {
276
276
  */
277
277
  tagCacheTtl?: number;
278
278
  /**
279
- * Cache version string override. When this changes, all cached entries are
280
- * effectively invalidated (new keys won't match old entries).
279
+ * Cache version string override. When set, this exact value prefixes every
280
+ * versioned key the store writes (`v/{version}/...`), for cached data and
281
+ * stored HTML alike; changing it makes every earlier entry unreachable.
281
282
  *
282
- * Defaults to the auto-generated VERSION from the `@rangojs/router:version` virtual module.
283
- * Only set this if you need a custom versioning strategy.
283
+ * Leave it unset for the default: the store keys with the versions of the
284
+ * router serving the request. Segment entries and `"use cache"` items use
285
+ * the router's data version; document responses and PPR shells use its
286
+ * document version (see `createRouter({ version })`). Those are computed
287
+ * from the router's built code, so a deploy that does not change the router
288
+ * keeps its entries.
289
+ *
290
+ * Tag invalidation markers are never versioned: an invalidation applies to
291
+ * the entries of every version, including one that comes back in a rollback.
284
292
  */
285
293
  version?: string;
286
294
  /**
@@ -31,11 +31,14 @@ export interface DocumentCacheOptions<TEnv = any> {
31
31
  /**
32
32
  * Custom cache key generator.
33
33
  *
34
- * Replaces the default `host + pathname + search` key entirely. On a
35
- * multi-domain deployment served by one function you MUST include `url.host`
36
- * (or an equivalent tenant discriminator) yourself — the default key is
37
- * host-namespaced, but a custom generator's output is used verbatim, so
38
- * omitting host bleeds one hostname's cached response to another.
34
+ * Replaces the default `router id + host + pathname + search` key entirely.
35
+ * On a multi-domain deployment served by one function you MUST include
36
+ * `url.host` (or an equivalent tenant discriminator) yourself — the default
37
+ * key is host-namespaced, but a custom generator's output is used verbatim,
38
+ * so omitting host bleeds one hostname's cached response to another. The
39
+ * same goes for the router: two routers that share a cache store and can
40
+ * serve one host and path (a `hostOverride` cookie) need a generator that
41
+ * tells them apart.
39
42
  */
40
43
  keyGenerator?: (url: URL) => string;
41
44
  /**
@@ -6,7 +6,7 @@
6
6
  * during segment resolution and stored alongside cached segments.
7
7
  */
8
8
  import type { ResolvedSegment } from "../types.js";
9
- import type { HandleStore } from "../server/handle-store.js";
9
+ import type { HandleStore, RecordAuthority } from "../server/handle-store.js";
10
10
  import type { HandleOwners, SegmentHandleData } from "./types.js";
11
11
  type HandleRecord = Record<string, SegmentHandleData>;
12
12
  /**
@@ -44,54 +44,50 @@ export declare function decodeHandleValue<T>(encoded: string): Promise<T | null>
44
44
  export declare function captureHandles(segments: ResolvedSegment[], handleStore: HandleStore): Record<string, SegmentHandleData>;
45
45
  /** The owners of captureHandles' values; undefined when none is owned. */
46
46
  export declare function captureHandleOwners(segments: ResolvedSegment[], handleStore: HandleStore): HandleOwners | undefined;
47
- /** How restoreHandles delivers a record's loader-owned values. */
48
- export interface OwnedPushDelivery {
49
- /**
50
- * Asked once per owner delivered through pushReplayed
51
- * (setupLoaderAccess _claimLoaderPushes); an owner it refuses is skipped.
52
- */
53
- claim?: (loaderId: string) => boolean;
54
- /**
55
- * A PPR shell's record (capture, document HIT tail, navigation replay): the
56
- * loaders the route runs on the live lane (liveLaneLoaderIds), marked on
57
- * the store (HandleStore.markLiveLane). A hole's values are replayed
58
- * unclaimed, so its own cache() HIT redelivers its entry's pushes in their
59
- * place (HandleStore.redeliverReplays), and its run ending without a push
60
- * drops them (HandleStore.settleLoaderRun).
61
- */
62
- liveLane?: ReadonlySet<string>;
63
- /**
64
- * A document HIT tail: every owner outside `liveLane` goes through
65
- * pushRestored and stands, unclaimed.
66
- */
67
- restore?: boolean;
68
- }
47
+ /**
48
+ * captureHandles and captureHandleOwners cut down to the handle arrays that
49
+ * hold an owned value, each whole (its unowned values too, in place), so
50
+ * restoreHandles rebuilds exactly those arrays over a store that already
51
+ * holds the rest. A prerender-served shell capture stores this: the
52
+ * prerender store restores the handler pushes on a HIT, and the loader
53
+ * pushes have no other record (shell-capture.ts captureAndStoreShell).
54
+ */
55
+ export declare function captureOwnedHandles(handleStore: HandleStore): {
56
+ handles: HandleRecord;
57
+ owners?: HandleOwners;
58
+ };
59
+ /**
60
+ * What the record being restored is to each loader's handle pushes on this
61
+ * request (HandleStore RecordAuthority). Built by loaderPins (loader-cache.ts)
62
+ * over the pins resolveLoaderData serves a loader's value from, so a value
63
+ * and its record copies get one answer.
64
+ */
65
+ export type OwnedPushDelivery = (loaderId: string) => RecordAuthority;
69
66
  /**
70
67
  * Restore handle data from a cached snapshot into the handle store.
71
68
  * Used when serving cached segments to replay their handle data: a route
72
69
  * cache() record owns its segments' arrays, so it REPLACES them.
73
70
  *
74
71
  * A segment with `owners` is replaced with empty arrays and re-pushed in
75
- * recorded order, an owned value through pushReplayed: if the owning loader
76
- * runs (a client navigation replaying a ppr route's cache() record runs its
77
- * `ssr: false` loaders), its live pushes replace the recorded ones in place
78
- * instead of appending a second copy. `owned.claim` is asked once per
79
- * owner, so the loader's own cache() HIT does not replay its pushes a second
80
- * time (loader-cache.ts replayLoaderHandles). A record without owners
81
- * restores as a plain replay.
82
- *
83
- * `owned.restore`: a document HIT tail restoring the shell's doc record
84
- * (withCacheLookup). A bake-lane owner's values are the prelude's, so they go
85
- * through pushRestored and stand: a run of that loader on the HIT (a
86
- * promise-carrying `ssr: false` loader, one whose record asks for a run)
87
- * reads the store, and its settled pushes are dropped, not swapped in.
88
- * Nothing is claimed, so the loader's own cache() HIT still replays the
89
- * pushes the record could not keep (the thenable ones pushRestored lets
90
- * through). A loader the route also runs on the live lane is a hole: its
91
- * values keep pushReplayed, unclaimed, and its live run replaces them
92
- * (#936), even inside a restored loader's body (`owned.liveLane`).
72
+ * recorded order. A loader-owned value stands (pushRestored) where `owned`
73
+ * says the record supplies that loader's value too ("pin", "copies"), and is
74
+ * a placeholder (pushPlaceholder) everywhere else: without `owned`, every
75
+ * owner. Neither claims its loader, so the loader's own cache() HIT still
76
+ * delivers (the thenable pushes a restored copy lets through, or its entry's
77
+ * pushes in a placeholder's place).
93
78
  */
94
79
  export declare function restoreHandles(handles: Record<string, SegmentHandleData>, handleStore: HandleStore, owners?: HandleOwners, owned?: OwnedPushDelivery): void;
80
+ /**
81
+ * Serve a doc record's handles: set its authority over the loaders' pushes
82
+ * (with or without a handles blob: a pinned loader whose capture pushed
83
+ * nothing has no copy, and its run must still add no settled push), then
84
+ * decode and restoreHandles. `keepIds` limits the restore to the segments a
85
+ * boundary-scoped scope serves. A decode failure skips the restore.
86
+ */
87
+ export declare function restoreRecordHandles(handleStore: HandleStore, record: {
88
+ handles?: string;
89
+ handleOwners?: HandleOwners;
90
+ }, owned: OwnedPushDelivery | undefined, keepIds?: ReadonlySet<string>): Promise<void>;
95
91
  /**
96
92
  * Append recorded handle pushes to the store, in recorded order, via push()
97
93
  * so active captures and loader-scope tagging see them like live pushes. For
@@ -99,14 +95,27 @@ export declare function restoreHandles(handles: Record<string, SegmentHandleData
99
95
  * restoreHandles would wipe those. `segmentId` redirects every value to one
100
96
  * segment (the caller's).
101
97
  *
102
- * Owner-keyed groups (useCacheRecordKey, `${seq}:${loaderId}`) are a loader's
103
- * pushes, which reach the page once per request: `claim` (setupLoaderAccess
104
- * _claimLoaderPushes) skips a loader that already ran or was replayed in this
105
- * request, and a claimed loader that runs later replaces its replayed values
106
- * (HandleStore.pushReplayed). `${seq}:` groups are the function's own pushes.
107
- * A key without ":" is a segment id (records written before owner keys) and
108
- * replays unchanged. A loader's own cache() replays through loader-cache.ts
109
- * replayLoaderHandles.
98
+ * The replay of every cached unit that records by owner: a "use cache"
99
+ * function (useCacheRecordKey) and a loader's own cache() (loader-cache.ts
100
+ * recordOwnerKey, with `unitLoader` the cached loader).
101
+ *
102
+ * A `${seq}:${loaderId}` group is that loader's pushes, which reach the page
103
+ * once per request: `claim` (setupLoaderAccess _claimLoaderPushes) is asked
104
+ * once per loader and skips one that already ran or was replayed in this
105
+ * request; a claimed loader that runs later replaces its replayed values
106
+ * (HandleStore.pushReplayed). A group without an owner (`${seq}:`, or a key
107
+ * without ":" from a record written before owner keys, which is a segment
108
+ * id) is the unit's own: `unitLoader`'s, or no loader's for a function,
109
+ * whose pushes are plain.
110
+ *
111
+ * The claimed loaders' placeholders give way to this delivery
112
+ * (HandleStore.replacePlaceholders), `unitLoader`'s even when the entry
113
+ * recorded no push for it: the entry stands for that loader's run.
114
+ *
115
+ * No `claim` means the caller's pushes are diverted off the page (a stale
116
+ * refresh: cache-runtime.ts refreshView, setupLoaderAccess
117
+ * _runLoaderIsolated): every group is delivered, into the diverting capture,
118
+ * and the page's placeholders stay, or the page would lose the push.
110
119
  */
111
- export declare function appendHandles(handles: Record<string, SegmentHandleData>, handleStore: HandleStore, segmentId?: string, claim?: (loaderId: string) => boolean): void;
120
+ export declare function appendHandles(handles: Record<string, SegmentHandleData>, handleStore: HandleStore, segmentId: string | undefined, claim: ((loaderId: string) => boolean) | undefined, unitLoader?: string): void;
112
121
  export {};
@@ -1,4 +1,4 @@
1
- export type { SegmentCacheStore, CachedEntryData, CacheGetResult, CacheItemResult, CacheItemOptions, ShellCacheEntry, SerializedSegmentData, SegmentHandleData, CacheReadError, } from "./types.js";
1
+ export type { SegmentCacheStore, CacheStoreScope, CachedEntryData, CacheGetResult, CacheItemResult, CacheItemOptions, ShellCacheEntry, SerializedSegmentData, SegmentHandleData, CacheReadError, } from "./types.js";
2
2
  export { CACHE_READ_ERROR } from "./types.js";
3
3
  export { MemorySegmentCacheStore } from "./memory-segment-store.js";
4
4
  export type { StoreMemoOptions } from "./shell-memo.js";
@@ -8,3 +8,5 @@ export { TRACKING_SEARCH_PARAMS, type CacheSearchParams, } from "./search-params
8
8
  export { CacheScope, createCacheScope } from "./cache-scope.js";
9
9
  export { createDocumentCacheMiddleware, type DocumentCacheOptions, } from "./document-cache.js";
10
10
  export type { CacheErrorCategory } from "./cache-error.js";
11
+ export { getCacheVersions } from "../server/build-version-table.js";
12
+ export type { RouterVersions } from "../router-versions.js";
@@ -8,6 +8,20 @@ export interface ExecutionStart {
8
8
  readonly seq: number;
9
9
  readonly at: number;
10
10
  }
11
+ /**
12
+ * The `taggedAt` / `ta` stamp a store writes on a tagged data entry: the start
13
+ * of the execution that produced the value (`startedAt`), so a marker written
14
+ * while it ran rejects the entry on read (#1068). Write time when the writer
15
+ * knows no start. Never later than now: a start from a skewed clock must not
16
+ * stamp the future, which would make the entry look newer than every marker.
17
+ */
18
+ export declare function entryStamp(startedAt: number | undefined): number;
19
+ /** The stamp of a data entry: `entryStamp` for a tagged entry, none otherwise. */
20
+ export declare function taggedStamp(tags: readonly string[] | undefined, startedAt: number | undefined): number | undefined;
21
+ /** Mark the execution start behind a Response handed to putResponse. */
22
+ export declare function markResponseStart(response: Response, start: ExecutionStart): Response;
23
+ /** The start a writer marked on `response`, if any. */
24
+ export declare function responseStartedAt(response: Response): number | undefined;
11
25
  /** Record an execution's start, before it reads its data. */
12
26
  export declare function executionStart(): ExecutionStart;
13
27
  /**
@@ -4,7 +4,7 @@
4
4
  * Simple in-memory implementation of SegmentCacheStore.
5
5
  * Uses globalThis to survive HMR in development.
6
6
  */
7
- import type { SegmentCacheStore, CachedEntryData, CacheDefaults, CacheGetResult, CacheItemResult, CacheItemOptions, ShellCacheEntry } from "./types.js";
7
+ import type { CacheStoreScope, SegmentCacheStore, CachedEntryData, CacheDefaults, CacheGetResult, CacheItemResult, CacheItemOptions, ShellCacheEntry } from "./types.js";
8
8
  import type { RequestContext } from "../server/request-context.js";
9
9
  /**
10
10
  * Options for MemorySegmentCacheStore
@@ -70,6 +70,25 @@ export interface MemorySegmentCacheStoreOptions<TEnv = unknown> {
70
70
  * ```
71
71
  */
72
72
  maxEntries?: number;
73
+ /**
74
+ * Where an entry this store writes can be read; what `router.prerender()`
75
+ * checks before warming a route. Defaults to `"local"`: the maps live in one
76
+ * process, and the router cannot tell one long-running server from several
77
+ * replicas or serverless instances, so a warm into it is refused in
78
+ * production. Set `"global"` only when exactly one process serves the app
79
+ * (a single Node server); a warm then reports `warmed`. Under the Vite dev
80
+ * server a `"local"` instance already counts as shared.
81
+ *
82
+ * Like `defaults` and `keyGenerator`, the scope is per instance: two
83
+ * instances with the same `name` share their maps but each declares its own
84
+ * scope. Keep them identical.
85
+ *
86
+ * @example
87
+ * ```typescript
88
+ * const store = new MemorySegmentCacheStore({ scope: "global" });
89
+ * ```
90
+ */
91
+ scope?: CacheStoreScope;
73
92
  }
74
93
  /**
75
94
  * In-memory segment cache store.
@@ -106,6 +125,13 @@ export interface MemorySegmentCacheStoreOptions<TEnv = unknown> {
106
125
  * ```
107
126
  */
108
127
  export declare class MemorySegmentCacheStore<TEnv = unknown> implements SegmentCacheStore<TEnv> {
128
+ /**
129
+ * SegmentCacheStore.scope: "local" unless `options.scope` says otherwise, so
130
+ * router.prerender() refuses to warm into these maps in production. Under
131
+ * the Vite dev server the gate counts a "local" instance as shared
132
+ * (store-scope.ts resolveWarmStoreScope).
133
+ */
134
+ readonly scope: CacheStoreScope;
109
135
  readonly supportsPassiveShellReads: true;
110
136
  private cache;
111
137
  private responseCache;
@@ -151,6 +177,12 @@ export declare class MemorySegmentCacheStore<TEnv = unknown> implements SegmentC
151
177
  } | null>;
152
178
  putShell(key: string, entry: ShellCacheEntry, ttlSeconds?: number, swrSeconds?: number, tags?: string[]): Promise<"stored" | "invalidated">;
153
179
  isTagsInvalidatedSince(tags: string[], sinceMs: number): Promise<boolean>;
180
+ /**
181
+ * A tag invalidated in a millisecond after the write's execution started
182
+ * (#1068). Strictly after: the start's own millisecond is ambiguous, and
183
+ * invalidation-order.ts orders those exactly.
184
+ */
185
+ private invalidatedAfter;
154
186
  private tagsInvalidatedSince;
155
187
  invalidateTags(tags: string[]): Promise<void>;
156
188
  private registerTags;
@@ -18,9 +18,11 @@
18
18
  * the capture's copy of a value: it is dynamic or has its own cache. A
19
19
  * bake-lane loader body that runs on a HIT reads the store too; its pinned
20
20
  * container paths stay the pin's (overlayLoaderContainer), and its settled
21
- * pushes stay the record's (HandleStore.pushRestored).
21
+ * pushes stay the record's (HandleStore.pushRestored). The two go together:
22
+ * a loader whose pin the entry lost runs fresh, and the record's copies of
23
+ * its pushes give way to that run's (restoreHandles).
22
24
  */
23
- import type { SegmentCacheStore, CacheGetResult, CacheItemResult, CacheItemOptions, CachedEntryData, ShellCacheEntry, ShellSnapshotRecord, CacheReadError } from "./types.js";
25
+ import type { SegmentCacheStore, CacheGetResult, CacheItemResult, CacheItemOptions, CachedEntryData, ShellCacheEntry, ShellSnapshotRecord, ShellSnapshotHandlesValue, CacheReadError } from "./types.js";
24
26
  /**
25
27
  * A store wrapper the CAPTURE render reads through. Every call passes through
26
28
  * to the underlying store unchanged; the one thing it records is the doc
@@ -172,22 +174,45 @@ export interface ShellLoaderSeedEntry {
172
174
  * hole-free record: a hole-carrying one always runs.
173
175
  */
174
176
  runs: boolean;
177
+ /**
178
+ * The capture that wrote the pin recorded every settled push of the
179
+ * loader's run in the doc record (CachedEntryData.handleOwners): the pin
180
+ * carries the `runs` bit, which captures write since they record those
181
+ * pushes (v0.18). A replay then drops any other settled push of the loader
182
+ * (HandleStore RecordAuthority "pin"). False for an older pin, whose record
183
+ * may hold none and leaves them to the run.
184
+ */
185
+ complete: boolean;
175
186
  }
176
187
  /**
177
188
  * Materialize the loader-family seed from a shell snapshot for a HIT's tail
178
189
  * render: Flight-deserialize each recorded (promise-elided) bake-lane
179
- * container into a segment-key -> container Map, which serveShellHit assigns
180
- * to the tail context's `_shellLoaderSeed` for the resolveLoaderData overlay.
190
+ * container into a loader-id -> pin Map, which serveShellHit assigns to the
191
+ * tail context's `_shellLoaderSeed` (loader-cache.ts servedPins).
181
192
  * Lives here so every snapshot family is decoded in this module (the segment
182
193
  * family via {@link SeededShellStore}); the loader family is not a store
183
194
  * read, so it seeds the context instead of a store.
184
195
  *
196
+ * Keyed by loader, not by the segment a pin was stored under: a loader runs
197
+ * once per capture, so every segment that registers it pinned the same
198
+ * container, and the loader's value and its handle pushes are both looked up
199
+ * by loader. A key that does not parse pins nothing.
200
+ *
185
201
  * Deserializations run in parallel; a record that fails to decode is skipped
186
202
  * (that loader drifts — the pre-snapshot behavior — instead of failing the
187
203
  * HIT). Returns undefined when the snapshot carries no loader records, without
188
204
  * touching the Flight codec (kept lazy for cold paths and non-RSC configs).
189
205
  */
190
206
  export declare function buildShellLoaderSeed(snapshot: ShellSnapshotRecord[]): Promise<Map<string, ShellLoaderSeedEntry> | undefined>;
207
+ /** The key of a snapshot's one `handles` record (ShellSnapshotHandlesValue). */
208
+ export declare const SHELL_HANDLES_RECORD_KEY = "handles";
209
+ /**
210
+ * The `handles` record of a prerender-served entry, which a HIT tail restores
211
+ * after the prerender store's handles (cache-lookup.ts yieldFromStore), or
212
+ * undefined: an entry with a doc record, or one written before the record
213
+ * existed (v0.21), which serves as it did.
214
+ */
215
+ export declare function shellPrerenderHandles(snapshot: readonly ShellSnapshotRecord[]): ShellSnapshotHandlesValue | undefined;
191
216
  /**
192
217
  * The implicit doc scope's store on a HIT tail and on a partial replay: it
193
218
  * serves the snapshot's segment records, the doc record among them, and
@@ -0,0 +1,22 @@
1
+ /**
2
+ * The store scope `router.prerender()` gates a warm on
3
+ * (prerender/create-prerender-trigger.ts). See
4
+ * docs/design/prerender-every-route.md, "The store declaration".
5
+ */
6
+ import type { CacheStoreScope, SegmentCacheStore } from "./types.js";
7
+ /**
8
+ * True under the Vite dev server: only its routes-manifest virtual module sets
9
+ * the dev origin (vite/discovery/virtual-module-codegen.ts). A build, a preview
10
+ * server and a test runner answer false.
11
+ */
12
+ export declare function isViteDevServer(): boolean;
13
+ /**
14
+ * The scope a warm treats `store` as having: its own declaration, with none
15
+ * or an unknown value being `"local"`.
16
+ *
17
+ * One exception: under the Vite dev server a MemorySegmentCacheStore answers
18
+ * `"global"`, because that one process serves every request. Production reads
19
+ * the class's own `scope` (`"local"`): changing that declaration is what
20
+ * would admit it there.
21
+ */
22
+ export declare function resolveWarmStoreScope(store: SegmentCacheStore<any>, devServer?: boolean): CacheStoreScope;