@rangojs/router 0.21.0 → 0.22.1

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 (244) 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 +180 -497
  88. package/package.json +13 -3
  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/loader-registry.ts +6 -1
  215. package/src/server/request-context.ts +87 -1
  216. package/src/ssr/ssr-root.tsx +20 -13
  217. package/src/suspense-audit-react.tsx +98 -0
  218. package/src/suspense-audit.ts +1030 -0
  219. package/src/testing/action-form.ts +66 -0
  220. package/src/testing/flight-runtime.d.ts +27 -0
  221. package/src/testing/flight.entry.ts +3 -0
  222. package/src/testing/index.ts +19 -0
  223. package/src/testing/internal/server-modules.ts +32 -0
  224. package/src/testing/render-route.tsx +25 -12
  225. package/src/testing/serve-shell-request.ts +190 -34
  226. package/src/testing/shell-status.ts +17 -6
  227. package/src/testing/vitest-stubs/plugin-rsc.ts +37 -11
  228. package/src/thenable-status.ts +10 -0
  229. package/src/types/handler-context.ts +7 -0
  230. package/src/types/route-entry.ts +6 -0
  231. package/src/urls/path-helper.ts +69 -9
  232. package/src/use-loader.tsx +57 -30
  233. package/src/vite/discovery/bundle-postprocess.ts +4 -0
  234. package/src/vite/discovery/discover-routers.ts +15 -0
  235. package/src/vite/discovery/shell-prerender-phase.ts +5 -5
  236. package/src/vite/discovery/state.ts +17 -1
  237. package/src/vite/inject-client-debug.ts +42 -20
  238. package/src/vite/plugins/virtual-entries.ts +8 -1
  239. package/src/vite/router-discovery.ts +14 -0
  240. package/src/vite/utils/bundle-analysis.ts +39 -7
  241. package/dist/types/browser/react/subscribe-to-nothing.d.ts +0 -7
  242. package/dist/types/segment-content-promise.d.ts +0 -13
  243. package/src/browser/react/subscribe-to-nothing.ts +0 -7
  244. package/src/segment-content-promise.ts +0 -67
@@ -22,7 +22,7 @@
22
22
  * a loader whose pin the entry lost runs fresh, and the record's copies of
23
23
  * its pushes give way to that run's (restoreHandles).
24
24
  */
25
- 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";
26
26
  /**
27
27
  * A store wrapper the CAPTURE render reads through. Every call passes through
28
28
  * to the underlying store unchanged; the one thing it records is the doc
@@ -204,6 +204,15 @@ export interface ShellLoaderSeedEntry {
204
204
  * touching the Flight codec (kept lazy for cold paths and non-RSC configs).
205
205
  */
206
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;
207
216
  /**
208
217
  * The implicit doc scope's store on a HIT tail and on a partial replay: it
209
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;
@@ -37,16 +37,17 @@
37
37
  */
38
38
  import type { SegmentCacheStore } from "./types.js";
39
39
  import { type ExecutionStart } from "./invalidation-order.js";
40
- export { executionStart, invalidatedSince, type ExecutionStart, } from "./invalidation-order.js";
40
+ export { executionStart, invalidatedSince, markResponseStart, type ExecutionStart, } from "./invalidation-order.js";
41
41
  /**
42
42
  * The write gate of every cache execution (#977): whether one of `tags` was
43
43
  * invalidated after the execution that produced the value started.
44
44
  *
45
- * Every built-in store stamps an entry when it is written (CFCacheStore's
46
- * `taggedAt`, VercelCacheStore's `ta`) or checks nothing on write
47
- * (MemorySegmentCacheStore), so a value read before an invalidation and
48
- * written after it would be served as newer than the invalidation until it
49
- * expires. Two answers, either one skips the write:
45
+ * A store stamps an entry with the execution's start (CFCacheStore's
46
+ * `taggedAt`, VercelCacheStore's `ta`, #1068), but only CFCacheStore with KV
47
+ * markers rejects it on a later request's read, and MemorySegmentCacheStore
48
+ * refuses the write. The gate saves the write everywhere and is the only
49
+ * guard where a read compares nothing. Two answers, either one skips the
50
+ * write:
50
51
  *
51
52
  * - this isolate's order (invalidatedSince), which sees every request's
52
53
  * updateTag()/revalidateTag() here, the calling one included;
@@ -36,6 +36,17 @@ export interface CacheGetResult {
36
36
  */
