@rangojs/router 0.17.0 → 0.19.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 (293) hide show
  1. package/README.md +3 -2
  2. package/dist/types/browser/cookie-name.d.ts +6 -0
  3. package/dist/types/browser/event-controller.d.ts +3 -3
  4. package/dist/types/browser/history-state.d.ts +18 -0
  5. package/dist/types/browser/navigation-transaction.d.ts +20 -2
  6. package/dist/types/browser/partial-update.d.ts +16 -1
  7. package/dist/types/browser/prefetch/cache.d.ts +6 -6
  8. package/dist/types/browser/react/Link.d.ts +19 -9
  9. package/dist/types/browser/react/context.d.ts +2 -2
  10. package/dist/types/browser/react/location-state-shared.d.ts +95 -56
  11. package/dist/types/browser/react/location-state.d.ts +1 -1
  12. package/dist/types/browser/transition-when.d.ts +80 -0
  13. package/dist/types/browser/types.d.ts +104 -9
  14. package/dist/types/build/generate-manifest.d.ts +8 -0
  15. package/dist/types/cache/background-task.d.ts +24 -2
  16. package/dist/types/cache/cache-exec-scope.d.ts +31 -0
  17. package/dist/types/cache/cache-key-utils.d.ts +48 -2
  18. package/dist/types/cache/cache-policy.d.ts +18 -1
  19. package/dist/types/cache/cache-runtime.d.ts +7 -0
  20. package/dist/types/cache/cache-scope.d.ts +192 -26
  21. package/dist/types/cache/cache-tag.d.ts +116 -11
  22. package/dist/types/cache/cf/cf-base64.d.ts +6 -0
  23. package/dist/types/cache/cf/cf-cache-constants.d.ts +11 -0
  24. package/dist/types/cache/cf/cf-cache-store.d.ts +224 -72
  25. package/dist/types/cache/cf/cf-cache-types.d.ts +31 -4
  26. package/dist/types/cache/cf/cf-shell-frame.d.ts +95 -0
  27. package/dist/types/cache/cf/cf-tag-marker-memo.d.ts +19 -0
  28. package/dist/types/cache/document-cache.d.ts +10 -0
  29. package/dist/types/cache/handle-snapshot.d.ts +41 -8
  30. package/dist/types/cache/index.d.ts +1 -0
  31. package/dist/types/cache/invalidation-order.d.ts +23 -0
  32. package/dist/types/cache/isolate-tag-memo.d.ts +149 -0
  33. package/dist/types/cache/request-tag-mask.d.ts +54 -0
  34. package/dist/types/cache/shell-memo.d.ts +178 -0
  35. package/dist/types/cache/shell-snapshot.d.ts +141 -105
  36. package/dist/types/cache/tag-invalidation.d.ts +54 -3
  37. package/dist/types/cache/types.d.ts +267 -96
  38. package/dist/types/cache/vercel/vercel-cache-store.d.ts +123 -18
  39. package/dist/types/client-urls/navigation.d.ts +28 -2
  40. package/dist/types/client-urls/server-projection.d.ts +5 -2
  41. package/dist/types/client-urls/types.d.ts +5 -5
  42. package/dist/types/client.d.ts +1 -1
  43. package/dist/types/index.d.ts +1 -1
  44. package/dist/types/index.rsc.d.ts +1 -1
  45. package/dist/types/prerender/build-shell-capture.d.ts +11 -4
  46. package/dist/types/route-definition/helpers-types.d.ts +11 -11
  47. package/dist/types/route-name.d.ts +7 -0
  48. package/dist/types/router/error-handling.d.ts +1 -1
  49. package/dist/types/router/instrument.d.ts +8 -0
  50. package/dist/types/router/intercept-resolution.d.ts +2 -0
  51. package/dist/types/router/match-context.d.ts +5 -0
  52. package/dist/types/router/match-middleware/background-revalidation.d.ts +0 -4
  53. package/dist/types/router/middleware-types.d.ts +6 -1
  54. package/dist/types/router/router-options.d.ts +4 -1
  55. package/dist/types/router/segment-resolution/loader-cache.d.ts +58 -12
  56. package/dist/types/router/segment-resolution/loader-mask.d.ts +3 -4
  57. package/dist/types/router/segment-resolution/loader-snapshot.d.ts +33 -48
  58. package/dist/types/router/segment-resolution/mask-nested.d.ts +73 -24
  59. package/dist/types/router/segment-resolution/view-transition-default.d.ts +7 -5
  60. package/dist/types/router/transition-when.d.ts +24 -10
  61. package/dist/types/rsc/attach-transition-when.d.ts +18 -0
  62. package/dist/types/rsc/full-payload.d.ts +21 -0
  63. package/dist/types/rsc/handler.d.ts +1 -1
  64. package/dist/types/rsc/response-cache-serve.d.ts +15 -12
  65. package/dist/types/rsc/shell-build-manifest.d.ts +12 -3
  66. package/dist/types/rsc/shell-capture-constants.d.ts +116 -12
  67. package/dist/types/rsc/shell-capture.d.ts +202 -74
  68. package/dist/types/rsc/shell-serve.d.ts +144 -50
  69. package/dist/types/rsc/types.d.ts +9 -0
  70. package/dist/types/segment-system.d.ts +7 -0
  71. package/dist/types/server/context.d.ts +155 -33
  72. package/dist/types/server/cookie-store.d.ts +33 -0
  73. package/dist/types/server/handle-store.d.ts +52 -1
  74. package/dist/types/server/request-context.d.ts +210 -63
  75. package/dist/types/server/request-headers.d.ts +44 -0
  76. package/dist/types/server.d.ts +1 -0
  77. package/dist/types/ssr/index.d.ts +9 -0
  78. package/dist/types/ssr/ssr-root.d.ts +9 -8
  79. package/dist/types/testing/e2e/index.d.ts +1 -1
  80. package/dist/types/testing/flight.entry.d.ts +7 -0
  81. package/dist/types/testing/index.d.ts +3 -2
  82. package/dist/types/testing/internal/action-id.d.ts +7 -0
  83. package/dist/types/testing/location-state-key.d.ts +24 -0
  84. package/dist/types/testing/render-route.d.ts +17 -1
  85. package/dist/types/testing/run-loader.d.ts +18 -3
  86. package/dist/types/testing/run-transition-when.d.ts +44 -69
  87. package/dist/types/testing/serve-shell-request.d.ts +133 -0
  88. package/dist/types/testing/shell-status.d.ts +44 -19
  89. package/dist/types/transition-when-ref.d.ts +79 -0
  90. package/dist/types/types/cache-types.d.ts +15 -3
  91. package/dist/types/types/handler-context.d.ts +14 -1
  92. package/dist/types/types/index.d.ts +1 -1
  93. package/dist/types/types/segments.d.ts +86 -71
  94. package/dist/types/urls/path-helper-types.d.ts +3 -4
  95. package/dist/types/urls/pattern-types.d.ts +21 -18
  96. package/dist/types/vite/plugins/transition-when-hoist.d.ts +45 -0
  97. package/dist/types/vite/router-discovery.d.ts +29 -2
  98. package/dist/vite/index.js +1005 -105
  99. package/package.json +1 -1
  100. package/skills/breadcrumbs/SKILL.md +17 -7
  101. package/skills/cache-guide/SKILL.md +175 -76
  102. package/skills/caching/SKILL.md +361 -33
  103. package/skills/catalog.json +1 -1
  104. package/skills/client-urls/SKILL.md +12 -7
  105. package/skills/cloudflare/SKILL.md +15 -0
  106. package/skills/comparison/references/framework-comparison.md +52 -37
  107. package/skills/defer-hydration/SKILL.md +50 -30
  108. package/skills/deployment-caching/SKILL.md +5 -2
  109. package/skills/document-cache/SKILL.md +32 -2
  110. package/skills/handler-use/SKILL.md +2 -0
  111. package/skills/hooks/SKILL.md +3 -1
  112. package/skills/hooks/handle-and-actions.md +3 -1
  113. package/skills/hooks/navigation.md +23 -2
  114. package/skills/hooks/state.md +101 -7
  115. package/skills/intercept/SKILL.md +29 -12
  116. package/skills/loader/SKILL.md +197 -44
  117. package/skills/middleware/SKILL.md +34 -9
  118. package/skills/migrate-nextjs/SKILL.md +68 -37
  119. package/skills/migrate-react-router/SKILL.md +2 -1
  120. package/skills/migrate-react-router/component-migration.md +3 -1
  121. package/skills/migrate-react-router/data-and-actions.md +3 -1
  122. package/skills/migrate-react-router/route-mapping.md +7 -4
  123. package/skills/observability/SKILL.md +65 -0
  124. package/skills/parallel/SKILL.md +13 -8
  125. package/skills/ppr/SKILL.md +715 -371
  126. package/skills/prerender/SKILL.md +30 -17
  127. package/skills/rango/SKILL.md +72 -34
  128. package/skills/route/SKILL.md +14 -7
  129. package/skills/router-setup/SKILL.md +4 -2
  130. package/skills/scripts/SKILL.md +8 -1
  131. package/skills/server-actions/SKILL.md +11 -0
  132. package/skills/shell-manifest/SKILL.md +21 -12
  133. package/skills/testing/SKILL.md +49 -43
  134. package/skills/testing/cache-prerender.md +15 -14
  135. package/skills/testing/client-components.md +45 -3
  136. package/skills/testing/loader.md +19 -1
  137. package/skills/testing/middleware.md +1 -0
  138. package/skills/testing/predicates.md +49 -49
  139. package/skills/testing/render-handler.md +1 -0
  140. package/skills/testing/serve-shell-request.md +202 -0
  141. package/skills/testing/server-actions.md +9 -3
  142. package/skills/theme/SKILL.md +56 -4
  143. package/skills/typesafety/env-and-bindings.md +18 -2
  144. package/skills/use-cache/SKILL.md +91 -23
  145. package/skills/vercel/SKILL.md +14 -2
  146. package/skills/view-transitions/SKILL.md +150 -29
  147. package/src/__augment-tests__/augmented.check.ts +23 -0
  148. package/src/browser/cookie-name.ts +23 -0
  149. package/src/browser/event-controller.ts +4 -4
  150. package/src/browser/history-state.ts +75 -1
  151. package/src/browser/link-interceptor.ts +4 -0
  152. package/src/browser/navigation-bridge.ts +77 -18
  153. package/src/browser/navigation-client.ts +4 -3
  154. package/src/browser/navigation-store.ts +43 -1
  155. package/src/browser/navigation-transaction.ts +31 -11
  156. package/src/browser/partial-update.ts +146 -35
  157. package/src/browser/prefetch/cache.ts +6 -6
  158. package/src/browser/prefetch/fetch.ts +2 -2
  159. package/src/browser/react/Link.tsx +38 -33
  160. package/src/browser/react/NavigationProvider.tsx +2 -2
  161. package/src/browser/react/context.ts +2 -2
  162. package/src/browser/react/location-state-shared.ts +241 -49
  163. package/src/browser/react/location-state.ts +5 -2
  164. package/src/browser/rsc-router.tsx +9 -0
  165. package/src/browser/server-action-bridge.ts +84 -15
  166. package/src/browser/transition-when.ts +205 -0
  167. package/src/browser/types.ts +124 -10
  168. package/src/build/generate-manifest.ts +77 -1
  169. package/src/cache/background-task.ts +55 -2
  170. package/src/cache/cache-exec-scope.ts +48 -1
  171. package/src/cache/cache-key-utils.ts +62 -2
  172. package/src/cache/cache-policy.ts +31 -4
  173. package/src/cache/cache-runtime.ts +209 -129
  174. package/src/cache/cache-scope.ts +573 -69
  175. package/src/cache/cache-tag.ts +475 -19
  176. package/src/cache/cf/cf-base64.ts +36 -4
  177. package/src/cache/cf/cf-cache-constants.ts +12 -0
  178. package/src/cache/cf/cf-cache-store.ts +1094 -376
  179. package/src/cache/cf/cf-cache-types.ts +32 -4
  180. package/src/cache/cf/cf-shell-frame.ts +258 -0
  181. package/src/cache/cf/cf-tag-marker-memo.ts +55 -40
  182. package/src/cache/document-cache.ts +53 -7
  183. package/src/cache/handle-snapshot.ts +53 -9
  184. package/src/cache/index.ts +2 -0
  185. package/src/cache/invalidation-order.ts +84 -0
  186. package/src/cache/isolate-tag-memo.ts +308 -0
  187. package/src/cache/request-tag-mask.ts +147 -0
  188. package/src/cache/shell-memo.ts +341 -0
  189. package/src/cache/shell-snapshot.ts +249 -277
  190. package/src/cache/tag-invalidation.ts +109 -18
  191. package/src/cache/types.ts +274 -102
  192. package/src/cache/vercel/vercel-cache-store.ts +561 -74
  193. package/src/client-urls/client-root.tsx +8 -4
  194. package/src/client-urls/client-urls.ts +8 -6
  195. package/src/client-urls/navigation.ts +55 -4
  196. package/src/client-urls/server-projection.ts +31 -10
  197. package/src/client-urls/types.ts +12 -5
  198. package/src/client.tsx +2 -0
  199. package/src/host/cookie-handler.ts +2 -1
  200. package/src/index.rsc.ts +3 -0
  201. package/src/index.ts +3 -0
  202. package/src/loader-redirect.tsx +7 -3
  203. package/src/prerender/build-shell-capture.ts +173 -110
  204. package/src/route-definition/dsl-helpers.ts +6 -3
  205. package/src/route-definition/helpers-types.ts +11 -11
  206. package/src/route-name.ts +12 -0
  207. package/src/router/content-negotiation.ts +4 -1
  208. package/src/router/error-handling.ts +5 -2
  209. package/src/router/handler-context.ts +42 -5
  210. package/src/router/instrument.ts +54 -2
  211. package/src/router/intercept-resolution.ts +30 -8
  212. package/src/router/loader-resolution.ts +89 -33
  213. package/src/router/logging.ts +5 -3
  214. package/src/router/match-api.ts +89 -42
  215. package/src/router/match-context.ts +5 -0
  216. package/src/router/match-handlers.ts +14 -29
  217. package/src/router/match-middleware/background-revalidation.ts +3 -5
  218. package/src/router/match-middleware/cache-lookup.ts +52 -19
  219. package/src/router/match-middleware/cache-store.ts +36 -10
  220. package/src/router/match-result.ts +1 -0
  221. package/src/router/middleware-types.ts +6 -1
  222. package/src/router/middleware.ts +20 -4
  223. package/src/router/navigation-snapshot.ts +6 -5
  224. package/src/router/prerender-match.ts +10 -0
  225. package/src/router/request-classification.ts +7 -4
  226. package/src/router/router-options.ts +4 -1
  227. package/src/router/segment-resolution/fresh.ts +18 -13
  228. package/src/router/segment-resolution/helpers.ts +4 -2
  229. package/src/router/segment-resolution/loader-cache.ts +366 -70
  230. package/src/router/segment-resolution/loader-mask.ts +3 -4
  231. package/src/router/segment-resolution/loader-snapshot.ts +215 -126
  232. package/src/router/segment-resolution/mask-nested.ts +161 -42
  233. package/src/router/segment-resolution/revalidation.ts +27 -13
  234. package/src/router/segment-resolution/view-transition-default.ts +6 -21
  235. package/src/router/telemetry.ts +6 -3
  236. package/src/router/transition-when.ts +34 -52
  237. package/src/router.ts +2 -1
  238. package/src/rsc/attach-transition-when.ts +47 -0
  239. package/src/rsc/full-payload.ts +47 -3
  240. package/src/rsc/handler.ts +24 -10
  241. package/src/rsc/loader-fetch.ts +2 -1
  242. package/src/rsc/manifest-init.ts +4 -0
  243. package/src/rsc/origin-guard.ts +8 -5
  244. package/src/rsc/progressive-enhancement.ts +10 -32
  245. package/src/rsc/response-cache-serve.ts +69 -78
  246. package/src/rsc/response-route-handler.ts +2 -2
  247. package/src/rsc/rsc-rendering.ts +712 -338
  248. package/src/rsc/server-action.ts +8 -20
  249. package/src/rsc/shell-build-manifest.ts +49 -29
  250. package/src/rsc/shell-capture-constants.ts +182 -12
  251. package/src/rsc/shell-capture.ts +1126 -612
  252. package/src/rsc/shell-serve.ts +331 -85
  253. package/src/rsc/ssr-setup.ts +3 -2
  254. package/src/rsc/types.ts +9 -0
  255. package/src/segment-system.tsx +40 -7
  256. package/src/server/context.ts +328 -72
  257. package/src/server/cookie-store.ts +184 -133
  258. package/src/server/handle-store.ts +245 -27
  259. package/src/server/request-context.ts +364 -100
  260. package/src/server/request-headers.ts +134 -0
  261. package/src/server.ts +4 -0
  262. package/src/ssr/index.tsx +96 -37
  263. package/src/ssr/ssr-root.tsx +9 -8
  264. package/src/testing/dispatch.ts +4 -7
  265. package/src/testing/e2e/index.ts +1 -0
  266. package/src/testing/flight.entry.ts +14 -0
  267. package/src/testing/index.ts +5 -0
  268. package/src/testing/internal/action-id.ts +25 -0
  269. package/src/testing/internal/context.ts +1 -1
  270. package/src/testing/location-state-key.ts +45 -0
  271. package/src/testing/render-handler.ts +1 -1
  272. package/src/testing/render-route.tsx +109 -16
  273. package/src/testing/run-client-revalidate.ts +3 -19
  274. package/src/testing/run-loader.ts +67 -5
  275. package/src/testing/run-transition-when.ts +109 -172
  276. package/src/testing/serve-shell-request.ts +381 -0
  277. package/src/testing/shell-status.ts +73 -47
  278. package/src/theme/ThemeProvider.tsx +7 -6
  279. package/src/transition-when-ref.ts +220 -0
  280. package/src/types/cache-types.ts +15 -3
  281. package/src/types/handler-context.ts +14 -1
  282. package/src/types/index.ts +3 -0
  283. package/src/types/segments.ts +94 -91
  284. package/src/urls/path-helper-types.ts +3 -4
  285. package/src/urls/pattern-types.ts +21 -18
  286. package/src/vite/discovery/discover-routers.ts +3 -0
  287. package/src/vite/discovery/shell-prerender-phase.ts +9 -2
  288. package/src/vite/discovery/virtual-module-codegen.ts +16 -6
  289. package/src/vite/plugins/transition-when-hoist.ts +1064 -0
  290. package/src/vite/rango.ts +2 -0
  291. package/src/vite/router-discovery.ts +89 -9
  292. package/dist/types/rsc/transition-gate.d.ts +0 -23
  293. package/src/rsc/transition-gate.ts +0 -86
