@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
@@ -10,8 +10,10 @@
10
10
  import type { PartialCacheOptions } from "../types.js";
11
11
  import type { ResolvedSegment } from "../types.js";
12
12
  import type { SegmentCacheStore } from "./types.js";
13
+ import { type ExecutionStart } from "./tag-invalidation.js";
14
+ import { type OwnedPushDelivery } from "./handle-snapshot.js";
13
15
  import type { RequestContext } from "../server/request-context.js";
14
- export declare function resolveCacheTags(config: PartialCacheOptions | false, ctx: RequestContext | undefined): string[] | undefined;
16
+ type CacheKeyFn = NonNullable<PartialCacheOptions["key"]>;
15
17
  /**
16
18
  * Discriminated outcome of a route cache lookup — see
17
19
  * {@link CacheScope.lookupRouteDetailed} for what each status licenses.
@@ -52,10 +54,58 @@ export declare class CacheScope {
52
54
  * a ppr route).
53
55
  */
54
56
  readonly boundary?: string | undefined;
57
+ /**
58
+ * Awaited on every lookup HIT: the implicit doc scope's marker `onHit`
59
+ * (createShellImplicitDocScope), which arms a partial replay's bake-lane
60
+ * loader pins before the route's loaders resolve.
61
+ */
62
+ private readonly onHit?;
63
+ /**
64
+ * The record key of the implicit doc scope of a document HIT tail
65
+ * (createShellImplicitDocScope): the shell entry's own `docKey`, used as
66
+ * is. Not a `key()`, which would be namespaced (#975) and miss the
67
+ * record.
68
+ */
69
+ private readonly fixedKey?;
55
70
  readonly config: PartialCacheOptions | false;
56
71
  readonly parent: CacheScope | null;
57
72
  /** Explicit store from cache() options, if specified */
58
73
  private readonly explicitStore;
74
+ /**
75
+ * @internal The `key()` functions that partition this scope's records,
76
+ * outermost first: every cache() on the parent chain that sets one, this
77
+ * scope's last (#970). `cache(false)` adds none and does not cut the chain,
78
+ * so a cache() re-enabled under it stays in the partition. Before, the
79
+ * innermost config alone decided the key: an inner cache() without `key()`
80
+ * wrote under the default key and an inner `key()` dropped the outer one,
81
+ * so a request in one partition HIT another partition's inner records.
82
+ */
83
+ readonly keyFns: readonly CacheKeyFn[];
84
+ /** Whether this scope's own config sets `key()` (the last of keyFns). */
85
+ private readonly hasOwnKey;
86
+ /**
87
+ * The `condition()` predicates gating this scope, outermost first: every
88
+ * cache() on the parent chain that sets one, this scope's last (#974). A
89
+ * read or write needs all of them to allow it (conditionAllows). Before,
90
+ * only the innermost config's predicate ran, so an outer condition()
91
+ * refusing a request still let a nested cache() read and write.
92
+ */
93
+ private readonly conditions;
94
+ /**
95
+ * The `tags` options of this scope and every cache() on the parent chain,
96
+ * outermost first (#974): a nested record carries them all (resolveTags),
97
+ * so `updateTag(outerTag)` evicts it and the shells built from it.
98
+ */
99
+ private readonly tagOptions;
100
+ /**
101
+ * The enclosing enabled scopes without a `key()` of their own whose
102
+ * `store` option differs from this scope's, outermost first: a store
103
+ * keyGenerator of theirs partitions this scope's records too (#974,
104
+ * resolvePartitionParts). On the same store the keyGenerator already
105
+ * applies through this scope's own default key, or its own `key()`
106
+ * overrides it (#970).
107
+ */
108
+ private readonly storeScopes;
59
109
  constructor(config: PartialCacheOptions | false, parent?: CacheScope | null, defaultKeyPrefix?: "doc" | undefined,
60
110
  /**
61
111
  * @internal shortCode of the outermost cache() entry of this scope's
@@ -64,7 +114,20 @@ export declare class CacheScope {
64
114
  * Undefined covers the whole matched chain (the implicit shell doc scope,
65
115
  * a ppr route).
66
116
  */
67
- boundary?: string | undefined);
117
+ boundary?: string | undefined,
118
+ /**
119
+ * Awaited on every lookup HIT: the implicit doc scope's marker `onHit`
120
+ * (createShellImplicitDocScope), which arms a partial replay's bake-lane
121
+ * loader pins before the route's loaders resolve.
122
+ */
123
+ onHit?: (() => void | Promise<void>) | undefined,
124
+ /**
125
+ * The record key of the implicit doc scope of a document HIT tail
126
+ * (createShellImplicitDocScope): the shell entry's own `docKey`, used as
127
+ * is. Not a `key()`, which would be namespaced (#975) and miss the
128
+ * record.
129
+ */
130
+ fixedKey?: string | undefined);
68
131
  /**
69
132
  * @internal Whether a segment belongs to this scope's cache entry. ShortCodes are
70
133
  * hierarchical (server/context.ts getShortCode: parent shortCode + include
@@ -106,6 +169,71 @@ export declare class CacheScope {
106
169
  * @internal
107
170
  */