37
37
  shouldRevalidate: boolean;
38
38
  }
39
+ /**
40
+ * Where an entry a store writes during one request can be read by a later
41
+ * request. `router.prerender()` warms a route only when the app store answers
42
+ * `"global"` or `"regional"`: a warm fills the store where it runs, which
43
+ * serves other traffic only when the store is shared beyond that place.
44
+ * - `"global"`: every location reads the same entries.
45
+ * - `"regional"`: each region has its own copy; a warm fills the region it
46
+ * runs in.
47
+ * - `"local"`: only the process, isolate or edge location that wrote it.
48
+ */
49
+ export type CacheStoreScope = "global" | "regional" | "local";
39
50
  /**
40
51
  * Low-level segment cache store interface.
41
52
  *
@@ -53,6 +64,11 @@ export interface CacheGetResult {
53
64
  * @typeParam TEnv - Platform bindings type (e.g., Cloudflare env)
54
65
  */
55
66
  export interface SegmentCacheStore<TEnv = unknown> {
67
+ /**
68
+ * Where an entry this store writes can be read ({@link CacheStoreScope}).
69
+ * Absent: unknown to the router, treated as `"local"`.
70
+ */
71
+ readonly scope?: CacheStoreScope;
56
72
  /**
57
73
  * The store honors getShell(..., { claimRevalidation: false }) without
58
74
  * claiming an SWR lock. Navigation replay requires this opt-in because it
@@ -147,6 +163,11 @@ export interface SegmentCacheStore<TEnv = unknown> {
147
163
  * @param ttl - Time-to-live in seconds
148
164
  * @param swr - Optional stale-while-revalidate window in seconds
149
165
  * @param tags - Optional cache tags for invalidation
166
+ *
167
+ * The built-in stores read the start of the render from the `Response`
168
+ * object itself (#1068). A custom store, or a wrapper that rebuilds the
169
+ * `Response` before delegating, stamps write time. The write gate
170
+ * (`predatesInvalidation`) runs before this call either way.
150
171
  */
151
172
  putResponse?(key: string, response: Response, ttl: number, swr?: number, tags?: string[]): Promise<void>;
152
173
  /**
@@ -397,9 +418,10 @@ export interface ShellCacheEntry {
397
418
  * bake-lane loader pins (pruned to what a HIT reads, issue #941). Replaying
398
419
  * them on a HIT keeps the freshly rendered hydration payload equal to the
399
420
  * frozen prelude after the underlying cache entries have drifted; every
400
- * other read on a HIT, a hole's included, reads the store. Empty when the capture
401
- * recorded nothing (a prerender-served capture without bake-lane loaders,
402
- * a tombstone). A custom store returns it as putShell received it. See
421
+ * other read on a HIT, a hole's included, reads the store. A
422
+ * prerender-served capture holds a `handles` record instead of a doc record
423
+ * (ShellSnapshotHandlesValue). Empty for a tombstone. A custom store
424
+ * returns it as putShell received it. See
403
425
  * docs/design/ppr-shell-resume.md ("the capture data snapshot").
404
426
  */
405
427
  snapshot: ShellSnapshotRecord[];
@@ -560,12 +582,22 @@ export interface ShellReadStats {
560
582
  }
561
583
  /**
562
584
  * The families a shell snapshot pins: the doc segment record (recorded by
563
- * RecordingShellStore) and the settled CONTAINER of each bake-lane loader
564
- * (lane rule: see resolveLoaderData, loader-cache.ts). No cache read is
565
- * pinned: a HIT's holes, and a bake-lane loader body that runs on the HIT,
566
- * read the store.
585
+ * RecordingShellStore), the settled CONTAINER of each bake-lane loader
586
+ * (lane rule: see resolveLoaderData, loader-cache.ts), and a prerender-served
587
+ * entry's `handles` record. No cache read is pinned: a HIT's holes, and a bake-lane
588
+ * loader body that runs on the HIT, read the store.
567
589
  */
568
- export type ShellSnapshotFamily = "segment" | "loader";
590
+ export type ShellSnapshotFamily = "segment" | "loader" | "handles";
591
+ /**
592
+ * The stored form of the `handles` family: the handle arrays of a
593
+ * prerender-served capture that hold a loader-owned push, in the doc record's
594
+ * format (CachedEntryData `handles` + `handleOwners`). A Prerender + ppr
595
+ * route's handler layer comes from the prerender store, whose build-time
596
+ * entry has no loader push, so the entry keeps these for the HIT to restore
597
+ * as a doc record's (issue #1057, yieldFromStore in cache-lookup.ts). An
598
+ * entry written before it (v0.21) has none and serves as it did.
599
+ */
600
+ export type ShellSnapshotHandlesValue = Pick<CachedEntryData, "handles" | "handleOwners">;
569
601
  /**
570
602
  * The stored form of a loader-family snapshot value: the bake-lane loader's
571
603
  * settled container, Flight-serialized AFTER eliding every still-pending nested
@@ -598,17 +630,18 @@ export interface ShellSnapshotLoaderValue {
598
630
  runs: 0 | 1;
599
631
  }
600
632
  /**
601
- * One snapshot record: the doc segment record or a bake-lane loader pin.
602
- * `value` carries it in its stored/serialized shape so it round-trips
603
- * through a JSON-serializing store (KV, CF, Vercel) with the rest of the
604
- * ShellCacheEntry:
633
+ * One snapshot record: the doc segment record, a bake-lane loader pin, or a
634
+ * prerender-served entry's handle record. `value` carries it in its
635
+ * stored/serialized shape so it round-trips through a JSON-serializing store
636
+ * (KV, CF, Vercel) with the rest of the ShellCacheEntry:
605
637
  * - `segment` -> {@link CachedEntryData} (already JSON-able)
606
638
  * - `loader` -> {@link ShellSnapshotLoaderValue}
639
+ * - `handles` -> {@link ShellSnapshotHandlesValue}
607
640
  */
608
641
  export interface ShellSnapshotRecord {
609
642
  family: ShellSnapshotFamily;
610
643
  key: string;
611
- value: CachedEntryData | ShellSnapshotLoaderValue;
644
+ value: CachedEntryData | ShellSnapshotLoaderValue | ShellSnapshotHandlesValue;
612
645
  }
613
646
  /**
614
647
  * Options for setItem() for function-level caching ("use cache").
@@ -622,6 +655,17 @@ export interface CacheItemOptions {
622
655
  swr?: number;
623
656
  /** Cache tags for invalidation */
624
657
  tags?: string[];
658
+ /**
659
+ * Ms since epoch when the execution that produced the value started (#1068).
660
+ * A store stamps a tagged entry with it instead of its write time. Later
661
+ * requests then reject an entry an invalidation landed under in
662
+ * CFCacheStore with KV markers; MemorySegmentCacheStore refuses the write.
663
+ * Not rejected: VercelCacheStore (a read compares the stamp with the
664
+ * invalidating request's mask only) and CFCacheStore L1 hits in purge mode
665
+ * or without KV. Absent or not positive: write time. A custom store may
666
+ * ignore it.
667
+ */
668
+ startedAt?: number;
625
669
  }