@@ -5,9 +5,11 @@
5
5
  * `@vercel/functions`). It is the production store analogue to CFCacheStore, but
6
6
  * far smaller: Vercel's Runtime Cache already IS a distributed, tag-aware cache
7
7
  * (regional storage, global tag-expire within ~300ms), so none of CFCacheStore's
8
- * L1/L2 tiering, KV tag-marker comparison, marker memoization, or per-tier
9
- * timeout budgets are needed here - the platform does that work. What this store
10
- * adds on top of the raw primitive is the parts Vercel does NOT give us:
8
+ * L1/L2 tiering or per-tier timeout budgets are needed here - the platform does
9
+ * that work. Tag markers (`tm`, see isTagsInvalidatedSince) gate shell reads
10
+ * and writes and every cache write that started before an invalidation
11
+ * (#977); only PPR shell reads memoize them. What this
12
+ * store adds on top of the raw primitive is the parts Vercel does NOT give us:
11
13
  *
12
14
  * 1. Stale-while-revalidate. `getCache` has no stale-but-serve: a TTL'd entry
13
15
  * simply becomes a miss. We store our own {staleAt, expiresAt} envelope and
@@ -30,8 +32,9 @@
30
32
  * `getCache(...)` result, which satisfies the shape structurally. That keeps the
31
33
  * router free of a hard Vercel dependency.
32
34
  */
33
- import type { SegmentCacheStore, CachedEntryData, CacheDefaults, CacheGetResult, CacheItemResult, CacheItemOptions, ShellCacheEntry, CacheReadError } from "../types.js";
34
- import type { RequestContext } from "../../server/request-context.js";
35
+ import type { SegmentCacheStore, CachedEntryData, CacheDefaults, CacheGetResult, CacheItemResult, CacheItemOptions, ShellCacheEntry, ShellDocumentRead, ShellDocumentReadOptions, CacheReadError } from "../types.js";
36
+ import { type RequestContext } from "../../server/request-context.js";
37
+ import { type StoreMemoOptions } from "../shell-memo.js";
35
38
  /**
36
39
  * Minimal structural shape of the Vercel Runtime Cache returned by `getCache()`
37
40
  * from `@vercel/functions`. Declared locally so @rangojs/router carries no hard
@@ -69,7 +72,7 @@ export declare const VERCEL_MAX_TAGS_PER_ITEM: number;
69
72
  /** Max tag length in UTF-8 bytes accepted by Vercel; longer tags are skipped. */
70
73
  export declare const VERCEL_MAX_TAG_BYTES: number;
71
74
  /** Read-path outcome for the debug sink. */
72
- export type VercelCacheReadOutcome = "miss" | "fresh" | "stale-revalidate" | "expired" | "corrupt" | "error";
75
+ export type VercelCacheReadOutcome = "miss" | "memo-hit" | "fresh" | "stale-revalidate" | "expired" | "corrupt" | "error";
73
76
  /** Diagnostic event emitted on every read when `debug` is set. */
74
77
  export interface VercelCacheReadDebugEvent {
75
78
  op: "get" | "getItem" | "getResponse" | "getShell";
@@ -100,10 +103,13 @@ export interface VercelCacheStoreOptions<TEnv = unknown> {
100
103
  */
101
104
  cache: VercelRuntimeCache;
102
105
  /**
103
- * `waitUntil` from `@vercel/functions`. Used only to run the stale-read lock
104
- * write (herd dampening) off the response path - the router already
105
- * backgrounds the actual writes. When omitted, the lock write runs detached
106
- * (fire-and-forget) instead.
106
+ * `waitUntil` from `@vercel/functions`. Keeps two background reads/writes
107
+ * alive past the response: the stale-read lock write (herd dampening; the
108
+ * router already backgrounds the actual writes) and the PPR shell reads'
109
+ * tag-marker memo refreshes (`memo.markerMaxStaleMs`). When omitted, both
110
+ * run detached (fire-and-forget), and the platform may end a refresh when
111
+ * the response ends; the next read past the stale window then reads the
112
+ * marker itself.
107
113
  */
108
114
  waitUntil?: (promise: Promise<unknown>) => void;
109
115
  /**
@@ -136,6 +142,26 @@ export interface VercelCacheStoreOptions<TEnv = unknown> {
136
142
  * receives the structured VercelCacheReadDebugEvent.
137
143
  */
138
144
  debug?: VercelCacheDebug;
145
+ /**
146
+ * Per-process memos ({@link StoreMemoOptions}), kept per `cache` handle:
147
+ * create the `getCache()` handle once per process, not per request, or the
148
+ * memos never hit. `shellMs` (default 2000) and `shellMaxBytes` (default
149
+ * 16 MiB) size the PPR shell memo; `markerFreshMs` (default 300) and
150
+ * `markerMaxStaleMs` (default 2000) the memo of the tag markers
151
+ * `invalidateTags()` writes, which PPR shell reads use
152
+ * (stale-while-revalidate). The process that invalidates writes the new
153
+ * marker into its memo; another process in the region rejects a memoized
154
+ * shell once its marker memo sees it (up to `markerMaxStaleMs`). The
155
+ * markers are a regional `cache.set` (only `expireTag` is global), so
156
+ * another region serves a shell it memoized before the invalidation until
157
+ * its window passes, where without the memo `expireTag` removes it within
158
+ * about 300 ms; a platform `expireTag` issued outside rango is likewise
159
+ * seen once the window passes. The response of a request that ran
160
+ * `updateTag()` / `revalidateTag()` sets the fresh-reads cookie, and the
161
+ * same user's requests carrying it skip both memos. `{ shellMs: 0 }` turns
162
+ * the shell memo off, `{ markerFreshMs: 0 }` the marker memo.
163
+ */
164
+ memo?: StoreMemoOptions;
139
165
  }
140
166
  /**
141
167
  * Vercel Runtime Cache-backed segment cache store.
@@ -170,12 +196,18 @@ export interface VercelCacheStoreOptions<TEnv = unknown> {
170
196
  * import { getCache, waitUntil } from "@vercel/functions";
171
197
  * import { VercelCacheStore } from "@rangojs/router/cache";
172
198
  *
199
+ * // One handle per process: getCache() resolves the platform cache on every
200
+ * // call, and the PPR shell and tag-marker memos are kept per handle.
201
+ * // `process.env` (not `import.meta.env`, which needs a VITE_ prefix and
202
+ * // would be undefined here) so cross-deploy busting via `version` works.
203
+ * const runtimeCache = getCache({
204
+ * namespace: process.env.VERCEL_DEPLOYMENT_ID,
205
+ * });
206
+ *
173
207
  * export const router = createRouter({
174
208
  * cache: () => ({
175
209
  * store: new VercelCacheStore({
176
- * // `process.env` (not `import.meta.env`, which needs a VITE_ prefix and
177
- * // would be undefined here) so cross-deploy busting via `version` works.
178
- * cache: getCache({ namespace: process.env.VERCEL_DEPLOYMENT_ID }),
210
+ * cache: runtimeCache,
179
211
  * waitUntil,
180
212
  * defaults: { ttl: 60, swr: 300 },
181
213
  * }),
@@ -191,6 +223,16 @@ export declare class VercelCacheStore<TEnv = unknown> implements SegmentCacheSto
191
223
  private readonly waitUntil?;
192
224
  private readonly version?;
193
225
  private readonly maxItemBytes;
226
+ private readonly memo;
227
+ private readonly handleMemos;
228
+ /** @internal SegmentCacheStore.freshReadsWindowMs */
229
+ readonly freshReadsWindowMs: number;
230
+ /**
231
+ * @internal SegmentCacheStore.maxShellEntryBytes: the item cap, less the
232
+ * base64 expansion of the prelude the shell envelope stores (the capture
233
+ * measures raw prelude bytes).
234
+ */
235
+ readonly maxShellEntryBytes: number;
194
236
  private readonly name?;
195
237
  private readonly debug?;
196
238
  constructor(options: VercelCacheStoreOptions<TEnv>);
@@ -210,15 +252,78 @@ export declare class VercelCacheStore<TEnv = unknown> implements SegmentCacheSto
210
252
  entry: ShellCacheEntry;
211
253
  shouldRevalidate?: boolean;
212
254
  } | null>;
255
+ /**
256
+ * @internal The document serve path's read (SegmentCacheStore
257
+ * .readShellDocument): the same read as getShell, with the prelude decoded
258
+ * here, once per memoized shell rather than once per HIT, and the read's
259
+ * `debugPerformance` stats. The runtime cache returns a parsed value, so
260
+ * there is no prelude-first read and the tag-marker read follows the entry
261
+ * read (`markerSerial`).
262
+ */
263
+ readShellDocument(key: string, options?: ShellDocumentReadOptions): Promise<ShellDocumentRead | null>;
264
+ /** @internal SegmentCacheStore.dropShellMemo */
265
+ dropShellMemo(key: string): void;
266
+ /**
267
+ * getShell's read, shared with readShellDocument: the shell memo first
268
+ * (shell-memo.ts), then the runtime cache. A memoized shell still runs the
269
+ * tag-marker check; `memoized` is the memo slot's value when the read came
270
+ * from, or went into, the memo (readShellDocument keeps the decoded prelude
271
+ * there).
272
+ */
273
+ private readShell;
274
+ /**
275
+ * The shell's tag-marker check, timed into `stats`. Markers a prefetch
276
+ * already started (hinted tags) are awaited, not re-read; the check itself
277
+ * always uses the entry's own tags.
278
+ */
279
+ private shellTagsInvalidated;
280
+ /**
281
+ * Start the tm-marker reads of the tags this shell key carried last time
282
+ * (and the route's static tags) alongside the entry read.
283
+ */
284
+ private prefetchShellMarkers;
285
+ /**
286
+ * A tag's latest invalidatedAt (or null). With `isolateMemo` (PPR shell
287
+ * reads only) through the per-process marker memo (isolate-tag-memo.ts):
288
+ * fresh values are used as is, stale ones are used and refreshed in the
289
+ * background, older ones block on the runtime-cache read. A request
290
+ * carrying the fresh-reads cookie skips the memo. A failed read rejects and
291
+ * is never memoized. The shell write gate, build shells and the data
292
+ * write gates (isTagsInvalidatedSince) read the runtime cache each time
293
+ * they ask; gates of a request asking while a read is in flight share it
294
+ * (gateMarkerRead), and nothing is kept once it settles.
295
+ */
296
+ private readTagMarker;
213
297
  putShell(key: string, entry: ShellCacheEntry, ttlSeconds?: number, swrSeconds?: number, tags?: string[]): Promise<"stored" | "invalidated" | void>;
214
298
  /**
215
- * Shell tag-generation gate (SegmentCacheStore.isTagsInvalidatedSince).
216
- * expireTag() keeps no queryable history, so invalidateTags() writes "tm"
217
- * markers for runtime capture races and immutable build shells. Marker >=
218
- * generation start wins; read errors fail open like the CF marker check.
299
+ * SegmentCacheStore.isTagsInvalidatedSince. expireTag() keeps no queryable
300
+ * history, so invalidateTags() writes "tm" markers for runtime capture
301
+ * races, immutable build shells and data writes that started before an
302
+ * invalidation (#977, tag-invalidation.ts predatesInvalidation). Marker >=
303
+ * generation start wins. Read errors fail open like the CF marker check,
304
+ * except for the write gate (`failClosed`), which counts them as an
305
+ * invalidation. Each tag's marker is read through gateMarkerRead
306
+ * (request-tag-mask.ts): gates asking while a read is in flight share it.
219
307
  */
220
- isTagsInvalidatedSince(tags: string[], sinceMs: number): Promise<boolean>;
308
+ isTagsInvalidatedSince(tags: string[], sinceMs: number, options?: {
309
+ failClosed?: boolean;
310
+ }): Promise<boolean>;
311
+ private tagsInvalidatedSince;
221
312
  invalidateTags(tags: string[]): Promise<void>;
313
+ /**
314
+ * The request-local half of invalidateTags(), run before its first await
315
+ * (#973): the request masks the tags (request-tag-mask.ts). The platform's
316
+ * expireTag() and the tm marker writes are what other requests see, and
317
+ * revalidateTag() does not wait for them.
318
+ */
319
+ private maskTagsForRequest;
320
+ /**
321
+ * Whether this request masked one of `tags` through this store at or after
322
+ * `taggedAt`. An entry without a stamp counts as older than any mask.
323
+ */
324
+ private maskedForRequest;
325
+ /** A data read's mask check: reports the miss and returns true when masked. */
326
+ private maskedMiss;
222
327
  private toStoreKey;
223
328
  private write;
224
329
  /**
@@ -1,4 +1,5 @@
1
1
  import type { ClientUrlPatterns } from "./types.js";
2
+ import type { TransitionWhenFn } from "../types/segments.js";
2
3
  export interface ClientUrlNavigationIntent {
3
4
  readonly routeId: string;
4
5
  /** Destination params from the local trie match (definition-local). */
@@ -6,14 +7,39 @@ export interface ClientUrlNavigationIntent {
6
7
  /** Absolute destination pathname (mount included) and search ("?..." or ""). */
7
8
  readonly pathname: string;
8
9
  readonly search: string;
10
+ /**
11
+ * transition({ when }) gated this navigation off at the swap: the
12
+ * destination presents urgently instead of in a transition lane.
13
+ */
14
+ readonly transitionGatedOff?: boolean;
15
+ }
16
+ /** What the bridge needs to decide transition({ when }) at the swap. */
17
+ export interface ClientUrlDestination {
18
+ /** Definition-local params of the destination match. */
19
+ readonly params: Readonly<Record<string, string>>;
20
+ /** Canonical route name (include prefix applied); undefined when unnamed. */
21
+ readonly routeName: string | undefined;
22
+ /** The destination route's own `when`, from its "use client" definition. */
23
+ readonly when: TransitionWhenFn | undefined;
24
+ /**
25
+ * Param names the ORIGIN route matched inside the group: the committed
26
+ * params minus these are the mount's (include prefix) params.
27
+ */
28
+ readonly originLocalParamNames: readonly string[];
9
29
  }
10
30
  export declare function setActiveInterceptTargets(targets: readonly string[] | undefined): void;
11
31
  export interface ClientUrlNavigationPresentation {
12
32
  readonly routeId: string;
33
+ /**
34
+ * The transition({ when }) decision made at the optimistic swap (true =
35
+ * gated off), or undefined when nothing was swapped (a same-route
36
+ * navigation decides at the canonical commit).
37
+ */
38
+ readonly transitionGatedOff: boolean | undefined;
13
39
  clear(): void;
14
40
  }
15
- export declare function registerClientUrlGroup(definition: ClientUrlPatterns, mount: string, namePrefix: string, setIntent: (intent: ClientUrlNavigationIntent | null) => void): () => void;
16
- export declare function beginClientUrlNavigation(targetUrl: URL, signal: AbortSignal): ClientUrlNavigationPresentation | null;
41
+ export declare function registerClientUrlGroup(definition: ClientUrlPatterns, mount: string, namePrefix: string, setIntent: (intent: ClientUrlNavigationIntent | null) => void, routeId?: string): () => void;
42
+ export declare function beginClientUrlNavigation(targetUrl: URL, signal: AbortSignal, decideTransition?: (destination: ClientUrlDestination) => boolean): ClientUrlNavigationPresentation | null;
17
43
  /**
18
44
  * Run the held clientUrls route's per-loader revalidate() predicates and
19
45
  * encode their decisions for the request header. Predicates are CLIENT code
@@ -32,9 +32,12 @@ export interface ClientUrlProjectionRoute {
32
32
  * document renders await them before first flush. Absent (= none) in
33
33
  * projections serialized before the option existed. */
34
34
  readonly awaitedLoaderIndices?: readonly number[];
35
- /** Data-only transition config (no `when` — server-tree only); absent in
36
- * projections serialized before transition support. */
35
+ /** Data-only transition config (never `when`); absent in projections
36
+ * serialized before transition support. */
37
37
  readonly transition?: Readonly<ClientTransitionConfig>;
38
+ /** The route's transition() declares a browser-run `when`. Materialization
39
+ * attaches a clientUrls when reference (definition + route id). */
40
+ readonly transitionWhen?: true;
38
41
  }