108
171
  private resolveKey;
172
+ /**
173
+ * A shell captured from a route record this request read or wrote must not
174
+ * outlive it (noteRouteRecordWindow). The implicit doc scope's own record
175
+ * is the shell's, so it notes nothing.
176
+ */
177
+ private noteRecordWindow;
178
+ /**
179
+ * @internal This scope's record key for `defaultKey`, resolved once per
180
+ * request: a ppr route's shell partition (resolvePartition) and the record
181
+ * lookup share the `key()` runs, and a rejection is shared too.
182
+ *
183
+ * With partition parts on the chain (resolvePartitionParts: the `key()`
184
+ * results, and the keyGenerator results of enclosing scopes on another
185
+ * store), they are composed by composeCacheKeys, which namespaces every
186
+ * `key()` result (#975), so a nested cache() keys its records within the
187
+ * enclosing partition (#970, #974). A scope whose own config sets no
188
+ * `key()` appends its own default key (resolveDefaultKey). Reusing the
189
+ * outer `key()` result alone would give every route under the inner
190
+ * cache() one record: a `key()` replaces the whole default key, so an
191
+ * outer key that names no route (`tier:gold`) would make `/a` HIT `/b`'s
192
+ * record. A scope with its own `key()` adds no default key: a `key()` is a
193
+ * full override, and its store's keyGenerator does not run. With no
194
+ * partition part it is resolveDefaultKey alone. With a `key()` on the
195
+ * chain but no request context it rejects (keyWithoutContextError).
196
+ * `partitionedPrefix` prefixes a key built from partition parts only (the
197
+ * response family's `response:`).
198
+ *
199
+ * Each `key()` result lives on the request context (`_resolvedCacheKeys`),
200
+ * by function: it runs once per request whatever the default key
201
+ * (document, partial or `doc` shell prefix) and however many scopes share
202
+ * it (sibling routes under one keyed cache()). A shell capture, whose
203
+ * context is `Object.create` of the request's (shell-capture.ts), reads the
204
+ * foreground's results through the prototype chain and never runs `key()`
205
+ * itself: a `key()` calling `cookies()` would trip the capture guard there,
206
+ * and a capture must store the partition its request resolved.
207
+ */
208
+ resolveKeyFrom(defaultKey: string, partitionedPrefix?: string): Promise<string>;
209
+ /**
210
+ * @internal The request partition of a ppr route's shell under this scope
211
+ * (resolveShellPartition): its partition parts (resolvePartitionParts),
212
+ * composed like the record key but without the default key, which the
213
+ * URL-based shell key already carries. A scope without its own `key()`
214
+ * whose store has a keyGenerator adds that result when it differs from the
215
+ * `doc` default key, as an unkeyed scope's partition always did: the
216
+ * record is split by it, so the shell must be too. Undefined
217
+ * (synchronously, with no work) when nothing can partition; null when
218
+ * nothing does.
219
+ */
220
+ resolvePartition(pathname: string, params: Record<string, string> | undefined): Promise<string | null> | undefined;
221
+ /** Whether a partition part can apply: a `key()` or a store scope above. */
222
+ private get mayPartition();
223
+ /**
224
+ * The parts partitioning this scope's records, outermost first: the
225
+ * chain's `key()` results (each once per request, memoizedKey) and the
226
+ * keyGenerator result of each enclosing scope on another store (once per
227
+ * keyGenerator, #974), null where it is `defaultKey`. The callers keep those
228
+ * positions (positionalParts): a result equal to the default key
229
+ * partitions nothing only when every one does (#970).
230
+ */
231
+ private resolvePartitionParts;
232
+ /**
233
+ * This scope's store keyGenerator result for `defaultKey` (once per request
234
+ * and default key), else `defaultKey` (resolveCacheKey).
235
+ */
236
+ private resolveDefaultKey;
109
237
  /**
110
238
  * @internal Whether a cache read/write is allowed for the current request:
111
239
  * the scope is enabled AND its `condition` (if any) returns true. "read"
@@ -128,11 +256,11 @@ export declare class CacheScope {
128
256
  */