626
670
  /**
627
671
  * Serialized segment data stored in cache
@@ -664,7 +708,12 @@ export interface CachedEntryData {
664
708
  expiresAt: number;
665
709
  /** Cache tags for invalidation */
666
710
  tags?: string[];
667
- /** Timestamp (ms since epoch) when tags were attached, for distributed invalidation */
711
+ /**
712
+ * Timestamp (ms since epoch) the entry's tags are valid as of, for
713
+ * distributed invalidation. The writer sets it to the start of the render
714
+ * that produced the record; a store stamps write time when it is absent
715
+ * (#1068). Which reads reject on it: see `CacheItemOptions.startedAt`.
716
+ */
668
717
  taggedAt?: number;
669
718
  }
670
719
  /**
@@ -33,7 +33,7 @@
33
33
  * `getCache(...)` result, which satisfies the shape structurally. That keeps the
34
34
  * router free of a hard Vercel dependency.
35
35
  */
36
- import type { SegmentCacheStore, CachedEntryData, CacheDefaults, CacheGetResult, CacheItemResult, CacheItemOptions, ShellCacheEntry, ShellDocumentRead, ShellDocumentReadOptions, CacheReadError } from "../types.js";
36
+ import type { CacheStoreScope, SegmentCacheStore, CachedEntryData, CacheDefaults, CacheGetResult, CacheItemResult, CacheItemOptions, ShellCacheEntry, ShellDocumentRead, ShellDocumentReadOptions, CacheReadError } from "../types.js";
37
37
  import { type RequestContext } from "../../server/request-context.js";
