@rangojs/router 0.21.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 (243) hide show
  1. package/dist/bin/rango.js +13 -1
  2. package/dist/types/browser/event-controller.d.ts +30 -13
  3. package/dist/types/browser/navigation-store.d.ts +9 -0
  4. package/dist/types/browser/partial-update.d.ts +2 -1
  5. package/dist/types/browser/prefetch/default-strategy.d.ts +16 -20
  6. package/dist/types/browser/react/NavigationProvider.d.ts +14 -1
  7. package/dist/types/browser/react/context.d.ts +14 -0
  8. package/dist/types/browser/rsc-router.d.ts +13 -2
  9. package/dist/types/browser/types.d.ts +10 -0
  10. package/dist/types/build/generate-manifest.d.ts +2 -0
  11. package/dist/types/cache/background-task.d.ts +22 -0
  12. package/dist/types/cache/cache-key-utils.d.ts +50 -5
  13. package/dist/types/cache/cache-policy.d.ts +17 -0
  14. package/dist/types/cache/cache-scope.d.ts +1 -1
  15. package/dist/types/cache/cf/cf-cache-store.d.ts +7 -1
  16. package/dist/types/cache/document-cache.d.ts +8 -5
  17. package/dist/types/cache/handle-snapshot.d.ts +23 -0
  18. package/dist/types/cache/index.d.ts +1 -1
  19. package/dist/types/cache/invalidation-order.d.ts +14 -0
  20. package/dist/types/cache/memory-segment-store.d.ts +33 -1
  21. package/dist/types/cache/shell-snapshot.d.ts +10 -1
  22. package/dist/types/cache/store-scope.d.ts +22 -0
  23. package/dist/types/cache/tag-invalidation.d.ts +7 -6
  24. package/dist/types/cache/types.d.ts +63 -14
  25. package/dist/types/cache/vercel/vercel-cache-store.d.ts +7 -1
  26. package/dist/types/errors.d.ts +6 -0
  27. package/dist/types/handles/deferred-resolution.d.ts +7 -0
  28. package/dist/types/internal-suspense-audit.d.ts +1 -0
  29. package/dist/types/loader-store.d.ts +4 -8
  30. package/dist/types/outlet-provider.d.ts +2 -1
  31. package/dist/types/prerender/cloudflare.d.ts +49 -0
  32. package/dist/types/prerender/create-prerender-trigger.d.ts +121 -0
  33. package/dist/types/prerender/index.d.ts +12 -0
  34. package/dist/types/prerender/memory-prerender-store.d.ts +29 -0
  35. package/dist/types/prerender/on-demand.d.ts +272 -0
  36. package/dist/types/prerender/producer-guard.d.ts +33 -0
  37. package/dist/types/prerender/shell-manifest-key.d.ts +1 -18
  38. package/dist/types/prerender/warm-request.d.ts +65 -0
  39. package/dist/types/prerender/warm.d.ts +65 -0
  40. package/dist/types/prerender/writable-store.d.ts +170 -0
  41. package/dist/types/prerender.d.ts +27 -0
  42. package/dist/types/reverse.d.ts +1 -1
  43. package/dist/types/route-content-wrapper.d.ts +3 -1
  44. package/dist/types/router/error-handling.d.ts +2 -0
  45. package/dist/types/router/handler-context.d.ts +1 -1
  46. package/dist/types/router/match-middleware/cache-lookup.d.ts +14 -0
  47. package/dist/types/router/pattern-matching.d.ts +2 -0
  48. package/dist/types/router/prefetch-default.d.ts +4 -4
  49. package/dist/types/router/prerender-match.d.ts +23 -1
  50. package/dist/types/router/route-trie-builder.d.ts +8 -1
  51. package/dist/types/router/router-context.d.ts +1 -0
  52. package/dist/types/router/router-interfaces.d.ts +30 -0
  53. package/dist/types/router/router-options.d.ts +24 -0
  54. package/dist/types/router/segment-resolution/fresh.d.ts +7 -0
  55. package/dist/types/router/segment-resolution/loader-cache.d.ts +2 -1
  56. package/dist/types/router/segment-resolution/revalidation.d.ts +5 -1
  57. package/dist/types/router/segment-resolution/streamed-handler-boundary.d.ts +43 -0
  58. package/dist/types/router/trie-matching.d.ts +2 -0
  59. package/dist/types/rsc/shell-build-manifest.d.ts +6 -4
  60. package/dist/types/rsc/shell-capture-constants.d.ts +20 -5
  61. package/dist/types/rsc/shell-capture.d.ts +14 -1
  62. package/dist/types/rsc/shell-serve.d.ts +2 -2
  63. package/dist/types/rsc/ssr-module-loader.d.ts +15 -0
  64. package/dist/types/rsc/types.d.ts +8 -0
  65. package/dist/types/segment-boundary-content.d.ts +17 -0
  66. package/dist/types/segment-loader-promise.d.ts +2 -6
  67. package/dist/types/server/build-version-table.d.ts +9 -0
  68. package/dist/types/server/context.d.ts +12 -4
  69. package/dist/types/server/handle-store.d.ts +2 -0
  70. package/dist/types/server/request-context.d.ts +63 -2
  71. package/dist/types/suspense-audit-react.d.ts +26 -0
  72. package/dist/types/suspense-audit.d.ts +157 -0
  73. package/dist/types/testing/action-form.d.ts +34 -0
  74. package/dist/types/testing/flight.entry.d.ts +2 -0
  75. package/dist/types/testing/index.d.ts +4 -0
  76. package/dist/types/testing/internal/server-modules.d.ts +8 -0
  77. package/dist/types/testing/serve-shell-request.d.ts +34 -4
  78. package/dist/types/testing/shell-status.d.ts +14 -6
  79. package/dist/types/testing/vitest-stubs/plugin-rsc.d.ts +9 -6
  80. package/dist/types/thenable-status.d.ts +6 -0
  81. package/dist/types/types/handler-context.d.ts +7 -0
  82. package/dist/types/types/route-entry.d.ts +5 -0
  83. package/dist/types/urls/path-helper.d.ts +18 -0
  84. package/dist/types/vite/discovery/state.d.ts +11 -0
  85. package/dist/types/vite/inject-client-debug.d.ts +6 -25
  86. package/dist/types/vite/utils/bundle-analysis.d.ts +6 -2
  87. package/dist/vite/index.js +179 -496
  88. package/package.json +12 -2
  89. package/skills/cache-guide/SKILL.md +18 -17
  90. package/skills/caching/SKILL.md +40 -4
  91. package/skills/catalog.json +1 -1
  92. package/skills/cloudflare/SKILL.md +131 -0
  93. package/skills/comparison/references/framework-comparison.md +1 -1
  94. package/skills/deployment-caching/SKILL.md +33 -12
  95. package/skills/document-cache/SKILL.md +16 -14
  96. package/skills/hooks/SKILL.md +22 -0
  97. package/skills/hooks/handle-and-actions.md +3 -4
  98. package/skills/hooks/navigation.md +10 -0
  99. package/skills/hooks/state.md +5 -4
  100. package/skills/host-router/SKILL.md +18 -0
  101. package/skills/loader/SKILL.md +6 -4
  102. package/skills/middleware/SKILL.md +1 -1
  103. package/skills/migrate-nextjs/SKILL.md +82 -9
  104. package/skills/ppr/SKILL.md +31 -8
  105. package/skills/prerender/SKILL.md +645 -8
  106. package/skills/rango/SKILL.md +1 -0
  107. package/skills/response-routes/SKILL.md +5 -5
  108. package/skills/route/SKILL.md +29 -0
  109. package/skills/testing/SKILL.md +2 -1
  110. package/skills/testing/cache-prerender.md +18 -17
  111. package/skills/testing/serve-shell-request.md +104 -8
  112. package/skills/use-cache/SKILL.md +9 -5
  113. package/skills/vercel/SKILL.md +2 -0
  114. package/skills/view-transitions/SKILL.md +9 -4
  115. package/src/browser/event-controller.ts +69 -25
  116. package/src/browser/link-interceptor.ts +2 -2
  117. package/src/browser/navigation-bridge.ts +75 -35
  118. package/src/browser/navigation-store.ts +26 -0
  119. package/src/browser/navigation-transaction.ts +9 -2
  120. package/src/browser/network-error-handler.ts +2 -0
  121. package/src/browser/partial-update.ts +48 -12
  122. package/src/browser/prefetch/default-strategy.ts +22 -42
  123. package/src/browser/react/Link.tsx +37 -42
  124. package/src/browser/react/NavigationProvider.tsx +63 -3
  125. package/src/browser/react/context.ts +17 -0
  126. package/src/browser/react/location-state.ts +5 -19
  127. package/src/browser/react/use-action.ts +4 -1
  128. package/src/browser/react/use-handle.ts +4 -20
  129. package/src/browser/react/use-link-status.ts +4 -3
  130. package/src/browser/react/use-navigation.ts +4 -1
  131. package/src/browser/react/use-params.ts +4 -1
  132. package/src/browser/react/use-pathname.ts +4 -1
  133. package/src/browser/react/use-search-params.ts +5 -3
  134. package/src/browser/react/use-segments.ts +36 -54
  135. package/src/browser/rsc-router.tsx +79 -39
  136. package/src/browser/segment-reconciler.ts +3 -3
  137. package/src/browser/server-action-bridge.ts +26 -6
  138. package/src/browser/types.ts +11 -0
  139. package/src/build/generate-manifest.ts +17 -0
  140. package/src/build/merge-full-manifests.ts +5 -0
  141. package/src/build/route-trie.ts +1 -0
  142. package/src/cache/background-task.ts +61 -0
  143. package/src/cache/cache-key-utils.ts +91 -12
  144. package/src/cache/cache-policy.ts +11 -2
  145. package/src/cache/cache-runtime.ts +23 -23
  146. package/src/cache/cache-scope.ts +38 -36
  147. package/src/cache/cf/cf-cache-store.ts +16 -11
  148. package/src/cache/document-cache.ts +35 -14
  149. package/src/cache/handle-snapshot.ts +50 -0
  150. package/src/cache/index.ts +1 -0
  151. package/src/cache/invalidation-order.ts +53 -0
  152. package/src/cache/memory-segment-store.ts +52 -1
  153. package/src/cache/shell-snapshot.ts +17 -0
  154. package/src/cache/store-scope.ts +36 -0
  155. package/src/cache/tag-invalidation.ts +7 -5
  156. package/src/cache/types.ts +69 -14
  157. package/src/cache/vercel/vercel-cache-store.ts +20 -6
  158. package/src/client.tsx +40 -3
  159. package/src/errors.ts +13 -6
  160. package/src/handles/deferred-resolution.ts +7 -2
  161. package/src/internal-suspense-audit.ts +12 -0
  162. package/src/loader-store.ts +4 -9
  163. package/src/outlet-provider.tsx +25 -1
  164. package/src/prerender/cloudflare.ts +130 -0
  165. package/src/prerender/create-prerender-trigger.ts +780 -0
  166. package/src/prerender/index.ts +39 -0
  167. package/src/prerender/memory-prerender-store.ts +115 -0
  168. package/src/prerender/on-demand.ts +339 -0
  169. package/src/prerender/producer-guard.ts +66 -0
  170. package/src/prerender/shell-manifest-key.ts +12 -17
  171. package/src/prerender/warm-request.ts +154 -0
  172. package/src/prerender/warm.ts +193 -0
  173. package/src/prerender/writable-store.ts +278 -0
  174. package/src/prerender.ts +29 -0
  175. package/src/reverse.ts +1 -1
  176. package/src/route-content-wrapper.tsx +107 -13
  177. package/src/router/error-handling.ts +12 -12
  178. package/src/router/find-match.ts +1 -0
  179. package/src/router/handler-context.ts +2 -0
  180. package/src/router/intercept-resolution.ts +15 -8
  181. package/src/router/loader-resolution.ts +2 -10
  182. package/src/router/match-middleware/background-revalidation.ts +2 -0
  183. package/src/router/match-middleware/cache-lookup.ts +230 -38
  184. package/src/router/pattern-matching.ts +14 -0
  185. package/src/router/prefetch-default.ts +4 -4
  186. package/src/router/prerender-match.ts +460 -352
  187. package/src/router/route-trie-builder.ts +9 -0
  188. package/src/router/router-context.ts +1 -0
  189. package/src/router/router-interfaces.ts +33 -0
  190. package/src/router/router-options.ts +30 -0
  191. package/src/router/segment-resolution/fresh.ts +20 -19
  192. package/src/router/segment-resolution/loader-cache.ts +15 -17
  193. package/src/router/segment-resolution/revalidation.ts +34 -24
  194. package/src/router/segment-resolution/streamed-handler-boundary.ts +133 -0
  195. package/src/router/trie-matching.ts +3 -0
  196. package/src/router.ts +107 -1
  197. package/src/rsc/handler.ts +51 -4
  198. package/src/rsc/progressive-enhancement.ts +5 -1
  199. package/src/rsc/response-cache-serve.ts +10 -4
  200. package/src/rsc/rsc-rendering.ts +53 -11
  201. package/src/rsc/shell-build-manifest.ts +10 -8
  202. package/src/rsc/shell-capture-constants.ts +39 -7
  203. package/src/rsc/shell-capture.ts +108 -19
  204. package/src/rsc/shell-serve.ts +12 -6
  205. package/src/rsc/ssr-module-loader.ts +26 -0
  206. package/src/rsc/types.ts +8 -0
  207. package/src/segment-boundary-content.ts +25 -0
  208. package/src/segment-loader-promise.ts +28 -38
  209. package/src/segment-system.tsx +117 -57
  210. package/src/server/build-version-table.ts +15 -0
  211. package/src/server/context.ts +40 -17
  212. package/src/server/cookie-store.ts +5 -0
  213. package/src/server/handle-store.ts +13 -0
  214. package/src/server/request-context.ts +87 -1
  215. package/src/ssr/ssr-root.tsx +20 -13
  216. package/src/suspense-audit-react.tsx +98 -0
  217. package/src/suspense-audit.ts +1030 -0
  218. package/src/testing/action-form.ts +66 -0
  219. package/src/testing/flight-runtime.d.ts +27 -0
  220. package/src/testing/flight.entry.ts +3 -0
  221. package/src/testing/index.ts +19 -0
  222. package/src/testing/internal/server-modules.ts +32 -0
  223. package/src/testing/render-route.tsx +25 -12
  224. package/src/testing/serve-shell-request.ts +190 -34
  225. package/src/testing/shell-status.ts +17 -6
  226. package/src/testing/vitest-stubs/plugin-rsc.ts +37 -11
  227. package/src/thenable-status.ts +10 -0
  228. package/src/types/handler-context.ts +7 -0
  229. package/src/types/route-entry.ts +6 -0
  230. package/src/urls/path-helper.ts +69 -9
  231. package/src/use-loader.tsx +57 -30
  232. package/src/vite/discovery/bundle-postprocess.ts +4 -0
  233. package/src/vite/discovery/discover-routers.ts +15 -0
  234. package/src/vite/discovery/shell-prerender-phase.ts +5 -5
  235. package/src/vite/discovery/state.ts +17 -1
  236. package/src/vite/inject-client-debug.ts +42 -20
  237. package/src/vite/plugins/virtual-entries.ts +8 -1
  238. package/src/vite/router-discovery.ts +14 -0
  239. package/src/vite/utils/bundle-analysis.ts +39 -7
  240. package/dist/types/browser/react/subscribe-to-nothing.d.ts +0 -7
  241. package/dist/types/segment-content-promise.d.ts +0 -13
  242. package/src/browser/react/subscribe-to-nothing.ts +0 -7
  243. package/src/segment-content-promise.ts +0 -67