39
42
  export interface ClientUrlProjectionIntercept {
40
43
  readonly slotName: `@${string}`;
@@ -88,12 +88,12 @@ export interface ClientUrlLoaderRecord {
88
88
  readonly ssr?: false;
89
89
  }
90
90
  /**
91
- * The data-only subset of TransitionConfig a clientUrls() route may declare:
92
- * ViewTransition classes/name plus the boundary opt-out. The `when` gate is a
93
- * server-executed predicate and stays in the server tree — it cannot cross the
94
- * "use client" projection boundary.
91
+ * The TransitionConfig subset a clientUrls() route may declare: ViewTransition
92
+ * classes/name, the boundary opt-out, and the browser-run `when` gate. The
93
+ * projection carries only a "has when" marker; the browser calls the function
94
+ * this module declares (transition-when-ref.ts resolveTransitionWhen).
95
95
  */
96
- export type ClientTransitionConfig = Pick<TransitionConfig, "enter" | "exit" | "update" | "share" | "default" | "name" | "viewTransition">;
96
+ export type ClientTransitionConfig = Pick<TransitionConfig, "enter" | "exit" | "update" | "share" | "default" | "name" | "viewTransition" | "when">;
97
97
  export interface ClientUrlRouteRecord {
98
98
  readonly id: string;
99
99
  readonly pattern: string;
@@ -167,7 +167,7 @@ export { useSearchParams } from "./browser/react/use-search-params.js";
167
167
  export type { SearchParamsInit, SetSearchParams, SetSearchParamsOptions, } from "./browser/react/use-search-params.js";
168
168
  export { useParams } from "./browser/react/use-params.js";
169
169
  export { useNonce } from "./browser/react/nonce-context.js";
170
- export type { RouterInstance, RouterNavigateOptions, ReadonlyURLSearchParams, ActionState, ActionLifecycleState, } from "./browser/types.js";
170
+ export type { RouterInstance, RouterNavigateOptions, HistoryState, PlainHistoryState, ReadonlyURLSearchParams, ActionState, ActionLifecycleState, } from "./browser/types.js";
171
171
  export { useAction, type ServerActionFunction, } from "./browser/react/use-action.js";
172
172
  export { useSegments, type SegmentsState, } from "./browser/react/use-segments.js";
173
173
  export { NavigationProvider, type NavigationProviderProps, } from "./browser/react/NavigationProvider.js";
@@ -16,7 +16,7 @@ export type { SearchSchema, SearchSchemaValue, ResolveSearchSchema, RouteSearchP
16
16
  export { TRACKING_SEARCH_PARAMS, type CacheSearchParams, } from "./cache/search-params-filter.js";
17
17
  export { createLoader } from "./loader.js";
18
18
  export type { RouteHelpers, RouteHandlers } from "./route-definition.js";
19
- export type { TransitionConfig, TransitionWhenFn, TransitionWhenContext, ViewTransitionClass, } from "./types.js";
19
+ export type { TransitionConfig, TransitionWhenFn, TransitionWhenContext, TransitionWhenKind, RouteLocation, ServerRouteLocation, ViewTransitionClass, } from "./types.js";
20
20
  export type { RouteUseItem, LayoutUseItem, AllUseItems, UseItems, HandlerUseItem, } from "./route-types.js";
21
21
  export type { ResponseHandler, ResponseHandlerContext, JsonResponseHandler, TextResponseHandler, JsonValue, ResponsePathFn, JsonResponsePathFn, TextResponsePathFn, RouteResponse, ProblemDetails, PartialPrerenderProps, } from "./urls.js";
22
22
  export type { MiddlewareContext, CookieOptions } from "./router/middleware.js";
@@ -9,7 +9,7 @@
9
9
  * in RSC context, while the regular index.ts is used in client components.
10
10
  */
11
11
  export { RouteNotFoundError, DataNotFoundError, notFound, MiddlewareError, HandlerError, BuildError, DslContextError, InvalidHandlerError, RouterError, Skip, isSkip, } from "./index.js";
12
- export type { DocumentProps, DefaultEnv, RouteDefinition, RouteConfig, RouteDefinitionOptions, TrailingSlashMode, Handler, HandlerContext, ExtractParams, GenericParams, Middleware, RevalidateParams, Revalidate, ActionRef, IsActionFn, RouteKeys, LoaderDefinition, LoaderFn, LoaderContext, LoaderOptions, FetchableLoaderOptions, LoadOptions, ErrorInfo, ErrorBoundaryFallbackProps, ErrorBoundaryHandler, ClientErrorBoundaryFallbackProps, NotFoundInfo, NotFoundBoundaryFallbackProps, NotFoundBoundaryHandler, ErrorPhase, OnErrorContext, OnErrorCallback, TransitionConfig, TransitionWhenFn, TransitionWhenContext, ViewTransitionClass, } from "./types.js";
12
+ export type { DocumentProps, DefaultEnv, RouteDefinition, RouteConfig, RouteDefinitionOptions, TrailingSlashMode, Handler, HandlerContext, ExtractParams, GenericParams, Middleware, RevalidateParams, Revalidate, ActionRef, IsActionFn, RouteKeys, LoaderDefinition, LoaderFn, LoaderContext, LoaderOptions, FetchableLoaderOptions, LoadOptions, ErrorInfo, ErrorBoundaryFallbackProps, ErrorBoundaryHandler, ClientErrorBoundaryFallbackProps, NotFoundInfo, NotFoundBoundaryFallbackProps, NotFoundBoundaryHandler, ErrorPhase, OnErrorContext, OnErrorCallback, TransitionConfig, TransitionWhenFn, TransitionWhenContext, TransitionWhenKind, RouteLocation, ServerRouteLocation, ViewTransitionClass, } from "./types.js";
13
13
  export type { RangoOptions, SSRStreamMode, SSROptions, ResolveStreamingContext, } from "./router.js";
14
14
  export type { OriginCheckConfig, OriginCheckContext, OriginCheckPhase, } from "./rsc/origin-guard.js";
15
15
  export type { ShellCaptureDebug, ShellCaptureDebugEvent, } from "./rsc/shell-capture.js";
@@ -4,9 +4,9 @@
4
4
  * Runs in the RSC realm of the build's temp server, AFTER all bundles are
5
5
  * written (the prelude embeds built client asset URLs — bootstrap module,
6
6
  * chunk preloads — that only exist post-client-build). The capture core is
7
- * producer A's, verbatim: deriveShellCaptureContext (mask funnel, liveness,
8
- * snapshot recording, implicit doc-cache scope) + captureAndStoreShell (gates,
9
- * quiesce, tags union, putShell barrier). Build capture first replays global
7
+ * producer A's, verbatim: deriveShellCaptureContext (push funnel, snapshot
8
+ * recording, implicit doc-cache scope) + settleCaptureRecord (the record-first
9
+ * step) + captureAndStoreShell (gates, quiesce, tags union, putShell barrier). Build capture first replays global
10
10
  * and route middleware with a synthetic build request context
11
11
  * (`ctx.build === true`, inert `ctx.waitUntil()`); middleware can seed vars or
12
12
  * call `ctx.dynamic()` to skip this URL. The sink is an entry collector instead
@@ -95,10 +95,17 @@ export interface BuildShellCaptureResult {
95
95
  tags?: string[];
96
96
  /** On route-mismatch: what this router's match actually landed on. */
97
97
  matchedRouteName?: string;
98
+ /**
99
+ * On no-shell: the attempt ran out of ppr.captureTimeout. It is not
100
+ * retried: its match may still be running, and the retry would start the
101
+ * same work beside it (runtime twin: shell-capture.ts runShellCapture).
102
+ */
103
+ timedOut?: true;
98
104
  }
99
105
  /**
100
106
  * Capture the PPR shell for one prerendered URL at build time. Retries once
101
107
  * in place on `no-shell` (the first attempt warms the temp server's SSR/Flight
102
- * transform graph, mirroring producer A's cold-start retry — same delay).
108
+ * transform graph, mirroring producer A's cold-start retry — same delay),
109
+ * unless the attempt ran out of ppr.captureTimeout (`timedOut`).
103
110
  */
104
111
  export declare function captureShellForBuild(opts: BuildShellCaptureOptions): Promise<BuildShellCaptureResult>;
@@ -110,7 +110,7 @@ export type RouteHelpers<T extends RouteDefinition, TEnv> = {
110
110
  *
111
111
  * // Conditional activation via the config object's `when` selector
112
112
  * intercept("@modal", "card", <CardModal />, {
113
- * when: ({ from }) => from.pathname.startsWith("/board"),
113
+ * when: ({ from }) => from.url.pathname.startsWith("/board"),
114
114
  * })
115
115
  *
116
116
  * // Config + other use-items: config is arg 4, use is arg 5
@@ -118,7 +118,7 @@ export type RouteHelpers<T extends RouteDefinition, TEnv> = {
118
118
  * "@modal",
119
119
  * "card",
120
120
  * <CardModal />,
121
- * { when: ({ from }) => from.pathname.startsWith("/board") },
121
+ * { when: ({ from }) => from.url.pathname.startsWith("/board") },
122
122
  * () => [loader(CardDetailLoader)],
123
123
  * )
124
124
  * ```
@@ -387,12 +387,12 @@ export type RouteHelpers<T extends RouteDefinition, TEnv> = {
387
387
  * transition({}) is startTransition + ViewTransition under the default and
388
388
  * startTransition only when the router sets viewTransition: false.
389
389
  *
390
- * Conditional hold: pass `when: (ctx) => boolean` to gate the transition per
391
- * request. It normally runs server-side after the route handler. On a `ppr`
392
- * route it is automatically hoisted before route handlers and reevaluated on
393
- * every replay, so it may read URL/params/action/env and middleware-set context
394
- * but not handler-set context. Returning false drops this transition for the
395
- * request. This is distinct from intercept()'s match-time `when` selector.
390
+ * Conditional hold: pass `when` to gate the navigation in the browser,
391
+ * inline (the build hoists the literal into a client module) or as a
392
+ * "use client" export. It runs once per
393
+ * navigation with { kind, from, to, isAction, action }, never on the server;
394
+ * false makes the commit urgent (no hold, no view transition). This is
395
+ * distinct from intercept()'s match-time `when` selector.
396
396
  *
397
397
  * ```typescript
398
398
  * // Attach to a single route
@@ -411,15 +411,15 @@ export type RouteHelpers<T extends RouteDefinition, TEnv> = {
411
411
  * transition({ viewTransition: false }),
412
412
  * ])
413
413
  *
414
- * // Hold only when the handler decided to (post-handler predicate):
414
+ * // Hold only when arriving from the list (a browser predicate):
415
415
  * path("/product/:id", ProductPage, { name: "product" }, () => [
416
- * transition({ when: (ctx) => ctx.get(KeepScroll) === true }),
416
+ * transition({ when: ({ from }) => from.routeName === "products.list" }),
417
417
  * ])
418
418
  * ```
419
419
  * @param config - ViewTransition configuration (enter, exit, update, share,
420
420
  * default, name), `viewTransition: "auto" | false` to toggle the router
421
421
  * boundary (createRouter({ viewTransition }) sets the app-wide default), and
422
- * `when: (ctx) => boolean` to gate the transition per request
422
+ * `when` to gate the transition per navigation (a browser predicate)
423
423
  * @param children - Optional callback returning child routes to wrap
424
424
  */
425
425
  transition: {
@@ -17,6 +17,13 @@ export declare const INTERNAL_INCLUDE_SCOPE_PREFIX = "$prefix_";
17
17
  * be preserved.
18
18
  */
19
19
  export declare function isAutoGeneratedRouteName(name: string): boolean;
20
+ /**
21
+ * The route name user code may see: a user-defined name, or undefined for an
22
+ * internal one (unnamed path(), hidden include scope). Internal names are
23
+ * route keys, not API, and must not leak into transition({ when }) or
24
+ * intercept({ when }) locations.
25
+ */
26
+ export declare function publicRouteName(routeKey: string | undefined): string | undefined;
20
27
  /**
21
28
  * Validate that a user-provided route name does not collide with
22
29
  * reserved internal prefixes. Checks every dot-separated segment,
@@ -5,7 +5,7 @@
5
5
  * Also includes the shared invokeOnError utility for error callback invocation.
6
6
  */
7
7
  import { type ReactNode } from "react";
8
- import type { EntryData } from "../server/context";
8
+ import type { EntryData } from "../server/context.js";
9
9
  import type { ResolvedSegment, ErrorInfo, ErrorBoundaryHandler, NotFoundInfo, NotFoundBoundaryHandler, ErrorPhase, OnErrorCallback } from "../types";
10
10
  /**
11
11
  * Context required to invoke the onError callback.
@@ -163,6 +163,14 @@ export declare function observePhase<T>(spec: PhaseSpec, fn: (span: TraceSpan) =
163
163
  * nothing to record here and short-circuits.
164
164
  */
165
165
  export declare function observeHandler<C, R>(id: string, handler: (ctx: C) => R, ctx: C): R;
166
+ /**
167
+ * observeHandler, inside segment `segmentId`'s tag scope, so the tags the
168
+ * handler records reach the route's cache() record (#957, cache-tag.ts
169
+ * runInSegmentTagScope). `spanId` is observeHandler's `id`. A request that
170
+ * cannot write a record takes observeHandler's path as is, with the same one
171
+ * request-context read: no scope, no closure.
172
+ */
173
+ export declare function observeSegmentHandler<C, R>(segmentId: string, spanId: string, handler: (ctx: C) => R, ctx: C): R;
166
174
  /**
167
175
  * Emit one discrete telemetry event (the event-shaped counterpart to
168
176
  * observePhase). Resolves the sink from the active router context and stamps the
@@ -11,6 +11,8 @@ import type { SegmentResolutionDeps } from "./types.js";
11
11
  * Check if an intercept's when conditions are satisfied.
12
12
  * All when() functions must return true for the intercept to activate.
13
13
  * If no when() conditions are defined, the intercept always activates.
14
+ * A selector that throws yields the conservative default, no intercept (the
15
+ * full page renders), and is logged; it never fails the request.
14
16
  *
15
17
  * During action revalidation, when() is NOT evaluated.
16
18
  */
@@ -151,6 +151,11 @@ export interface MatchContext<TEnv = any> {
151
151
  interceptSelectorContext: InterceptSelectorContext;
152
152
  isSameRouteNavigation: boolean;
153
153
  interceptResult: InterceptResult | null;
154
+ /**
155
+ * An intercept targets this route, matched or not (a same-route navigation
156
+ * skips it), so the response depends on the source. Partial matches only.
157
+ */
158
+ interceptTargeted?: boolean;
154
159
  cacheScope: CacheScope | null;
155
160
  isIntercept: boolean;
156
161
  actionContext?: ActionContext;
@@ -126,10 +126,6 @@ import { type RequestContext } from "../../server/request-context.js";
126
126
  * cache-scope.ts), so a swap sent live pushes into this render.
127
127
  * setupLoaderAccess binds the store at setup, so it runs inside the
128
128
  * derived context too.
129
- * - _transitionWhen: fresh resolution appends transition({ when })
130
- * predicates; the foreground's gateTransitions reads the array after the
131
- * match, and a stale HIT (which replays the stored transition) must not
132
- * evaluate this render's predicates.
133
129
  * - _metricsStore: undefined, and the DSL store below has `metrics` unset
134
130
  * (track() reads that one), so nothing lands on the foreground's perf
135
131
  * timeline. ctx.Store.run closes over ctx.Store, so the derived store
@@ -48,7 +48,12 @@ export interface MiddlewareContext<TEnv = any, TParams = Record<string, string |
48
48
  header(name: string, value: string): void;
49
49
  routeName?: DefaultRouteName;
50
50
  debugPerformance(): void;
51
- theme?: Theme;
51
+ /**
52
+ * The visitor's theme (cookie or default); undefined without theme config.
53
+ * Guarded like `cookies()`: a read inside a `"use cache"` function throws
54
+ * (#971).
55
+ */
56
+ readonly theme?: Theme;
52
57
  setTheme?: (theme: Theme) => void;
53
58
  setLocationState(entries: LocationStateEntry | LocationStateEntry[]): void;
54
59
  /** Global route names only; see {@link GlobalReverseFunction}. */
@@ -481,7 +481,10 @@ export interface RangoOptions<TEnv = any> {
481
481
  *
482
482
  * Controls how long prefetch responses are kept in the client-side
483
483
  * in-memory cache and sets `Cache-Control: private, max-age=<ttl>`
484
- * on server responses for CDN/edge caching.
484
+ * on prefetch responses, so the browser's HTTP cache may reuse them
485
+ * (`private`: shared caches such as a CDN do not store them). A route an
486
+ * intercept targets gets no prefetch `Cache-Control`: whether its
487
+ * response is the modal or the full page depends on the source page.
485
488
  *
486
489
  * The cache is automatically invalidated on server actions regardless
487
490
  * of TTL, so this is primarily a staleness safety net.
@@ -6,7 +6,8 @@
6
6
  * getItem()/setItem() methods on SegmentCacheStore.
7
7
  *
8
8
  * Cache key resolution (3-tier, matching CacheScope.resolveKey):
9
- * 1. options.key(requestCtx) — full override
9
+ * 1. options.key(requestCtx) — full override, stored namespaced as
10
+ * loader:{loaderId}:key:{encodeURIComponent(result)} (#1009)
10
11
  * 2. store.keyGenerator(requestCtx, defaultKey) — store-level modification
11
12
  * 3. loader:{loaderId}:{host}{pathname}:{sortedParams} — default
12
13
  *
@@ -17,15 +18,55 @@
17
18
  * On stale hit (SWR): returns stale data, schedules background revalidation.
18
19
  * On miss: executes loader, schedules non-blocking cache write.
19
20
  *
21
+ * Identity (#972): without a key() or a store keyGenerator the entry is
22
+ * shared across users, so a fill whose execution recorded a request-identity
23
+ * read fails and is not stored, whoever started that execution
24
+ * (server/context.ts recordLoaderIdentityRead).
25
+ *
20
26
  * Handle pushes (`ctx.use(Handle)(...)` in the loader body) are a side effect
21
27
  * the HIT must reproduce: the MISS records the pushes of the body and of the
22
28
  * loaders it awaits via ctx.use into the entry's `handles` blob (the
23
29
  * "use cache" capture/encode), and every HIT — stale included — replays
24
30
  * them, each loader's pushes at most once per request (a dependency another
25
31
  * reader already ran keeps its live pushes). See replayLoaderHandles.
32
+ *
33
+ * Tags too (#964): the entry stores its cache() tags plus every tag its
34
+ * execution recorded, those of the loader values it read included
35
+ * (cache-tag.ts "Recorded-tag sets"), so updateTag() of any of them drops it.
36
+ * Every HIT records the stored tags through the loader's tag owner
37
+ * (recordLoaderTags), so a route cache() record, PPR shell or document built
38
+ * over the HIT carries them, and a loader that reads this one takes them on.
39
+ * Once the binding started, a loader body's ctx.use of it gets the same
40
+ * dataPromise as a handler read (createLoaderExecutor's useLoader); a stale
41
+ * refresh does not (setupLoaderAccess _runLoaderIsolated).
26
42
  */
27
- import { type LoaderEntry } from "../../server/context.js";
43
+ import { type EntryData, type LoaderEntry } from "../../server/context.js";
28
44
  import type { HandlerContext } from "../../types.js";
45
+ /**
46
+ * Whether a request over the matched chain `entries` resolves a loader bound
47
+ * with its own cache(): on a chain entry, or on its parallel slots, orphan
48
+ * layouts or intercepts. The match arms the per-execution loader tag sets on
49
+ * it (cache-tag.ts armLoaderTagSets), before any loader starts, so a
50
+ * dependency the handler or another binding starts first still records.
51
+ * Memoized per entry.
52
+ */
53
+ export declare function bindsLoaderCache(entries: readonly EntryData[]): boolean;
54
+ /**
55
+ * The ids of the loaders a request over the matched chain `entries` runs on
56
+ * the live lane (no `ssr: false`), on a chain entry, its orphan layouts or
57
+ * its parallel slots. A document HIT tail restores the record's pushes of
58
+ * every other owner as authoritative (withCacheLookup, HandleStore
59
+ * pushRestored); a live-lane loader is a hole, so its live run replaces its
60
+ * recorded pushes instead. Memoized per entry.
61
+ */
62
+ export declare function liveLaneLoaderIds(entries: readonly EntryData[]): ReadonlySet<string>;
63
+ /**
64
+ * The lane of each loader the matched chain `entries` registers: "live" (no
65
+ * `ssr: false`) or "bake". A loader registered on both is "live". A PPR
66
+ * capture credits a push to the first registered loader around it when that
67
+ * one is live (shell-capture.ts deriveShellCaptureContext).
68
+ */
69
+ export declare function routeLoaderLanes(entries: readonly EntryData[]): ReadonlyMap<string, "live" | "bake">;
29
70
  /**
30
71
  * Resolve loader data with optional caching.
31
72
  *
@@ -46,18 +87,23 @@ import type { HandlerContext } from "../../types.js";
46
87
  * never run under a shell capture), so the lane is decided here, per LOADER,
47
88
  * never by the entry's loading():
48
89
  *
49
- * - BAKE lane: `loader(Def, { ssr: false })` (awaitBeforeFlush) with a
50
- * `bakeSegmentKey` from the caller. The loader EXECUTES at capture (the
90
+ * - BAKE lane: `loader(Def, { ssr: false })` (LoaderEntry `bake`: the
91
+ * caller passes a `bakeSegmentKey` for it on document and navigation
92
+ * evaluations alike; a capture, a document evaluation, also has
93
+ * awaitBeforeFlush). The loader EXECUTES at capture (the
51
94
  * flag's "data in the HTML before first flush" maps to the frozen prelude)
52
95
  * and its settled non-promise data bakes into the shell. Promises nested in
53
- * plain objects/arrays are masked (mask-nested.ts) and stay live holes at
54
- * the consumer's own Suspense — per-request material must be promise-shaped
55
- * (the #692 cross-session scar). The masked container registers on
56
- * `_shellCaptureLoaderRecords`: it holds the capture gate (bounded by
57
- * `ppr.captureTimeout`) and pins into the snapshot's loader family, and on a
58
- * HIT the recorded container is overlaid onto the fresh run (below) so the
59
- * payload matches the frozen prelude byte-for-byte. cookies()/headers() in
60
- * its body (nested promise bodies included) trip the capture guard.
96
+ * plain objects, arrays and JSX props are masked (mask-nested.ts) and stay
97
+ * live holes at the consumer's own Suspense — per-request material must be
98
+ * promise-shaped (the #692 cross-session scar). The masked container
99
+ * registers on `_shellCaptureLoaderRecords`: it holds the capture gate
100
+ * (bounded by `ppr.captureTimeout`) and pins into the snapshot's loader
101
+ * family, and on a HIT the recorded container is overlaid onto the fresh
102
+ * run (below) so the payload matches the frozen prelude outside the holes.
103
+ * A server-component element holding a promise is a hole as a whole, so its
104
+ * other props come from the fresh run (loader-snapshot.ts
105
+ * elideLoaderContainer). cookies()/headers() in its body (nested promise
106
+ * bodies included) trip the capture guard.
61
107
  * - LIVE lane: every other loader, whatever its entry's loading(). Never
62
108
  * executes at capture: the slot gets a never-resolving promise
63
109
  * (loader-mask.ts) and postpones at the reader's boundary — loading() or an
@@ -8,10 +8,9 @@
8
8
  * fresh into it.
9
9
  *
10
10
  * Capture mode is signalled by `requestCtx._shellCaptureRun`, set to true ONLY on
11
- * the derived request context of the background capture task (shell-capture.ts) —
12
- * NOT by the foreground render, whose `_shellCapture` descriptor merely means "a
13
- * capture is wanted" and must not change behavior. This module is the single home
14
- * for the mask so every loader execution site gates the same way (loader-cache.ts
11
+ * the derived request context of the background capture task (shell-capture.ts);
12
+ * the foreground render never sets it. This module is the single home for the
13
+ * mask so every loader execution site gates the same way (loader-cache.ts
15
14
  * `resolveLoaderData`, fresh.ts `resolveLoaders`).
16
15
  */
17
16
  import { type RequestContext } from "../../server/request-context.js";