129
257
  get isShellImplicitDocScope(): boolean;
130
258
  /**
131
- * Evaluate the cache `condition` predicate. Returns false (skip the cache
132
- * operation) when the predicate returns false or throws; returns true when
133
- * there is no condition or no request context to evaluate it against.
134
- */
135
- /**
259
+ * Evaluate the cache `condition` predicates of this scope and every
260
+ * enclosing cache() (`conditions`, #974), outermost first. Returns false
261
+ * (skip the cache operation) when one returns false or throws; returns
262
+ * true when there is none or no request context to evaluate them against.
263
+ *
136
264
  * One WRITE decision per (scope, request), memoized on the request context.
137
265
  * A capture render has TWO writers consulting the same predicate — the
138
266
  * explicit tier's cacheRoute and the snapshot-only doc record gate
@@ -144,6 +272,12 @@ export declare class CacheScope {
144
272
  * at the replay gate was the round-2 regression.
145
273
  */
146
274
  private readonly writeConditionMemo;
275
+ /**
276
+ * Evaluate the cache `condition` predicate. Returns false (skip the cache
277
+ * operation) when the predicate returns false or throws; returns true when
278
+ * there is no condition or no request context to evaluate it against. A
279
+ * write decision is memoized (writeConditionMemo).
280
+ */
147
281
  private conditionAllows;
148
282
  /**
149
283
  * Lookup cached segments for a route (single cache entry per request).
@@ -152,10 +286,11 @@ export declare class CacheScope {
152
286
  * @param pathname - URL pathname for cache key generation
153
287
  * @param params - Route params for cache key generation
154
288
  * @param isIntercept - Whether this is an intercept navigation (uses different cache key)
155
- * @param claimLoaderPushes - The handler context's _claimLoaderPushes, for
156
- * restoreHandles' loader-owned values
289
+ * @param ownedPushes - How restoreHandles delivers the record's
290
+ * loader-owned values (withCacheLookup: the handler context's
291
+ * _claimLoaderPushes, and on a document HIT tail the owners it restores)
157
292
  */