38
38
  import { type StoreMemoOptions } from "../shell-memo.js";
39
39
  /**
@@ -227,6 +227,12 @@ export interface VercelCacheStoreOptions<TEnv = unknown> {
227
227
  * ```
228
228
  */
229
229
  export declare class VercelCacheStore<TEnv = unknown> implements SegmentCacheStore<TEnv> {
230
+ /**
231
+ * SegmentCacheStore.scope: the Runtime Cache is per region. A
232
+ * router.prerender() warm fills the region it runs in, which is all traffic
233
+ * only when the project's functions run in one region.
234
+ */
235
+ readonly scope: CacheStoreScope;
230
236
  readonly supportsPassiveShellReads: true;
231
237
  readonly defaults?: CacheDefaults;
232
238
  readonly keyGenerator?: (ctx: RequestContext<TEnv>, defaultKey: string) => string | Promise<string>;
@@ -223,5 +223,11 @@ export declare function invariant(condition: any, message: string): asserts cond
223
223
  * @param error - Error to sanitize
224
224
  * @returns Response suitable for sending to client
225
225
  */
226
+ /**
227
+ * Vite replaces import.meta.env.DEV at compile time. The fallback covers
228
+ * non-Vite environments (plain Node, test runners without Vite transforms).
229
+ * SECURITY: fail closed — default to production when the environment is ambiguous.
230
+ */
231
+ export declare function isDevEnvironment(): boolean;
226
232
  export declare function sanitizeError(error: unknown): Response;
227
233
  export {};
@@ -51,6 +51,13 @@ export declare function resolveDeferredHandleValues(data: HandleData): Promise<H
51
51
  * one holding a masked promise), not this function's.
52
52
  */
53
53
  export declare function resolvedHandleStream(handleStore: HandleStore, recordedOnly?: boolean): AsyncGenerator<HandleData, void, unknown>;
54
+ /**
55
+ * `snapshot` without the pushes a record leaves out. A shell capture renders
56
+ * (resolvedHandleStream) and waits (settleCaptureRecord) on this cut only: a
57
+ * promise the record drops has no slot in the prelude, and waiting for it
58
+ * could refuse a capture for a value the shell does not hold.
59
+ */
60
+ export declare function recordedHandleData(handleStore: HandleStore, snapshot: HandleData): HandleData;
54
61
  /**
55
62
  * Resolve the deferred (Promise) values in ONE segment's handle bucket
56
63
  * (`handleName -> entries[]`). Same shallow + drop-rejections rule as
@@ -0,0 +1 @@
1
+ export declare const INTERNAL_RANGO_SUSPENSE_AUDIT: boolean;
@@ -29,9 +29,10 @@
29
29
  * reclaim a bucket that is about to be reused, and is held until any
30
30
  * in-flight load settles.
31
31
  *
32
- * The store is intentionally module-level: each browser tab is its own JS
33
- * realm, so there is no cross-request pollution. Server renders never mutate
34
- * the store — the hook falls back to `OutletContext.loaderData`.
32
+ * Each router's NavigationStore owns one (navigation-store.ts), and hooks reach
33
+ * it through NavigationStoreContext: the router store is the single source of
34
+ * truth. Server renders have no store — the hook falls back to
35
+ * `OutletContext.loaderData`.
35
36
  */
36
37
  /** The loader-segment shape announcePendingStreams reads (ResolvedSegment). */