package/dist/bin/rango.js CHANGED
@@ -1571,6 +1571,11 @@ function mergeFullManifests(manifests) {
1571
1571
  manifest.passthroughRoutes,
1572
1572
  overwrittenRoutes
1573
1573
  ),
1574
+ onDemandRoutes: mergeRouteList(
1575
+ merged.onDemandRoutes,
1576
+ manifest.onDemandRoutes,
1577
+ overwrittenRoutes
1578
+ ),
1574
1579
  responseTypeRoutes: mergeRouteRecord(
1575
1580
  merged.responseTypeRoutes,
1576
1581
  manifest.responseTypeRoutes,
@@ -1653,6 +1658,12 @@ async function initializeApp() {
1653
1658
  // is the default; createRouter({ strictMode: false }) ships the opt-out in the
1654
1659
  // payload metadata. StrictMode emits no DOM, so toggling never changes markup.
1655
1660
  const { strictMode, initialPayload } = await initBrowserApp({ rscStream, deps });
1661
+ // Set only on a document a no-JS useActionState POST rendered: React needs
1662
+ // the server's form state to hydrate the hook without resetting it. A state
1663
+ // Flight could not carry rejects: hydrate without it, as before.
1664
+ const formState = await Promise.resolve(initialPayload.formState).catch(
1665
+ () => undefined
1666
+ );
1656
1667
 
1657
1668
  if (import.meta.hot) {
1658
1669
  const { startDevDiscoveryHandshake } = await import(
@@ -1667,7 +1678,8 @@ async function initializeApp() {
1667
1678
  const app = createElement(Rango);
1668
1679
  hydrateRoot(
1669
1680
  document,
1670
- strictMode === false ? app : createElement(StrictMode, null, app)
1681
+ strictMode === false ? app : createElement(StrictMode, null, app),
1682
+ formState ? { formState } : undefined
1671
1683
  );
1672
1684
  }
1673
1685
 
@@ -102,6 +102,15 @@ export interface HandleState {
102
102
  segmentOrder: string[];
103
103
  routeSegmentIds: string[];
104
104
  }
105
+ /**
106
+ * What the document's HTML was rendered with, kept for the hydration window
107
+ * (EventController.lockHydration).
108
+ */
109
+ export interface HydrationSnapshot {
110
+ state: DerivedNavigationState;
111
+ params: Record<string, string>;
112
+ handles: HandleState;
113
+ }
105
114
  /**
106
115
  * Result from starting a navigation
107
116
  * Implements Disposable for use with `using` keyword
@@ -198,22 +207,30 @@ export interface EventController {
198
207
  resolvedIds?: string[]): void;
199
208
  getHandleState(): HandleState;
200
209
  /**
201
- * Keep the current handle state as the one the document's HTML was rendered
202
- * with. initBrowserApp calls it before hydrateRoot, once the document's
203
- * handle snapshot is applied.
210
+ * Open the hydration window. Until releaseHydrationLock(), getHandleState()
211
+ * returns the state the document's HTML was rendered with, and handle writes
212
+ * (setHandleData, setRouteSegmentIds) wait in order. initBrowserApp opens it
213
+ * before hydrateRoot, once the document's handle snapshot is applied: a hook
214
+ * reading the handle state when its boundary hydrates, however late, reads
215
+ * what SSR rendered (issue #1035).
216
+ */
217
+ lockHydration(): void;
218
+ /**
219
+ * While the hydration window is open, the state the document's HTML was
220
+ * rendered with; undefined once it closes. A router hook's first render
221
+ * reads it: a component hydrating in a boundary that streamed in late
222
+ * renders what SSR rendered, even after a navigation or an action started.
223
+ * The hook's effect then catches up with the live state, and its
224
+ * subscription stays live, so pending UI is not held back.
204
225
  */
205
- freezeHydrationHandleState(): void;
226
+ getHydrationSnapshot(): HydrationSnapshot | undefined;
206
227
  /**
207
- * The handle state the document's HTML was rendered with, once the live
208
- * state has moved on from it; undefined while the live state is still that
209
- * state (nothing changed since the freeze, or nothing was frozen: a tree
210
- * that was mounted, an SSR pass). A render React is hydrating reads it
211
- * (useHandle), whenever its boundary hydrates: the late handle channel
212
- * (rsc-router.tsx) waits for the root to hydrate, not for a `<Suspense>`
213
- * boundary that hydrates after it, and by then the live state can hold
214
- * values that boundary's HTML does not (issue #1035).
228
+ * Close the hydration window: apply the writes that waited, in order, and
229
+ * notify handle subscribers. NavigationProvider calls it after the commit
230
+ * that made React hydrate every boundary still dehydrated (its barrier).
231
+ * Idempotent.
215
232
  */
216
- getHydrationHandleState(): HandleState | undefined;
233
+ releaseHydrationLock(): void;
217
234
  /**
218
235
  * Update ONLY `routeSegmentIds` (what `useSegments` reads) from `matched`,
219
236
  * leaving `data` and `segmentOrder` (what `useHandle` collects over) untouched.
@@ -1,4 +1,5 @@
1
1
  import type { NavigationStore, ResolvedSegment, HandleData } from "./types.js";
2
+ import type { EventController } from "./event-controller.js";
2
3
  /**
3
4
  * Clone the handleData CONTAINERS (the handle-name map and each segment map) so
4
5
  * a cache entry is decoupled from the live map that eventController mutates — it
@@ -11,6 +12,14 @@ import type { NavigationStore, ResolvedSegment, HandleData } from "./types.js";
11
12
  * non-serializable bucket contents (React elements, functions, etc.).
12
13
  */
13
14
  export declare function cloneHandleData(handleData: HandleData): HandleData;
15
+ /**
16
+ * Handle data to write into the history cache for the page on screen. During
17
+ * the hydration window getHandleState() is the document snapshot while late
18
+ * pushes sit queued in the controller (they are already in the cache entry,
19
+ * written by rsc-router), so writing the snapshot back would drop them; the
20
+ * entry's own data is the live view then.
21
+ */
22
+ export declare function liveHandleDataForCache(eventController: Pick<EventController, "getHandleState" | "getHydrationSnapshot">, store: Pick<NavigationStore, "getCachedSegments">, historyKey: string): HandleData;
14
23
  /**
15
24
  * Options for generating a history key
16
25
  */
@@ -3,6 +3,7 @@ import type { ReactNode } from "react";
3
3
  import type { RenderSegmentsOptions } from "../segment-system.js";
4
4
  import type { BoundTransaction } from "./navigation-transaction.js";
5
5
  import type { NavigationUpdate } from "./types.js";
6
+ import type { LoaderStore } from "../loader-store.js";
6
7
  import { type TransitionWhenActionInput } from "./transition-when.js";
7
8
  /**
8
9
  * Hold/animate decision for a commit: a non-intercept segment carries a
@@ -18,7 +19,7 @@ export declare function shouldStartViewTransition(segments: readonly ResolvedSeg
18
19
  * directly: nothing is held there, a reader of a pending stream suspends to
19
20
  * its fallback. Also used by renderRoute's navigate() (testing/render-route.tsx).
20
21
  */
21
- export declare function commitInTransition(onUpdate: UpdateSubscriber, segments: readonly ResolvedSegment[], update: NavigationUpdate, transitionTypes: readonly string[]): void;
22
+ export declare function commitInTransition(loaders: LoaderStore, onUpdate: UpdateSubscriber, segments: readonly ResolvedSegment[], update: NavigationUpdate, transitionTypes: readonly string[]): void;
22
23
  /**
23
24
  * Configuration for creating a partial updater
24
25
  */
@@ -2,30 +2,26 @@
2
2
  * Router-wide default prefetch strategy (client seat).
3
3
  *
4
4
  * The server resolves `createRouter({ defaultPrefetch })` once at router init
5
- * (router/prefetch-default.ts) and ships it in initial payload metadata; the
6
- * browser entry applies it here before hydration — same lifecycle as
7
- * `initPrefetchCache` / `setPrefetchConcurrency`. Every `<Link>` without an
8
- * explicit `prefetch` prop and every eligible intercepted plain anchor that has
9
- * not opted out with `data-prefetch="false"` or `data-prefetch="none"` uses it.
10
- * Containers use the same `false`/`none` vocabulary via
11
- * `data-prefetch-scope`.
12
- *
13
- * The module initial value must equal the server resolver's environment default:
14
- * `"none"` in development and `"viewport"` in production. During SSR this
15
- * module is never initialized from metadata, so keeping both seats aligned also
16
- * gives metadata-less payloads the documented behavior.
5
+ * (router/prefetch-default.ts) and ships it in initial payload metadata. The
6
+ * router store carries it to its readers: `<Link>` reads
7
+ * `NavigationStoreContext.defaultPrefetch`, and delegated plain anchors get it
8
+ * from the navigation bridge. Every `<Link>` without an explicit `prefetch`
9
+ * prop and every eligible intercepted plain anchor that has not opted out with
10
+ * `data-prefetch="false"` or `data-prefetch="none"` uses it. Containers use the
11
+ * same `false`/`none` vocabulary via `data-prefetch-scope`.
17
12
  */
18
13
  import type { PrefetchStrategy } from "../../router/prefetch-default.js";
19
- /** Reset module state between tests that replace browser media globals. */
20
- export declare function resetAdaptiveStrategyForTesting(): void;
21
14
  /**
22
- * Apply the server-resolved default strategy. Called once at browser app init
23
- * from payload metadata; also used by tests to reset state.
15
+ * The default for a payload without `defaultPrefetch`: the server resolver's
16
+ * environment default (`"none"` in development, `"viewport"` in production),
17
+ * mirrored without pulling router-layer code into the client bundle. NODE_ENV
18
+ * is folded by the app build.
19
+ */
20
+ export declare const ENVIRONMENT_DEFAULT_PREFETCH: PrefetchStrategy;
21
+ /**
22
+ * Resolve adaptive to the strategy for the current input capability. Called
23
+ * when a prefetch is armed or triggered, never during render.
24
24
  */
25
- export declare function setDefaultPrefetchStrategy(strategy: PrefetchStrategy): void;
26
- /** Current default strategy for Links and delegated plain anchors. */
27
- export declare function getDefaultPrefetchStrategy(): PrefetchStrategy;
28
- /** Resolve adaptive to the strategy for the current input capability. */
29
25
  export declare function resolveAdaptiveStrategy(strategy: PrefetchStrategy): PrefetchStrategy;
30
26
  /** Subscribe to changes in the input capability used by adaptive prefetch. */
31
27
  export declare function subscribeToAdaptiveStrategyChange(listener: () => void): () => void;
@@ -63,6 +63,19 @@ export interface NavigationProviderProps {
63
63
  * it to seed a nonce so components calling useNonce() can be exercised.
64
64
  */
65
65
  nonce?: string;
66
+ /**
67
+ * The document this provider hydrates (initBrowserApp; renderRoute with
68
+ * `hydrate: true`), whose store EventController.lockHydration locked.
69
+ * `settled` resolves once the stream has ended and React has revealed every
70
+ * boundary. Until then location state is `undefined`, as SSR rendered it;
71
+ * then one transition gives the entry's state and changes
72
+ * HydrationBarrierContext, and the store is released after that commit. A
73
+ * navigation committed earlier releases the store before its update.
74
+ * Absent for a provider mounted on the client.
75
+ */
76
+ hydration?: {
77
+ settled: Promise<void>;
78
+ };
66
79
  }
67
80
  /**
68
81
  * Navigation provider component
@@ -85,4 +98,4 @@ export interface NavigationProviderProps {
85
98
  * />
86
99
  * ```
87
100
  */
88
- export declare function NavigationProvider({ store, eventController, initialPayload, bridge, themeConfig, initialTheme, warmupEnabled, version, basename, appShellRef, nonce, }: NavigationProviderProps): ReactNode;
101
+ export declare function NavigationProvider({ store, eventController, initialPayload, bridge, themeConfig, initialTheme, warmupEnabled, version, basename, appShellRef, nonce, hydration, }: NavigationProviderProps): ReactNode;
@@ -48,6 +48,12 @@ export interface NavigationStoreContextValue {
48
48
  basename: string | undefined;
49
49
  /** Router default from this instance's initial payload. */
50
50
  defaultPrefetch?: PrefetchStrategy;
51
+ /**
52
+ * The document's origin, fixed for its lifetime. Link compares absolute
53
+ * hrefs against it (data-external) and useLinkStatus resolves link targets
54
+ * with it.
55
+ */
56
+ origin: string | undefined;
51
57
  }
52
58
  /**
53
59
  * React context for navigation store
@@ -66,3 +72,11 @@ export declare const NavigationStoreContext: Context<NavigationStoreContextValue
66
72
  * (#1029). Undefined on the server and outside a provider.
67
73
  */
68
74
  export declare const LocationStateContext: Context<LocationStateSnapshot>;
75
+ /**
76
+ * Read by no component. NavigationProvider changes its value in a transition so
77
+ * that React hydrates every boundary still dehydrated beneath it before that
78
+ * commit: React hydrates a dehydrated boundary first when a provider above it
79
+ * changes, and nothing re-renders for a value nobody reads. See
80
+ * NavigationProviderProps.hydration.
81
+ */
82
+ export declare const HydrationBarrierContext: Context<number>;
@@ -95,6 +95,12 @@ export interface BrowserAppContext {
95
95
  * prefetch TTL are document-lifetime too (see AppShell).
96
96
  */
97
97
  appShellRef?: import("./app-shell.js").AppShellRef;
98
+ /**
99
+ * Resolves when the document's stream has ended and every Suspense boundary
100
+ * in it has been revealed. NavigationProvider closes the hydration window
101
+ * then (NavigationProviderProps.hydration).
102
+ */
103
+ documentRevealed: Promise<void>;
98
104
  }
99
105
  /**
100
106
  * Initialize the browser app. Must be called before rendering Rango.
@@ -131,13 +137,18 @@ export interface RangoProps {
131
137
  * import * as rscBrowser from "@vitejs/plugin-rsc/browser";
132
138
  *
133
139
  * async function main() {
134
- * await initBrowserApp({ rscStream, deps: rscBrowser });
140
+ * const { initialPayload } = await initBrowserApp({ rscStream, deps: rscBrowser });
141
+ * // Rejects when the action's state is not Flight-serializable.
142
+ * const formState = await Promise.resolve(initialPayload.formState).catch(
143
+ * () => undefined,
144
+ * );
135
145
  *
136
146
  * hydrateRoot(
137
147
  * document,
138
148
  * <React.StrictMode>
139
149
  * <Rango />
140
- * </React.StrictMode>
150
+ * </React.StrictMode>,
151
+ * formState ? { formState } : undefined
141
152
  * );
142
153
  * }
143
154
  * main();
@@ -1,4 +1,5 @@
1
1
  import type { ReactNode, ComponentType } from "react";
2
+ import type { ReactFormState } from "react-dom/client";
2
3
  import type { ResolvedSegment, SlotState } from "../types.js";
3
4
  import type { ResolvedThemeConfig, Theme } from "../theme/types.js";
4
5
  import type { RenderSegmentsOptions } from "../segment-system.js";
@@ -9,6 +10,11 @@ import type { RenderSegmentsOptions } from "../segment-system.js";
9
10
  export interface RscPayload<TMetadata = RscMetadata> {
10
11
  metadata?: TMetadata;
11
12
  returnValue?: ActionResult;
13
+ /**
14
+ * Form state of a PE POST document, for hydrateRoot's `formState` option.
15
+ * Rejects when the action's state is not Flight-serializable.
16
+ */
17
+ formState?: Promise<ReactFormState>;
12
18
  }
13
19
  /**
14
20
  * Handle data structure: handleName -> segmentId -> entries[]
@@ -406,6 +412,8 @@ export interface NavigateOptionsInternal extends Omit<NavigateOptions, "state">
406
412
  state?: unknown;
407
413
  /** Skip segment cache (used by redirect-with-state to force re-render) */
408
414
  _skipCache?: boolean;
415
+ /** Server redirects already followed by this navigation chain (0 for a user navigation) */
416
+ _redirectHops?: number;
409
417
  }
410
418
  /**
411
419
  * Options for useRouter push/replace methods.
@@ -476,6 +484,8 @@ export type StateListener = () => void;
476
484
  * EventController owns the public navigation lifecycle exposed by hooks.
477
485
  */
478
486
  export interface NavigationStore {
487
+ /** Client loader data for useLoader / useFetchLoader (loader-store.ts). */
488
+ readonly loaders: import("../loader-store.js").LoaderStore;
479
489
  getSegmentState(): SegmentState;
480
490
  setPath(path: string): void;
481
491
  setCurrentUrl(url: string): void;
@@ -37,6 +37,8 @@ export interface GeneratedManifest {
37
37
  prerenderRoutes?: string[];
38
38
  /** Route names wrapped with Passthrough() (live handler for runtime fallback) */
39
39
  passthroughRoutes?: string[];
40
+ /** Route names opted into on-demand prerender (Prerender(..., { onDemand })) */
41
+ onDemandRoutes?: string[];
40
42
  /** Route name → response type for non-RSC routes */
41
43
  responseTypeRoutes?: Record<string, string>;
42
44
  /** Route name -> search schema descriptor for typed URL helpers */
@@ -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;
@@ -356,7 +356,7 @@ export declare class CacheScope {
356
356
  * enclosing scopes on another store (#974) and of a scope without its own
357
357
  * `key()` (#970)); with none, the app store's keyGenerator result
358
358
  * given the document default key. Undefined (synchronously, with no work)
359
- * when neither applies: the shell key stays host + path + filtered search.
359
+ * when neither applies: the shell key stays unpartitioned (buildShellKey).
360
360
  * Resolves to null when the partition is the default key itself (a
361
361
  * keyGenerator that returns it unchanged): that partitions nothing, so the
362
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, };
@@ -43,6 +43,12 @@ export declare class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<T
43
43
  private readonly kv?;
44
44
  /** True when constructed without KV: no durable tag history (see ctor). */
45
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;
46
52
  private readonly onRevalidateTag?;
47
53
  private readonly tagPurge?;
48
54
  private readonly tagInvalidationTtl?;
@@ -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
  /**
@@ -44,6 +44,18 @@ 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
+ /**
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
+ };
47
59
  /**
48
60
  * What the record being restored is to each loader's handle pushes on this
49
61
  * request (HandleStore RecordAuthority). Built by loaderPins (loader-cache.ts)
@@ -65,6 +77,17 @@ export type OwnedPushDelivery = (loaderId: string) => RecordAuthority;
65
77
  * pushes in a placeholder's place).
66
78
  */
67
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>;
68
91
  /**
69
92
  * Append recorded handle pushes to the store, in recorded order, via push()
70
93
  * so active captures and loader-scope tagging see them like live pushes. For
@@ -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,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;