158
- lookupRoute(pathname: string, params: Record<string, string>, isIntercept?: boolean, claimLoaderPushes?: (loaderId: string) => boolean): Promise<{
293
+ lookupRoute(pathname: string, params: Record<string, string>, isIntercept?: boolean, ownedPushes?: OwnedPushDelivery): Promise<{
159
294
  segments: ResolvedSegment[];
160
295
  shouldRevalidate: boolean;
161
296
  } | null>;
@@ -177,7 +312,7 @@ export declare class CacheScope {
177
312
  * A corrupt entry that was evicted reads as `miss`: the store was consulted
178
313
  * and holds nothing servable, which is the post-eviction truth.
179
314
  */
180
- lookupRouteDetailed(pathname: string, params: Record<string, string>, isIntercept?: boolean, claimLoaderPushes?: (loaderId: string) => boolean): Promise<CacheRouteLookupOutcome>;
315
+ lookupRouteDetailed(pathname: string, params: Record<string, string>, isIntercept?: boolean, ownedPushes?: OwnedPushDelivery): Promise<CacheRouteLookupOutcome>;
181
316
  /**
182
317
  * Record this scope's segment-DSL cache({ tags }) into the request tag union
183
318
  * synchronously, under the same gate cacheRoute() uses for a write.
@@ -193,6 +328,12 @@ export declare class CacheScope {
193
328
  * (the tag union is a Set), so the duplicate record in cacheRoute is harmless.
194
329
  */
195
330
  recordTags(requestCtx: RequestContext | undefined): void;
331
+ /**
332
+ * @internal The `tags` of this scope and every enclosing cache()
333
+ * (`tagOptions`, #974), static and function forms, for `ctx`: the tags its
334
+ * records carry. Undefined when none resolve (a disabled scope has none).
335
+ */
336
+ resolveTags(ctx: RequestContext | undefined): string[] | undefined;
196
337
  /**
197
338
  * Cache all segments for a route (non-blocking via waitUntil)
198
339
  * Single cache entry per route request.
@@ -202,9 +343,29 @@ export declare class CacheScope {
202
343
  * @param params - Route params for cache key generation
203
344
  * @param segments - All resolved segments to cache
204
345
  * @param isIntercept - Whether this is an intercept navigation (uses different cache key)
346
+ * @param start - Where the render of `segments` started: the record is not
347
+ * written when one of its tags was invalidated since (#977,
348
+ * predatesInvalidation). A shell capture's doc record passes none: it
349
+ * lives only in the shell entry, which putShell gates by its own start.
205
350
  */
206
- cacheRoute(pathname: string, params: Record<string, string>, segments: ResolvedSegment[], isIntercept?: boolean): Promise<void>;
351
+ cacheRoute(pathname: string, params: Record<string, string>, segments: ResolvedSegment[], isIntercept?: boolean, start?: ExecutionStart): Promise<void>;
207
352
  }
353
+ /**
354
+ * @internal The request partition of a ppr route's shell: with a route
355
+ * cache() scope, its CacheScope.resolvePartition (the chain's `key()`
356
+ * results, namespaced and composed (#975), plus the keyGenerator results of
357
+ * enclosing scopes on another store (#974) and of a scope without its own
358
+ * `key()` (#970)); with none, the app store's keyGenerator result
359
+ * given the document default key. Undefined (synchronously, with no work)
360
+ * when neither applies: the shell key stays host + path + filtered search.
361
+ * Resolves to null when the partition is the default key itself (a
362
+ * keyGenerator that returns it unchanged): that partitions nothing, so the
363
+ * shell stays unpartitioned and keeps its build shell. The shell's key, its
364
+ * read and capture, and partial replay all use it, so a visitor is only
365
+ * served its own partition. A rejected promise is a failed key resolution:
366
+ * the caller serves no shell.
367
+ */
368
+ export declare function resolveShellPartition(routeScope: CacheScope | null | undefined, appStore: SegmentCacheStore | null | undefined, pathname: string, params: Record<string, string> | undefined): Promise<string | null> | undefined;
208
369
  /**
209
370
  * Create a cache scope from entry's cache config. `shortCode` is the cache()
210
371
  * entry's: a scope nested in an enabled parent keeps the parent's boundary
@@ -221,24 +382,29 @@ type ShellImplicitCacheMarker = NonNullable<RequestContext["_shellImplicitCache"
221
382
  * {@link resolveShellImplicitCacheScope} (routes that derived no scope) and
222
383
  * the explicit-scope composition sites (capture doc record in the cache-store
223
384
  * middleware, seeded replay fallback in withCacheLookup) so both ends of the
224
- * shell contract resolve the SAME canonical document key.
385
+ * shell contract resolve the SAME canonical document key. A document HIT
386
+ * tail's marker carries the entry's own key (`fixedDocKey`), which wins over
387
+ * key resolution.
225
388
  */
226
389
  export declare function createShellImplicitDocScope(marker: ShellImplicitCacheMarker): CacheScope;
227
390
  /**
228
- * Shell fast path: when the route tree derived NO cache scope and the current
229
- * request context carries the `_shellImplicitCache` marker (a shell capture,
230
- * an eligible HIT tail, or a normal partial navigation replay), substitute an
231
- * implicit doc-level scope so withCacheLookup/withCacheStore treat the WHOLE
232
- * matched route as a cache() boundary — the shell entry IS a cache() of the
233
- * handler layer, with loaders as the live carve-outs
234
- * (resolveFreshLoadersAndYield).
391
+ * The doc record scope: when the current request context carries the
392
+ * `_shellImplicitCache` marker (a shell capture, a document HIT tail, or a
393
+ * normal partial navigation replay), substitute an implicit doc-level scope
394
+ * so withCacheLookup/withCacheStore treat the WHOLE matched route as a
395
+ * cache() boundary — the shell entry IS a cache() of the handler layer, with
396
+ * loaders as the live carve-outs (resolveFreshLoadersAndYield).
235
397
  *
236
- * An existing scope — including an explicit cache(false) opt-out — always
237
- * wins HERE: the consumer's cache() semantics (their ttl/swr/store/condition)
238
- * are never overridden, and cache(false) keeps the tail on the full handler
239
- * re-run path. On the navigation-replay serve path the marker still composes
240
- * with an explicit scope downstream (withCacheLookup's seeded fallback after
241
- * an explicit-tier miss) — see `onExplicitHit` on `_shellImplicitCache`.
398
+ * A document HIT tail (`docTail`) always gets the implicit scope: it replays
399
+ * the shell's own record and never consults a route-derived scope, whose
400
+ * opt-outs the serve gate evaluated before the commit (shellServePlan). Every
401
+ * other marker leaves an existing scope — including an explicit cache(false)
402
+ * — in place: the consumer's cache() semantics (their ttl/swr/store/condition)
403
+ * are never overridden. On the navigation-replay serve path the marker still
404
+ * composes with an explicit scope downstream (withCacheLookup's seeded
405
+ * fallback after an explicit-tier miss) — see `onExplicitHit` on
406
+ * `_shellImplicitCache`; during a capture, recordShellCaptureDocRecord
407
+ * (cache-store.ts) writes the record for such a scope.
242
408
  */
243
409
  export declare function resolveShellImplicitCacheScope(scope: CacheScope | null): CacheScope | null;
244
410
  export {};
@@ -10,6 +10,61 @@
10
10
  * profile/DSL tags before storing.
11
11
  */
12
12
  import { type RequestContext } from "../server/request-context.js";
13
+ import { type LoaderIdentityRead } from "../server/context.js";
14
+ import type { ExecutionStart } from "./tag-invalidation.js";
15
+ /**
16
+ * Call `fn` outside any "use cache" tag scope: detached work a cached body
17
+ * schedules (a stale entry's background refresh) records nothing onto the
18
+ * entry being filled.
19
+ */
20
+ export declare function outsideCacheTagScope<T>(fn: () => T): T;
21
+ /**
22
+ * Arm the record tag owners for `ctx`: its match resolved a cache scope, so
23
+ * it can write a route cache() record (match-api.ts). A capture's or
24
+ * background re-render's derived context inherits the flag.
25
+ */
26
+ export declare function armRecordTagOwners(ctx?: RequestContext | undefined): void;
27
+ /** Whether `ctx` records tag owners (see armRecordTagOwners). */
28
+ export declare function recordTagOwnersArmed(ctx?: RequestContext | undefined): boolean;
29
+ /**
30
+ * Call `fn(arg)` inside segment `segmentId`'s tag scope: tags recorded while
31
+ * it or its async continuations run (outside a loader body) are owned by that
32
+ * segment. Unarmed, it is a plain call.
33
+ */
34
+ export declare function runInSegmentTagScope<R, A = undefined>(segmentId: string, fn: (arg: A) => R, arg?: A): R;
35
+ /**
36
+ * Record that the current owner reads loader `loaderId`'s value (ctx.use),
37
+ * so the loader's tags follow the reader onto a record. The DSL funnel
38
+ * starting a loader (resolveLoaderData, inside the DSL loader scope but no
39
+ * loader body) is not a reader.
40
+ */
41
+ export declare function linkLoaderTags(loaderId: string, ctx?: RequestContext | undefined): void;
42
+ /**
43
+ * Record `tags` as loader `loaderId`'s own, read outside its body (its
44
+ * cache() config tags, or a loader-cache HIT's stored tags):
45
+ * recordRequestTags with an explicit owner.
46
+ */
47
+ export declare function recordLoaderTags(loaderId: string, tags: Iterable<string> | undefined, ctx?: RequestContext | undefined): void;
48
+ /**
49
+ * The tags segment `segmentId`'s content recorded in `ctx`: its own, plus
50
+ * those of every loader it consumed, transitively.
51
+ */
52
+ export declare function getSegmentTags(ctx: RequestContext, segmentId: string): ReadonlySet<string>;
53
+ /**
54
+ * Record `tags` onto each of `segmentIds` (a route cache() HIT replaying
55
+ * those segments): a record written from the replay, such as a shell
56
+ * capture's doc record (match-middleware/cache-store.ts), then carries them
57
+ * as a fresh render's record would. Unarmed, a no-op.
58
+ */
59
+ export declare function recordSegmentTags(segmentIds: readonly string[], tags: Iterable<string> | undefined, ctx?: RequestContext | undefined): void;
60
+ /**
61
+ * The owner of the loaders a shell capture bakes (loader-cache.ts bake
62
+ * lane): their data is shell material that no handler reads, so their tags
63
+ * reach the shell through this owner (shell-capture.ts), not a record.
64
+ */
65
+ export declare const SHELL_BAKE_TAG_OWNER: string;
66
+ /** Link loader `loaderId`'s tags to `ownerId` (see SHELL_BAKE_TAG_OWNER). */
67
+ export declare function linkLoaderTagsTo(ownerId: string, loaderId: string, ctx?: RequestContext | undefined): void;
13
68
  export declare function normalizeTag(tag: string): string | null;
14
69
  export declare function normalizeTags(tags: Iterable<string>): string[];
15
70
  /**
@@ -32,14 +87,15 @@ export declare function normalizeTags(tags: Iterable<string>): string[];
32
87
  * cache" in its tree. This is PPR's DERIVATIVE invalidation: PPR is
33
88
  * execution-PRESERVING (everything still runs underneath; only document bytes
34
89
  * are shortcut), so its tags ride this existing instrument rather than a
35
- * first-class ppr key/tag API. The shell-expiry invariant holds by
36
- * construction — baked ⇒ evicts (bake-lane loaders execute during capture and
37
- * record here), hole ⇒ fresh (masked loaders behind a renderable loading()
38
- * never execute during capture, so nothing under a hole can tag the shell).
90
+ * first-class ppr key/tag API. Whatever renders into the shell records here,
91
+ * at capture or on the route's cache() record the capture replays. A masked
92
+ * live-lane loader never executes at capture, so its own tags reach a shell
93
+ * only through a handler that reads it (ctx.use).
39
94
  *
40
- * Inside a cache() DSL segment the render-callable form records at the DOCUMENT
41
- * level, not the segment (only the "use cache" runtime enters the tag scope) — a
42
- * documented semantic, not a filtered one. An empty/whitespace-only tag is
95
+ * Inside a route cache() boundary the render-callable form also tags that
96
+ * route's cache() record, which re-records it on every HIT (#957). A loader's
97
+ * tags reach the record only when a handler consumes its value (ctx.use);
98
+ * see runInSegmentTagScope / linkLoaderTags. An empty/whitespace-only tag is
43
99
  * dropped in both forms (the render-callable form silently, via normalizeTags in
44
100
  * recordRequestTags; the scope form with a dev warning).
45
101
  *
@@ -64,16 +120,65 @@ export declare function normalizeTags(tags: Iterable<string>): string[];
64
120
  export declare function cacheTag(...tags: string[]): void;
65
121
  export declare function recordRequestTags(tags: Iterable<string> | undefined, ctx?: RequestContext | undefined): void;
66
122
  /**
67
- * Run a function within a cache tag scope. Any cacheTag() calls inside `fn`
68
- * accumulate into the returned Set.
123
+ * Arm the per-execution loader tag sets for `ctx`: its match resolved a
124
+ * loader bound with its own cache() (match-api.ts, bindsLoaderCache), or such
125
+ * a binding started (loader-cache.ts). A loader-cache MISS captures its own
126
+ * execution and encodes either way.
127
+ */
128
+ export declare function armLoaderTagSets(ctx?: RequestContext | undefined): void;
129
+ /** Whether `ctx` records per-execution loader tag sets (armLoaderTagSets). */
130
+ export declare function loaderTagSetsArmed(ctx: RequestContext | undefined): boolean;
131
+ /**
132
+ * Call `fn`, adding every tag recorded in its async chain
133
+ * (recordRequestTags, recordLoaderTags) to `into`, until a loader body
134
+ * started inside it takes over with its own set.
135
+ */
136
+ export declare function captureRecordedTags<T>(into: Set<string>, fn: () => T): T;
137
+ /** `into` also answers for every tag `from` records. */
138
+ export declare function linkRecordedTags(into: Set<string>, from: Set<string>): void;
139
+ /** Mark a loader's value promise as carrying `tags` (see readValueTags). */
140
+ export declare function tagLoaderValue(value: object, tags: Set<string>): void;
141
+ /** A loader value's tags also answer for `tags` (tagLoaderValue). */
142
+ export declare function linkValueTags(value: object, tags: Set<string>): void;
143
+ /** The current execution reads a loader's value: it takes on its tags. */
144
+ export declare function readValueTags(value: object): void;
145
+ /**
146
+ * A loader-cache HIT restores the read its entry's MISS recorded onto the
147
+ * value's set, so an unkeyed reader of the value refuses it as it would on
148
+ * the MISS (loader-cache.ts identity mark).
149
+ */
150
+ export declare function markIdentityRead(set: Set<string>, read: LoaderIdentityRead): void;
151
+ /**
152
+ * The identity read `root`'s execution recorded, or one a set it links to
153
+ * recorded, nearest first (breadth-first, so the execution's own read wins).
154
+ */
155
+ export declare function recordedIdentityRead(root: Set<string>): LoaderIdentityRead | undefined;
156
+ /** `root`'s tags plus those of every set it links to, transitively. */
157
+ export declare function flattenRecordedTags(root: Set<string>): Set<string>;
158
+ /** Record where the execution recording into `set` started. */
159
+ export declare function markTagSetStart(set: Set<string>, start: ExecutionStart): void;
160
+ /**
161
+ * The earliest start among `root` and every set it links to: a value built
162
+ * from another execution's value is as old as the oldest of them. A
163
+ * loader-cache write gates on it (loader-cache.ts), because the loader value
164
+ * it stores can be a run a reader started before the binding executed, and
165
+ * the values that run read can be older still.
166
+ */
167
+ export declare function earliestRecordedStart(root: Set<string>): ExecutionStart | undefined;
168
+ /**
169
+ * Run a function within a cache tag scope. Any cacheTag() calls inside `fn`,
170
+ * and the tags a nested "use cache" call records (recordOwnedTags, #980),
171
+ * accumulate into the returned Set and every scope enclosing it
172
+ * (scopeParents).
69
173
  *
70
174
  * The returned Set is the LIVE reference - the caller must await `result`
71
175
  * before reading `tags`, because an async cached function may call cacheTag()
72
- * after an await boundary.
176
+ * after an await boundary. Pass `tagSet` to re-enter an execution's scope
177
+ * (its result's Flight encode, #980); it keeps the parent it opened under.
73
178
  *
74
179
  * @internal Used by cache-runtime.ts to wrap "use cache" execution.
75
180
  */
76
- export declare function runWithCacheTagScope<T>(fn: () => T): {
181
+ export declare function runWithCacheTagScope<T>(fn: () => T, tagSet?: Set<string>): {
77
182
  result: T;
78
183
  tags: Set<string>;
79
184
  };
@@ -1,4 +1,10 @@
1
1
  /** Encode ArrayBuffer to base64 string. */
2
2
  export declare function bufferToBase64(buffer: ArrayBuffer): string;
3
+ /** Encode bytes (a view's own region only) to a base64 string. */
4
+ export declare function bytesToBase64(bytes: Uint8Array): string;
5
+ /** Decode a base64 string to bytes. Throws on a character outside the alphabet. */
6
+ export declare function base64ToBytes(base64: string): Uint8Array;
7
+ /** The byte length `base64` decodes to (padded or not), without decoding. */
8
+ export declare function base64ByteLength(base64: string): number;
3
9
  /** Decode base64 string to ArrayBuffer. */
4
10
  export declare function base64ToBuffer(base64: string): ArrayBuffer;
@@ -110,3 +110,14 @@ export declare const EDGE_READ_TIMEOUT_MS = 20;
110
110
  * Override per store via `CFCacheStoreOptions.kvReadTimeoutMs` (<= 0 disables).
111
111
  */
112
112
  export declare const KV_READ_TIMEOUT_MS = 170;
113
+ /**
114
+ * Floor (ms) on the budget for reading a PPR shell's snapshot, the rest of a
115
+ * frame whose head and prelude were already read. A document HIT has sent
116
+ * its prelude by then and cannot render the rest without the snapshot's doc
117
+ * record, so a timed-out read degrades the HIT (the entry is replaced, the
118
+ * page reloads once into a MISS; rsc-rendering.ts degradeUnreplayableShell).
119
+ * That costs far more than waiting, so a slow body read gets at least this
120
+ * long; `kvReadTimeoutMs` above it wins, and `kvReadTimeoutMs <= 0` still
121
+ * disables the bound.
122
+ */
123
+ export declare const SHELL_SNAPSHOT_READ_MIN_TIMEOUT_MS = 1000;