37
38
  export interface PendingStreamSource {
@@ -237,9 +238,4 @@ export declare class LoaderStore {
237
238
  */
238
239
  reset(): void;
239
240
  }
240
- /**
241
- * Module-level singleton. Each browser tab gets its own; SSR never mutates it.
242
- * The hook falls through to `OutletContext.loaderData` during the server render.
243
- */
244
- export declare const loaderStore: LoaderStore;
245
241
  export declare const EMPTY_LOADER_SNAPSHOT: LoaderEntry;
@@ -1,4 +1,4 @@
1
- import { type ReactNode } from "react";
1
+ import { type ComponentProps, type ReactNode } from "react";
2
2
  import type { ResolvedSegment } from "./types.js";
3
3
  /**
4
4
  * Outlet content provider — stores parent context for useLoader chain walking.
@@ -13,3 +13,4 @@ export declare function OutletProvider({ content, parallel, segment, loaderData,
13
13
  pending?: boolean;
14
14
  children: ReactNode;
15
15
  }): ReactNode;
16
+ export declare function AuditedOutletProvider({ children, ...props }: ComponentProps<typeof OutletProvider>): ReactNode;
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Cloudflare KV writable prerender store (`@rangojs/router/prerender/cloudflare`).
3
+ *
4
+ * Backs the durable overlay for on-demand prerender on Cloudflare. Self-contained
5
+ * (no CFCacheStore construction constraints): entries are stored under the
6
+ * versioned key `prerender:{routerId}:{version}:{routeName}:{paramHash}`, and
7
+ * tag invalidation uses the same timestamp-marker technique as the runtime cache
8
+ * — a per-tag KV marker holding the ms epoch of the last invalidation; a read
9
+ * treats the entry as stale (never deleted) when any of its tags' markers is at
10
+ * or after the entry's storedAt.
11
+ *
12
+ * Key design constraints (see docs/design/ondemand-prerender.md, Store Model):
13
+ * - staleAt is SOFT metadata; entries are written with NO KV expirationTtl. Hard
14
+ * expiry would delete the entry SWR needs and re-expose older bundled-manifest
15
+ * content below the overlay.
16
+ * - Prerender keys are route + params + variant only — never host-namespaced, so
17
+ * a refresh from a different host than the serve request still hits.
18
+ * - Invalidation is mark-stale, not delete (deleting would re-expose the manifest
19
+ * entry as the result of an "invalidation").
20
+ * - Tag markers use a prerender-specific prefix, separate from the runtime cache's
21
+ * `updateTag()`/`revalidateTag()` namespace, and are scoped by router id
22
+ * (`{prefix}{encodeURIComponent(routerId)}/{tag}`): routers sharing one KV
23
+ * namespace (an app behind a host router) must not mark each other's entries.
24
+ * - Markers carry no expiry: an entry without a `ttl` never goes stale by
25
+ * itself, so an expired marker would silently drop an invalidation.
26
+ */
27
+ import type { KVNamespace } from "../cache/cf/cf-cache-types.js";
28
+ import { type WritablePrerenderStore } from "./writable-store.js";
29
+ export interface KVPrerenderStoreOptions {
30
+ /** Injectable clock for `markStale` markers in deterministic tests. Defaults to Date.now. */
31
+ now?: () => number;
32
+ }
33
+ /**
34
+ * Create a {@link WritablePrerenderStore} backed by a Cloudflare KV namespace.
35
+ *
36
+ * @example
37
+ * ```ts
38
+ * import { createKVPrerenderStore } from "@rangojs/router/prerender/cloudflare";
39
+ *
40
+ * createRouter<Env>({
41
+ * prerender: (env) => ({
42
+ * store: createKVPrerenderStore(env.PRERENDER_KV),
43
+ * ttl: 3600,
44
+ * onRevalidate: (target, e) => e.PRERENDER_QUEUE.send({ target }),
45
+ * }),
46
+ * });
47
+ * ```
48
+ */
49
+ export declare function createKVPrerenderStore(kv: KVNamespace, options?: KVPrerenderStoreOptions): WritablePrerenderStore;
@@ -0,0 +1,121 @@
1
+ /**
2
+ * `router.prerender()` trigger factory.
3
+ *
4
+ * Builds the `router.prerender` binder: `(runtime) => runner`, the runner being
5
+ * the per-target callable (plus `.many` / `.remove` / `.markStale`) from router-supplied
6
+ * deps. Pure and testable: all router internals (reverse, match, the producer,
7
+ * the request handler) arrive as injected functions, so this module has no RSC
8
+ * imports and can be unit-tested with fakes.
9
+ *
10
+ * One verb, dispatched by route after the match:
11
+ * - A `Prerender(..., { onDemand })` route takes the requestless render into
12
+ * the prerender store (docs/design/ondemand-prerender.md), then, when the
13
+ * app cache store is shared and an origin resolves, a warm request that
14
+ * rebuilds the runtime caches on top of the new entry.
15
+ * - Every other route is warmed: a cookie-free GET through the router's own
16
+ * handler in which every runtime cache read misses and every write replaces
17
+ * the entry (docs/design/prerender-every-route.md).
18
+ *
19
+ * Contract highlights:
20
+ * - Requestless: the producer render never inherits the caller's request state.
21
+ * - A refresh always renders and replaces; `onlyIfStale` is the cron-sweep
22
+ * opt-in and the only path that returns `already-fresh`.
23
+ * - A failed render/store keeps the previous entry (replace-on-success).
24
+ * - A `notFound()` in the render is not a failure: it stores a "removed"
25
+ * marker in place of the page, stamped with the route's ttl and tags so it
26
+ * is rechecked like a page. `prerender.remove()` stores one without
27
+ * rendering, which never goes stale. A Passthrough build handler that
28
+ * declines (`ctx.passthrough()`) stores one too, so its live handler answers.
29
+ * - A page and a render's marker (a `notFound()` or a decline) carry the
30
+ * render's start as `storedAt`, so a `markStale()` that lands during the
31
+ * render marks the result; `staleAt` counts the ttl from the write. A
32
+ * `remove()` marker renders nothing and carries its write time.
33
+ * - An on-demand target is path-only (its key carries no search); a warm
34
+ * target keeps its search params, which cache keys carry.
35
+ * - A warm writes only to a store shared beyond the place the call runs.
36
+ */
37
+ import type { SerializedSegmentData } from "../cache/types.js";
38
+ import type { HandlerCacheConfig } from "../rsc/types.js";
39
+ import type { ExecutionContext } from "../types/request-scope.js";
40
+ import type { OnDemandRouteConfig, PrerenderConfig, PrerenderFn, PrerenderResult } from "./on-demand.js";
41
+ /** Route metadata the trigger needs, resolved from a pathname by the router. */
42
+ export interface PrerenderMatchInfo {
43
+ routeName: string;
44
+ params: Record<string, string>;
45
+ /** True when the route opted into on-demand (Prerender(..., { onDemand })). */
46
+ isOnDemand: boolean;
47
+ /** True when the route is wrapped in Passthrough(). */
48
+ isPassthrough: boolean;
49
+ }
50
+ /**
51
+ * Raw producer output (matchForPrerender's main-variant shape). `onDemandConfig`
52
+ * is the route's `onDemand: { ttl, tags }` object (the `tags` callback can't
53
+ * ride the serialized trie), read from the loaded route entry during production.
54
+ */
55
+ export interface ProducerOutput {
56
+ segments: SerializedSegmentData[];
57
+ handles: string;
58
+ routeName: string;
59
+ params: Record<string, string>;
60
+ passthrough?: true;
61
+ /** The render hit `notFound()` and nothing else failed: no page to store. */
62
+ notFound?: true;
63
+ onDemandConfig?: OnDemandRouteConfig;
64
+ }
65
+ export interface PrerenderTriggerDeps<TEnv = any> {
66
+ routerId: string;
67
+ /**
68
+ * The key version: the router's data version (resolvePrerenderVersion),
69
+ * resolved per refresh (once per `.many()` batch), as the request handler
70
+ * resolves it per request.
71
+ */
72
+ resolveVersion: () => string;
73
+ /** True when running under Vite dev (drives the producer context's `dev`). */
74
+ isDev: () => boolean;
75
+ /**
76
+ * True under the Vite dev server (isViteDevServer): the one place the
77
+ * memory store counts as shared, and so where the not-shared warning fires.
78
+ */
79
+ isViteDevServer: () => boolean;
80
+ /** Load the per-router manifest/trie before matching, as router.fetch does. */
81
+ ensureManifest: () => Promise<void>;
82
+ /** Resolve the env-scoped prerender config (factory or object); undefined when unconfigured. */
83
+ resolveConfig: (env: TEnv, ctx: ExecutionContext | undefined) => PrerenderConfig<TEnv> | undefined;
84
+ /** Reverse an object target to a pathname; undefined when the route name is unknown. */
85
+ reverse: (route: string, params: Record<string, string>) => string | undefined;
86
+ /** Match a pathname to route metadata; null when nothing matches. */
87
+ matchRoute: (pathname: string) => PrerenderMatchInfo | null | Promise<PrerenderMatchInfo | null>;
88
+ /** Run the requestless producer (matchForPrerender with onDemand). May throw. */
89
+ runProducer: (input: {
90
+ pathname: string;
91
+ isPassthrough: boolean;
92
+ env: TEnv;
93
+ dev: boolean;
94
+ }) => Promise<ProducerOutput | null>;
95
+ /**
96
+ * Resolve `createRouter({ cache })` (factory or object) for a warm;
97
+ * undefined when the router has none. May throw, like the prerender factory.
98
+ */
99
+ resolveCacheConfig: (env: TEnv, ctx: ExecutionContext) => HandlerCacheConfig | undefined;
100
+ /** The router's own request handler (`router.fetch`): where a warm is dispatched. */
101
+ fetch: (request: Request, input: {
102
+ env: TEnv;
103
+ ctx: ExecutionContext;
104
+ }) => Promise<Response>;
105
+ /** The origin of the request the runner is called from, when there is one. */
106
+ ambientOrigin: () => string | undefined;
107
+ }
108
+ /** Thrown by the trigger when `throwOnError: true` and the operation did not succeed. */
109
+ export declare class PrerenderError extends Error {
110
+ readonly result: Extract<PrerenderResult, {
111
+ ok: false;
112
+ }>;
113
+ /** `operation` is the runner call that failed; the message names it. */
114
+ constructor(result: Extract<PrerenderResult, {
115
+ ok: false;
116
+ }>, operation?: "prerender" | "remove");
117
+ }
118
+ /**
119
+ * Create the `router.prerender` callable. See {@link PrerenderTriggerDeps}.
120
+ */
121
+ export declare function createPrerenderTrigger<TEnv = any, TRoutes = {}>(deps: PrerenderTriggerDeps<TEnv>): PrerenderFn<TEnv, TRoutes>;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Public on-demand prerender surface (`@rangojs/router/prerender`).
3
+ *
4
+ * Store contracts, config/result/target types, and the in-memory store (usable
5
+ * as a durable overlay in single-process node/Vercel functions, and as a test
6
+ * fake). The Cloudflare KV adapter lives in `@rangojs/router/prerender/cloudflare`.
7
+ */
8
+ export { PrerenderError } from "./create-prerender-trigger.js";
9
+ export { createMemoryPrerenderStore, type MemoryPrerenderStore, type MemoryPrerenderStoreOptions, } from "./memory-prerender-store.js";
10
+ export { serializePrerenderKey, type PrerenderKey, type PrerenderStoredEntry, type WritablePrerenderStore, } from "./writable-store.js";
11
+ export type { OnDemandOption, OnDemandRouteConfig, PrerenderConfig, PrerenderFn, PrerenderResult, PrerenderWarmCaches, PrerenderManyOptions, PrerenderRunner, PrerenderRunOptions, PrerenderRuntime, PrerenderTarget, PrerenderTargetObject, } from "./on-demand.js";
12
+ export type { PrerenderEntry } from "./store.js";
@@ -0,0 +1,29 @@
1
+ /**
2
+ * In-memory writable prerender store.
3
+ *
4
+ * Backs the zero-config dev overlay and the `@rangojs/router/testing` fake.
5
+ * A plain Map — no per-isolate null memoization (a later set() must be visible
6
+ * after an earlier miss), no React/RSC deps.
7
+ */
8
+ import { type PrerenderKey, type PrerenderStoredEntry, type WritablePrerenderStore } from "./writable-store.js";
9
+ export interface MemoryPrerenderStore extends WritablePrerenderStore {
10
+ delete(key: PrerenderKey): Promise<void>;
11
+ markStale(routerId: string, tags: string[]): Promise<void>;
12
+ /** Synchronous read by structured key (tests). */
13
+ peek(key: PrerenderKey): PrerenderStoredEntry | null;
14
+ /** All stored [serializedKey, entry] pairs (tests). */
15
+ entries(): [string, PrerenderStoredEntry][];
16
+ /** Drop every entry. */
17
+ clear(): void;
18
+ readonly size: number;
19
+ }
20
+ export interface MemoryPrerenderStoreOptions {
21
+ /** Injectable clock for `markStale` in deterministic tests. Defaults to Date.now. */
22
+ now?: () => number;
23
+ }
24
+ /**
25
+ * Create an in-memory {@link WritablePrerenderStore} with test-inspection
26
+ * helpers. Invalidation is mark-stale (sets `staleAt`), never delete — matching
27
+ * the durable v1 contract so the entry keeps serving until refreshed.
28
+ */
29
+ export declare function createMemoryPrerenderStore(options?: MemoryPrerenderStoreOptions): MemoryPrerenderStore;