@rangojs/router 0.18.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 (244) hide show
  1. package/dist/types/browser/event-controller.d.ts +3 -3
  2. package/dist/types/browser/history-state.d.ts +2 -0
  3. package/dist/types/browser/navigation-transaction.d.ts +20 -2
  4. package/dist/types/browser/partial-update.d.ts +16 -1
  5. package/dist/types/browser/prefetch/cache.d.ts +6 -6
  6. package/dist/types/browser/react/Link.d.ts +19 -9
  7. package/dist/types/browser/react/context.d.ts +2 -2
  8. package/dist/types/browser/react/location-state-shared.d.ts +95 -56
  9. package/dist/types/browser/react/location-state.d.ts +1 -1
  10. package/dist/types/browser/transition-when.d.ts +80 -0
  11. package/dist/types/browser/types.d.ts +104 -9
  12. package/dist/types/build/generate-manifest.d.ts +8 -0
  13. package/dist/types/cache/background-task.d.ts +24 -2
  14. package/dist/types/cache/cache-exec-scope.d.ts +31 -0
  15. package/dist/types/cache/cache-key-utils.d.ts +44 -13
  16. package/dist/types/cache/cache-policy.d.ts +1 -1
  17. package/dist/types/cache/cache-runtime.d.ts +7 -0
  18. package/dist/types/cache/cache-scope.d.ts +117 -35
  19. package/dist/types/cache/cache-tag.d.ts +24 -4
  20. package/dist/types/cache/cf/cf-cache-store.d.ts +55 -44
  21. package/dist/types/cache/cf/cf-shell-frame.d.ts +1 -1
  22. package/dist/types/cache/cf/cf-tag-marker-memo.d.ts +0 -15
  23. package/dist/types/cache/document-cache.d.ts +10 -0
  24. package/dist/types/cache/handle-snapshot.d.ts +40 -8
  25. package/dist/types/cache/invalidation-order.d.ts +23 -0
  26. package/dist/types/cache/request-tag-mask.d.ts +54 -0
  27. package/dist/types/cache/shell-snapshot.d.ts +88 -141
  28. package/dist/types/cache/tag-invalidation.d.ts +38 -14
  29. package/dist/types/cache/types.d.ts +68 -61
  30. package/dist/types/cache/vercel/vercel-cache-store.d.ts +23 -16
  31. package/dist/types/client-urls/navigation.d.ts +28 -2
  32. package/dist/types/client-urls/server-projection.d.ts +5 -2
  33. package/dist/types/client-urls/types.d.ts +5 -5
  34. package/dist/types/client.d.ts +1 -1
  35. package/dist/types/index.d.ts +1 -1
  36. package/dist/types/index.rsc.d.ts +1 -1
  37. package/dist/types/route-definition/helpers-types.d.ts +11 -11
  38. package/dist/types/route-name.d.ts +7 -0
  39. package/dist/types/router/error-handling.d.ts +1 -1
  40. package/dist/types/router/intercept-resolution.d.ts +2 -0
  41. package/dist/types/router/match-context.d.ts +5 -0
  42. package/dist/types/router/match-middleware/background-revalidation.d.ts +0 -4
  43. package/dist/types/router/router-options.d.ts +4 -1
  44. package/dist/types/router/segment-resolution/loader-cache.d.ts +18 -1
  45. package/dist/types/router/segment-resolution/loader-mask.d.ts +3 -4
  46. package/dist/types/router/segment-resolution/loader-snapshot.d.ts +14 -40
  47. package/dist/types/router/segment-resolution/mask-nested.d.ts +11 -3
  48. package/dist/types/router/segment-resolution/view-transition-default.d.ts +7 -5
  49. package/dist/types/router/transition-when.d.ts +24 -10
  50. package/dist/types/rsc/attach-transition-when.d.ts +18 -0
  51. package/dist/types/rsc/full-payload.d.ts +14 -7
  52. package/dist/types/rsc/response-cache-serve.d.ts +15 -12
  53. package/dist/types/rsc/shell-build-manifest.d.ts +4 -3
  54. package/dist/types/rsc/shell-capture-constants.d.ts +77 -20
  55. package/dist/types/rsc/shell-capture.d.ts +78 -81
  56. package/dist/types/rsc/shell-serve.d.ts +19 -48
  57. package/dist/types/rsc/types.d.ts +2 -0
  58. package/dist/types/segment-system.d.ts +7 -0
  59. package/dist/types/server/context.d.ts +89 -61
  60. package/dist/types/server/cookie-store.d.ts +30 -18
  61. package/dist/types/server/handle-store.d.ts +52 -1
  62. package/dist/types/server/request-context.d.ts +66 -45
  63. package/dist/types/server/request-headers.d.ts +44 -0
  64. package/dist/types/server.d.ts +1 -0
  65. package/dist/types/testing/e2e/index.d.ts +1 -1
  66. package/dist/types/testing/index.d.ts +3 -2
  67. package/dist/types/testing/internal/action-id.d.ts +7 -0
  68. package/dist/types/testing/location-state-key.d.ts +24 -0
  69. package/dist/types/testing/render-route.d.ts +17 -1
  70. package/dist/types/testing/run-transition-when.d.ts +44 -69
  71. package/dist/types/testing/serve-shell-request.d.ts +7 -6
  72. package/dist/types/testing/shell-status.d.ts +34 -22
  73. package/dist/types/transition-when-ref.d.ts +79 -0
  74. package/dist/types/types/cache-types.d.ts +15 -3
  75. package/dist/types/types/index.d.ts +1 -1
  76. package/dist/types/types/segments.d.ts +86 -71
  77. package/dist/types/urls/path-helper-types.d.ts +3 -4
  78. package/dist/types/urls/pattern-types.d.ts +3 -4
  79. package/dist/types/vite/plugins/transition-when-hoist.d.ts +45 -0
  80. package/dist/vite/index.js +960 -97
  81. package/package.json +1 -1
  82. package/skills/cache-guide/SKILL.md +76 -49
  83. package/skills/caching/SKILL.md +167 -69
  84. package/skills/client-urls/SKILL.md +1 -1
  85. package/skills/comparison/references/framework-comparison.md +8 -5
  86. package/skills/document-cache/SKILL.md +24 -0
  87. package/skills/hooks/SKILL.md +3 -1
  88. package/skills/hooks/navigation.md +23 -2
  89. package/skills/hooks/state.md +98 -6
  90. package/skills/intercept/SKILL.md +29 -12
  91. package/skills/loader/SKILL.md +52 -21
  92. package/skills/middleware/SKILL.md +18 -0
  93. package/skills/migrate-nextjs/SKILL.md +3 -3
  94. package/skills/observability/SKILL.md +6 -5
  95. package/skills/parallel/SKILL.md +1 -1
  96. package/skills/ppr/SKILL.md +129 -77
  97. package/skills/prerender/SKILL.md +3 -3
  98. package/skills/rango/SKILL.md +25 -6
  99. package/skills/route/SKILL.md +2 -0
  100. package/skills/testing/SKILL.md +27 -25
  101. package/skills/testing/cache-prerender.md +9 -9
  102. package/skills/testing/client-components.md +45 -3
  103. package/skills/testing/loader.md +3 -2
  104. package/skills/testing/middleware.md +1 -0
  105. package/skills/testing/predicates.md +49 -49
  106. package/skills/testing/render-handler.md +1 -0
  107. package/skills/testing/serve-shell-request.md +14 -0
  108. package/skills/testing/server-actions.md +9 -3
  109. package/skills/theme/SKILL.md +8 -4
  110. package/skills/typesafety/env-and-bindings.md +8 -0
  111. package/skills/use-cache/SKILL.md +64 -19
  112. package/skills/view-transitions/SKILL.md +150 -29
  113. package/src/__augment-tests__/augmented.check.ts +23 -0
  114. package/src/browser/event-controller.ts +4 -4
  115. package/src/browser/history-state.ts +52 -1
  116. package/src/browser/link-interceptor.ts +4 -0
  117. package/src/browser/navigation-bridge.ts +77 -18
  118. package/src/browser/navigation-client.ts +4 -3
  119. package/src/browser/navigation-store.ts +43 -1
  120. package/src/browser/navigation-transaction.ts +31 -11
  121. package/src/browser/partial-update.ts +146 -35
  122. package/src/browser/prefetch/cache.ts +6 -6
  123. package/src/browser/prefetch/fetch.ts +2 -2
  124. package/src/browser/react/Link.tsx +38 -33
  125. package/src/browser/react/NavigationProvider.tsx +2 -2
  126. package/src/browser/react/context.ts +2 -2
  127. package/src/browser/react/location-state-shared.ts +241 -49
  128. package/src/browser/react/location-state.ts +5 -2
  129. package/src/browser/rsc-router.tsx +4 -0
  130. package/src/browser/server-action-bridge.ts +84 -15
  131. package/src/browser/transition-when.ts +205 -0
  132. package/src/browser/types.ts +124 -10
  133. package/src/build/generate-manifest.ts +77 -1
  134. package/src/cache/background-task.ts +55 -2
  135. package/src/cache/cache-exec-scope.ts +48 -1
  136. package/src/cache/cache-key-utils.ts +56 -16
  137. package/src/cache/cache-policy.ts +9 -4
  138. package/src/cache/cache-runtime.ts +197 -144
  139. package/src/cache/cache-scope.ts +304 -112
  140. package/src/cache/cache-tag.ts +105 -13
  141. package/src/cache/cf/cf-cache-store.ts +152 -76
  142. package/src/cache/cf/cf-shell-frame.ts +2 -2
  143. package/src/cache/cf/cf-tag-marker-memo.ts +11 -100
  144. package/src/cache/document-cache.ts +53 -7
  145. package/src/cache/handle-snapshot.ts +52 -9
  146. package/src/cache/invalidation-order.ts +84 -0
  147. package/src/cache/request-tag-mask.ts +147 -0
  148. package/src/cache/shell-snapshot.ts +130 -348
  149. package/src/cache/tag-invalidation.ts +57 -51
  150. package/src/cache/types.ts +71 -67
  151. package/src/cache/vercel/vercel-cache-store.ts +61 -58
  152. package/src/client-urls/client-root.tsx +8 -4
  153. package/src/client-urls/client-urls.ts +8 -6
  154. package/src/client-urls/navigation.ts +55 -4
  155. package/src/client-urls/server-projection.ts +31 -10
  156. package/src/client-urls/types.ts +12 -5
  157. package/src/client.tsx +2 -0
  158. package/src/host/cookie-handler.ts +2 -1
  159. package/src/index.rsc.ts +3 -0
  160. package/src/index.ts +3 -0
  161. package/src/loader-redirect.tsx +7 -3
  162. package/src/prerender/build-shell-capture.ts +155 -146
  163. package/src/route-definition/dsl-helpers.ts +2 -0
  164. package/src/route-definition/helpers-types.ts +11 -11
  165. package/src/route-name.ts +12 -0
  166. package/src/router/content-negotiation.ts +4 -1
  167. package/src/router/error-handling.ts +5 -2
  168. package/src/router/handler-context.ts +10 -7
  169. package/src/router/intercept-resolution.ts +11 -1
  170. package/src/router/loader-resolution.ts +28 -3
  171. package/src/router/logging.ts +5 -3
  172. package/src/router/match-api.ts +62 -34
  173. package/src/router/match-context.ts +5 -0
  174. package/src/router/match-handlers.ts +9 -29
  175. package/src/router/match-middleware/background-revalidation.ts +3 -5
  176. package/src/router/match-middleware/cache-lookup.ts +35 -19
  177. package/src/router/match-middleware/cache-store.ts +15 -9
  178. package/src/router/match-result.ts +1 -0
  179. package/src/router/middleware.ts +2 -5
  180. package/src/router/navigation-snapshot.ts +6 -5
  181. package/src/router/prerender-match.ts +4 -0
  182. package/src/router/request-classification.ts +7 -4
  183. package/src/router/router-options.ts +4 -1
  184. package/src/router/segment-resolution/fresh.ts +0 -4
  185. package/src/router/segment-resolution/loader-cache.ts +150 -25
  186. package/src/router/segment-resolution/loader-mask.ts +3 -4
  187. package/src/router/segment-resolution/loader-snapshot.ts +15 -78
  188. package/src/router/segment-resolution/mask-nested.ts +47 -22
  189. package/src/router/segment-resolution/revalidation.ts +0 -3
  190. package/src/router/segment-resolution/view-transition-default.ts +6 -21
  191. package/src/router/telemetry.ts +6 -3
  192. package/src/router/transition-when.ts +34 -52
  193. package/src/router.ts +2 -1
  194. package/src/rsc/attach-transition-when.ts +47 -0
  195. package/src/rsc/full-payload.ts +30 -10
  196. package/src/rsc/handler.ts +12 -10
  197. package/src/rsc/loader-fetch.ts +2 -1
  198. package/src/rsc/manifest-init.ts +4 -0
  199. package/src/rsc/origin-guard.ts +8 -5
  200. package/src/rsc/progressive-enhancement.ts +7 -30
  201. package/src/rsc/response-cache-serve.ts +68 -80
  202. package/src/rsc/response-route-handler.ts +2 -2
  203. package/src/rsc/rsc-rendering.ts +206 -252
  204. package/src/rsc/server-action.ts +8 -20
  205. package/src/rsc/shell-build-manifest.ts +22 -20
  206. package/src/rsc/shell-capture-constants.ts +136 -20
  207. package/src/rsc/shell-capture.ts +352 -589
  208. package/src/rsc/shell-serve.ts +73 -109
  209. package/src/rsc/ssr-setup.ts +3 -2
  210. package/src/rsc/types.ts +2 -0
  211. package/src/segment-system.tsx +40 -7
  212. package/src/server/context.ts +189 -124
  213. package/src/server/cookie-store.ts +153 -127
  214. package/src/server/handle-store.ts +245 -27
  215. package/src/server/request-context.ts +123 -84
  216. package/src/server/request-headers.ts +134 -0
  217. package/src/server.ts +4 -0
  218. package/src/testing/dispatch.ts +4 -7
  219. package/src/testing/e2e/index.ts +1 -0
  220. package/src/testing/index.ts +5 -0
  221. package/src/testing/internal/action-id.ts +25 -0
  222. package/src/testing/internal/context.ts +1 -1
  223. package/src/testing/location-state-key.ts +45 -0
  224. package/src/testing/render-handler.ts +1 -1
  225. package/src/testing/render-route.tsx +109 -16
  226. package/src/testing/run-client-revalidate.ts +3 -19
  227. package/src/testing/run-loader.ts +5 -1
  228. package/src/testing/run-transition-when.ts +109 -172
  229. package/src/testing/serve-shell-request.ts +10 -7
  230. package/src/testing/shell-status.ts +60 -56
  231. package/src/transition-when-ref.ts +220 -0
  232. package/src/types/cache-types.ts +15 -3
  233. package/src/types/index.ts +3 -0
  234. package/src/types/segments.ts +94 -91
  235. package/src/urls/path-helper-types.ts +3 -4
  236. package/src/urls/pattern-types.ts +3 -4
  237. package/src/vite/discovery/discover-routers.ts +3 -0
  238. package/src/vite/discovery/shell-prerender-phase.ts +0 -2
  239. package/src/vite/discovery/virtual-module-codegen.ts +16 -6
  240. package/src/vite/plugins/transition-when-hoist.ts +1064 -0
  241. package/src/vite/rango.ts +2 -0
  242. package/src/vite/router-discovery.ts +75 -5
  243. package/dist/types/rsc/transition-gate.d.ts +0 -23
  244. package/src/rsc/transition-gate.ts +0 -86
@@ -41,6 +41,12 @@ export interface RscMetadata {
41
41
  resolvedIds?: string[];
42
42
  /** Merged route params from the matched route */
43
43
  params?: Record<string, string>;
44
+ /**
45
+ * The matched route's name (include name prefix applied), when the route is
46
+ * named. The browser keeps it per history entry for transition({ when })'s
47
+ * `from.routeName` / `to.routeName` (browser/transition-when.ts).
48
+ */
49
+ routeName?: string;
44
50
  /**
45
51
  * State of named slots for this route match
46
52
  * Key is slot name (e.g., "@modal"), value is slot state
@@ -246,6 +252,17 @@ export interface SegmentState {
246
252
  currentUrl: string;
247
253
  currentSegmentIds: string[];
248
254
  }
255
+ /**
256
+ * What the router remembers about one history entry for transition({ when })
257
+ * (NavigationStore.rememberDisplayedEntry).
258
+ *
259
+ * @internal This type is an implementation detail and may change without notice.
260
+ */
261
+ export interface HistoryEntryMemory {
262
+ readonly routeName: string | undefined;
263
+ /** The entry's `history.state` as last recorded. */
264
+ readonly state: unknown;
265
+ }
249
266
  /**
250
267
  * Navigation update emitted when UI should re-render
251
268
  *
@@ -265,11 +282,51 @@ export interface NavigationUpdate {
265
282
  };
266
283
  }
267
284
  /**
268
- * State value for navigate/Link
269
- * - LocationStateEntry[]: Type-safe state entries (recommended)
270
- * - unknown: Plain state format (object or getter function)
285
+ * Plain object accepted as {@link PlainHistoryState}. The `any` string index
286
+ * admits interface-typed values (an `unknown` index would reject interfaces,
287
+ * which carry no implicit index signature). The `never` members reject:
288
+ * - a single typed entry (`GridState(value)` without the array) — its
289
+ * `__rsc_ls_*` fields would be spread onto history.state and
290
+ * `useLocationState(GridState)` would read `undefined`;
291
+ * - functions, including a location-state definition passed uncalled
292
+ * (`Symbol.hasInstance` comes from `Function.prototype`), which throw
293
+ * `DataCloneError`;
294
+ * - arrays and other iterables (`Symbol.iterator`), so an array must match the
295
+ * typed-entry or plain-array member instead;
296
+ * - built-ins tagged with `Symbol.toStringTag` (`Promise`, `WeakMap`,
297
+ * `WeakSet`), which throw `DataCloneError`. The cloneable tagged ones
298
+ * (`Map`, `Set`, `ArrayBuffer`, typed arrays) have their own members.
299
+ *
300
+ * Members are not checked: a function, symbol, or React element inside a
301
+ * plain object still compiles, as does a top-level `ReactElement` or DOM node
302
+ * (an object with none of the keys above).
303
+ */
304
+ export interface PlainHistoryObject {
305
+ readonly [key: string]: any;
306
+ readonly __rsc_ls_key?: never;
307
+ readonly __rsc_ls_value?: never;
308
+ readonly __rsc_ls_lazy?: never;
309
+ readonly [Symbol.iterator]?: never;
310
+ readonly [Symbol.hasInstance]?: never;
311
+ readonly [Symbol.toStringTag]?: never;
312
+ }
313
+ /**
314
+ * Plain (untyped) navigation state, stored under `history.state.state` and
315
+ * read with `useLocationState<T>()`. A structured-clone-safe value: primitives,
316
+ * arrays, `Map`/`Set`, `ArrayBuffer`, typed arrays, and plain objects. Excludes
317
+ * functions, symbols, `Promise`/`WeakMap`/`WeakSet`, and objects carrying
318
+ * `__rsc_ls_*` keys (a typed entry passed without its array). Arrays, `Map`,
319
+ * and `Set` are checked element by element; plain object members are not
320
+ * checked (see {@link PlainHistoryObject}).
271
321
  */
272
- export type HistoryState = import("./react/location-state-shared.js").LocationStateEntry[] | unknown;
322
+ export type PlainHistoryState = string | number | boolean | bigint | null | undefined | readonly PlainHistoryState[] | ReadonlyMap<PlainHistoryState, PlainHistoryState> | ReadonlySet<PlainHistoryState> | ArrayBuffer | ArrayBufferView | PlainHistoryObject;
323
+ /**
324
+ * State value for `router.push()` / `router.replace()` / `<Link state>`.
325
+ * - `readonly LocationStateEntry[]`: typed entries from `createLocationState()`
326
+ * definitions, e.g. `[GridState({ count: 3 })]` (recommended)
327
+ * - {@link PlainHistoryState}: plain structured-clone-safe state
328
+ */
329
+ export type HistoryState = readonly import("./react/location-state-shared.js").LocationStateEntry[] | PlainHistoryState;
273
330
  /**
274
331
  * Options for navigation operations
275
332
  */
@@ -295,6 +352,22 @@ export interface NavigateOptions {
295
352
  * ```
296
353
  */
297
354
  revalidate?: boolean;
355
+ /**
356
+ * Set to `false` to present this navigation without a transition: the
357
+ * commit is urgent and every `<ViewTransition>` class resolves to `"none"`,
358
+ * the same result as a `transition({ when })` predicate returning `false`.
359
+ * No `when` predicate is called. Back/forward, action and revalidation
360
+ * commits are unaffected (they are not started by this call).
361
+ *
362
+ * @default true (the route's transition config applies)
363
+ *
364
+ * @example
365
+ * ```tsx
366
+ * router.push("/photos/2", { transition: false });
367
+ * <Link to="/photos/2" transition={false}>Next</Link>
368
+ * ```
369
+ */
370
+ transition?: boolean;
298
371
  /**
299
372
  * State to pass to history.pushState/replaceState
300
373
  * Accessible via useLocationState() hook.
@@ -316,14 +389,21 @@ export interface NavigateOptions {
316
389
  * // Plain static state
317
390
  * navigate("/product", { state: { from: "list" } });
318
391
  *
319
- * // Plain just-in-time state
320
- * navigate("/product", { state: () => ({ from: window.location.pathname }) });
392
+ * // Compile errors (and a dev-mode runtime error for untyped callers):
393
+ * navigate("/product", { state: ProductState(p) }); // entry without the array
394
+ * navigate("/product", { state: [ProductState] }); // definition not called
321
395
  * ```
322
396
  */
323
397
  state?: HistoryState;
324
398
  }
325
- /** @internal Extended options used only within the navigation bridge */
326
- export interface NavigateOptionsInternal extends NavigateOptions {
399
+ /**
400
+ * @internal Extended options used only within the navigation bridge. `state`
401
+ * is widened to `unknown`: the redirect lanes pass state already resolved to
402
+ * a flat `history.state` record, and in dev Link passes an uncalled
403
+ * definition through to resolveNavigationState's check.
404
+ */
405
+ export interface NavigateOptionsInternal extends Omit<NavigateOptions, "state"> {
406
+ state?: unknown;
327
407
  /** Skip segment cache (used by redirect-with-state to force re-render) */
328
408
  _skipCache?: boolean;
329
409
  }
@@ -402,6 +482,21 @@ export interface NavigationStore {
402
482
  setSegmentIds(ids: string[]): void;
403
483
  getHistoryKey(): string;
404
484
  setHistoryKey(key: string): void;
485
+ /**
486
+ * Per-history-entry memory for transition({ when }) sources: record the
487
+ * entry on screen (keyed by its `history.state.key`) with its current
488
+ * `history.state` and, when given, its route name (kept from the last
489
+ * record otherwise; every push/replace creates a new key, so a stale name
490
+ * never carries to another entry). Called after every commit, restore and
491
+ * state merge.
492
+ */
493
+ rememberDisplayedEntry(routeName?: string): void;
494
+ /**
495
+ * The memory of the entry on screen, or of `entryKey`. At popstate
496
+ * history.state already belongs to the destination, so back/forward reads
497
+ * the entry being LEFT from here. In-memory only: empty after a reload.
498
+ */
499
+ getHistoryEntryMemory(entryKey?: string): HistoryEntryMemory | undefined;
405
500
  /** Monotonic token of the most recently committed navigation. */
406
501
  getNavInstance(): number;
407
502
  cacheSegmentsForHistory(historyKey: string, segments: ResolvedSegment[], handleData?: HandleData): void;
@@ -517,7 +612,7 @@ export interface ServerActionBridgeConfig {
517
612
  * Navigation bridge for handling client-side navigation
518
613
  */
519
614
  export interface NavigationBridge {
520
- navigate(url: string, options?: NavigateOptions): Promise<void>;
615
+ navigate(url: string, options?: NavigateOptionsInternal): Promise<void>;
521
616
  refresh(): Promise<void>;
522
617
  handlePopstate(): Promise<void>;
523
618
  registerLinkInterception(): () => void;
@@ -93,6 +93,14 @@ export declare function generateManifestFull<TEnv>(urlpatterns: UrlPatterns<TEnv
93
93
  * EntryData map built below is local; this is the only seam that surfaces it.
94
94
  */
95
95
  collectClientFallbackRef?: (refKey: string) => void;
96
+ /**
97
+ * Fail on an invalid transition({ when }): a non-function, or a plain
98
+ * (server) function where a client reference is required. Only the Vite
99
+ * plugin's discovery passes it: it evaluates the tree in the react-server
100
+ * graph, the one place a "use client" import is told apart from a server
101
+ * function (transition-when-ref.ts).
102
+ */
103
+ validateTransitionWhen?: boolean;
96
104
  }): Promise<FullManifest>;
97
105
  /**
98
106
  * Generate TypeScript code for the manifest
@@ -1,8 +1,10 @@
1
1
  /**
2
2
  * Background Task Runner
3
3
  *
4
- * Unified helper for scheduling async work via waitUntil.
5
- * When waitUntil is unavailable, falls back to blocking or skipping.
4
+ * Unified helper for scheduling async work via waitUntil, and the two bounded
5
+ * waits the PPR shell capture puts on background work (raceDeadline,
6
+ * settleGrowing). When waitUntil is unavailable, falls back to blocking or
7
+ * skipping.
6
8
  */
7
9
  interface WaitUntilHost {
8
10
  waitUntil?: (fn: () => Promise<void>) => void;
@@ -18,4 +20,24 @@ interface WaitUntilHost {
18
20
  * @returns A promise when blocking fallback is used, void otherwise.
19
21
  */
20
22
  export declare function runBackground(host: WaitUntilHost | null | undefined, task: () => Promise<void>, blockWhenNoWaitUntil?: boolean): Promise<void> | void;
23
+ /**
24
+ * Race `work` against an absolute deadline (epoch ms), keeping its value. The
25
+ * losing work is not cancelled (a router match has no abort signal) and its
26
+ * late rejection is swallowed. The timer is unref'd, so a pending deadline
27
+ * never keeps a Node dev process alive.
28
+ */
29
+ export declare function raceDeadline<T>(work: Promise<T>, deadline: number): Promise<{
30
+ done: true;
31
+ value: T;
32
+ } | {
33
+ done: false;
34
+ }>;
35
+ /**
36
+ * Wait until every promise in `list` has settled, including ones appended
37
+ * while waiting (a settled write can schedule a nested one: cacheRoute
38
+ * schedules its store.set in a second waitUntil), or until `deadline` (epoch
39
+ * ms). Resolves true when the list drained, false at the deadline. Never
40
+ * mutates `list`: a later call waits for what is still pending.
41
+ */
42
+ export declare function settleGrowing(list: readonly Promise<unknown>[], deadline: number): Promise<boolean>;
21
43
  export {};
@@ -31,6 +31,37 @@
31
31
  export interface CacheExecScope {
32
32
  readonly parent?: CacheExecScope;
33
33
  }
34
+ /**
35
+ * Run a callback whose identity reads never reach a shared entry, so
36
+ * guardIdentityRead (server/context.ts) lets them through and records
37
+ * nothing (#976):
38
+ *
39
+ * - a cache's own decisions and metadata: a `key()`, a store `keyGenerator`
40
+ * (cache-policy.ts resolveCacheKey), a `condition()` (cache-scope.ts
41
+ * conditionAllows, loader-cache.ts) and a `tags()` function
42
+ * (cache-policy.ts resolveTagsOption). The value picks the entry, whether
43
+ * one is used, or how it is invalidated; it is never rendered. Reading the
44
+ * request there is the documented way to partition an entry;
45
+ * - the router's `onError` (router/error-handling.ts invokeOnError): it
46
+ * observes, and a render error can reach it inside a capture or a cached
47
+ * scope.
48
+ *
49
+ * Scar: a ppr capture resolves its doc record key, and the record's tags,
50
+ * again under the capture context; once `ctx.request.headers` was guarded, a
51
+ * keyGenerator or tags() reading it refused every capture of the route.
52
+ *
53
+ * The exemption is async-local, so it would follow everything the callback
54
+ * starts. A "use cache" body (runWithCacheExecScope), a loader body
55
+ * (server/context.ts runInsideLoaderBodyScope) and a segment funnel
56
+ * (getContext().runWithStore) end it on entry (endIdentityExempt). Scar: a
57
+ * key() that awaited a "use cache" function reading cookies() stored the
58
+ * first visitor's cookie and served it to the next.
59
+ */
60
+ export declare function runIdentityExempt<T>(fn: () => T): T;
61
+ /** True inside runIdentityExempt, and not in a scope started from it. */
62
+ export declare function isInsideIdentityExempt(): boolean;
63
+ /** Run `fn` with the identity exemption ended (see runIdentityExempt). */
64
+ export declare function endIdentityExempt<T>(fn: () => T): T;
34
65
  /**
35
66
  * Run fn with the "use cache" execution scope active. Continuations spawned
36
67
  * from fn's synchronous kickoff inherit the scope; parallel chains do not.
@@ -42,17 +42,48 @@ export declare function sortedRouteParams(params: Record<string, string> | undef
42
42
  */
43
43
  export declare function cacheKeyBase(host: string, pathname: string, searchParams?: URLSearchParams, params?: Record<string, string>, filter?: SearchParamsFilter): string;
44
44
  /**
45
- * The record key, or shell partition, of nested keyed cache() scopes (#970)
46
- * from its parts, outermost first: the chain's `key()` results and, for an
47
- * inner scope without its own `key()`, that scope's default key
48
- * (CacheScope.resolveKeyFrom). One part is returned raw, so a single keyed
49
- * scope keeps its key byte for byte. Two or more are joined by `|`, each
50
- * URI-encoded (like partitionShellKey, rsc/shell-capture-constants.ts): an
51
- * encoded part holds no `|`, so every tuple maps to its own key, of any
52
- * depth. Raw, `("a|b", "c")` and `("a", "b|c")` named one record. A composed
53
- * key holds no `:` either, so a single raw result with one (a prefixed
54
- * `tier:...`, a `doc:` default key) never equals it; an unprefixed raw result
55
- * can. Shared with the testing helper `shellCacheKey`, which composes a
56
- * nested partition the same way.
45
+ * The prefix of a `key()` result's part in a record key (#975), and of a
46
+ * loader's own `key()` result after its `loader:<id>:` namespace (#1009,
47
+ * loader-cache.ts loaderKeyFromResult).
57
48
  */
58
- export declare function composeCacheKeys(parts: readonly string[]): string;
49
+ export declare const KEY_PART_PREFIX = "key:";
50
+ /**
51
+ * A cache() record key, or ppr shell partition, from its parts
52
+ * (CacheScope.resolveKeyFrom, CacheScope.resolvePartition): the `key()`
53
+ * results of the scope's cache() chain, outermost first, then its default
54
+ * parts: the keyGenerator results of enclosing scopes on another store
55
+ * (#974) and, for a record key under a scope without its own `key()`, its
56
+ * default key (or its store's keyGenerator result, #970).
57
+ *
58
+ * Key scheme invariant (#975). A `key()` result is often request input,
59
+ * and a record key has no route or scope discriminator (the store reads
60
+ * `store.get(key)`), so a result that equals another record's key writes
61
+ * one route's content under the other's entry. Before #975 a single
62
+ * `key()` result was stored raw: the header value `doc:localhost/pricing`
63
+ * named /pricing's default-keyed record, and `gold|doc%3Alocalhost%2Fpricing`
64
+ * named gold's nested /pricing record (#970). So every part is encoded:
65
+ *
66
+ * - a `key()` part is `key:` plus `encodeURIComponent(result)`: it starts
67
+ * with `key:` and holds no `|` and no other `:`;
68
+ * - a default part is `encodeURIComponent(value)`: it holds no `:` or `|`;
69
+ * an enclosing store's keyGenerator result equal to the default key is an
70
+ * empty part, so each store keeps its position (CacheScope
71
+ * positionalParts);
72
+ * - one `key()` part is the key; two or more parts are joined by `|`,
73
+ * `key()` parts first;
74
+ * - a lone default part stays raw: with no `key()` on the chain the record
75
+ * key is the default key or the store's keyGenerator result, unchanged.
76
+ *
77
+ * Why nothing collides: the router's default keys (`doc:`, `partial:`,
78
+ * `intercept:`, `response:<type>:`) never start with `key:` and hold a `:`.
79
+ * A namespaced `key()` result starts with `key:` and holds no `|`. A
80
+ * composed key holds a `|` and either starts with `key:` or holds no `:`.
81
+ * Among composed keys, splitting on `|` recovers each part, a part's kind
82
+ * (a `key:` prefix, or no `:` at all) and its value. Pinned by the collision
83
+ * probe in src/cache/__tests__/cache-scope-chain.test.ts. A keyGenerator
84
+ * result is the store's own namespace, as before.
85
+ *
86
+ * Shared with the testing helper `shellCacheKey`, so a test builds the
87
+ * production partition.
88
+ */
89
+ export declare function composeCacheKeys(keyResults: readonly string[], defaultParts?: readonly string[]): string;
@@ -71,6 +71,6 @@ export declare function computeExpiration(ttlSeconds: number, swrSeconds?: numbe
71
71
  staleAt: number;
72
72
  expiresAt: number;
73
73
  };
74
- export declare function resolveCacheKey(keyFn: ((ctx: RequestContext) => string | Promise<string>) | undefined, store: SegmentCacheStore | null, defaultKey: string, _label: string): Promise<string>;
74
+ export declare function resolveCacheKey(keyFn: ((ctx: RequestContext) => string | Promise<string>) | undefined, store: SegmentCacheStore | null, defaultKey: string): Promise<string>;
75
75
  export declare function resolveTagsOption<TEnv>(tags: string[] | ((ctx: RequestContext<TEnv>) => string[]) | undefined, ctx: RequestContext<TEnv> | undefined, label: string): string[] | undefined;
76
76
  export declare function resolveCacheStore(explicitStore: SegmentCacheStore | undefined): SegmentCacheStore | null;
@@ -40,6 +40,13 @@ export { isCachedFunction };
40
40
  * identical arg sets.
41
41
  */
42
42
  export declare function replyToCacheKey(encoded: string | FormData): Promise<string>;
43
+ /**
44
+ * @internal Reset this module's per-isolate state: the in-flight leaders a
45
+ * follower would join for up to IN_FLIGHT_LEADER_MAX_WAIT_MS, and the
46
+ * once-per-function warnings. Tests only (testing/serve-shell-request.ts
47
+ * resetShellTestState), never while a request is in flight.
48
+ */
49
+ export declare function resetCacheRuntimeForTests(): void;
43
50
  /**
44
51
  * Register a function as a cached function.
45
52
  * Called by the Vite transform for each "use cache" function.
@@ -10,8 +10,9 @@
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;
15
16
  type CacheKeyFn = NonNullable<PartialCacheOptions["key"]>;
16
17
  /**
17
18
  * Discriminated outcome of a route cache lookup — see
@@ -53,6 +54,19 @@ export declare class CacheScope {
53
54
  * a ppr route).
54
55
  */
55
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?;
56
70
  readonly config: PartialCacheOptions | false;
57
71
  readonly parent: CacheScope | null;
58
72
  /** Explicit store from cache() options, if specified */
@@ -69,6 +83,29 @@ export declare class CacheScope {
69
83
  readonly keyFns: readonly CacheKeyFn[];
70
84
  /** Whether this scope's own config sets `key()` (the last of keyFns). */
71
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;
72
109
  constructor(config: PartialCacheOptions | false, parent?: CacheScope | null, defaultKeyPrefix?: "doc" | undefined,
73
110
  /**
74
111
  * @internal shortCode of the outermost cache() entry of this scope's
@@ -77,7 +114,20 @@ export declare class CacheScope {
77
114
  * Undefined covers the whole matched chain (the implicit shell doc scope,
78
115
  * a ppr route).
79
116
  */
80
- 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);
81
131
  /**
82
132
  * @internal Whether a segment belongs to this scope's cache entry. ShortCodes are
83
133
  * hierarchical (server/context.ts getShortCode: parent shortCode + include
@@ -130,17 +180,21 @@ export declare class CacheScope {
130
180
  * request: a ppr route's shell partition (resolvePartition) and the record
131
181
  * lookup share the `key()` runs, and a rejection is shared too.
132
182
  *
133
- * With `key()` functions on the chain (keyFns) the parts are their results,
134
- * outermost first, composed by composeCacheKeys (a single part raw), so a
135
- * nested cache() keys its records within the enclosing partition (#970).
136
- * A scope whose own config sets no `key()` appends its own default key
137
- * (resolveDefaultKey). Reusing the outer `key()` result alone would give
138
- * every route under the inner cache() one record: a `key()` replaces the
139
- * whole default key, so an outer key that names no route (`tier:gold`)
140
- * would make `/a` HIT `/b`'s record. A scope with its own `key()` adds no
141
- * default key: a `key()` is a full override, and no keyGenerator runs.
142
- * Without `key()` on the chain it is resolveDefaultKey alone. With one but
143
- * no request context it rejects (keyWithoutContextError).
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:`).
144
198
  *
145
199
  * Each `key()` result lives on the request context (`_resolvedCacheKeys`),
146
200
  * by function: it runs once per request whatever the default key
@@ -151,20 +205,30 @@ export declare class CacheScope {
151
205
  * itself: a `key()` calling `cookies()` would trip the capture guard there,
152
206
  * and a capture must store the partition its request resolved.
153
207
  */
154
- resolveKeyFrom(defaultKey: string): Promise<string>;
208
+ resolveKeyFrom(defaultKey: string, partitionedPrefix?: string): Promise<string>;
155
209
  /**
156
210
  * @internal The request partition of a ppr route's shell under this scope
157
- * (resolveShellPartition): the chain's `key()` results, composed like the
158
- * record key but without the default key, which the URL-based shell key
159
- * already carries. A scope without its own `key()` whose store has a
160
- * keyGenerator adds that result when it differs from the `doc` default
161
- * key, as an unkeyed scope's partition always did: the record is split by
162
- * it, so the shell must be too. Undefined (synchronously, with no work)
163
- * when nothing can partition; null when nothing does.
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.
164
219
  */
165
220
  resolvePartition(pathname: string, params: Record<string, string> | undefined): Promise<string | null> | undefined;
166
- /** The chain's `key()` results, each once per request (memoizedKey). */
167
- private resolveKeyResults;
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;
168
232
  /**
169
233
  * This scope's store keyGenerator result for `defaultKey` (once per request
170
234
  * and default key), else `defaultKey` (resolveCacheKey).
@@ -192,11 +256,11 @@ export declare class CacheScope {
192
256
  */
193
257
  get isShellImplicitDocScope(): boolean;
194
258
  /**
195
- * Evaluate the cache `condition` predicate. Returns false (skip the cache
196
- * operation) when the predicate returns false or throws; returns true when
197
- * there is no condition or no request context to evaluate it against.
198
- */
199
- /**
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
+ *
200
264
  * One WRITE decision per (scope, request), memoized on the request context.
201
265
  * A capture render has TWO writers consulting the same predicate — the
202
266
  * explicit tier's cacheRoute and the snapshot-only doc record gate
@@ -208,6 +272,12 @@ export declare class CacheScope {
208
272
  * at the replay gate was the round-2 regression.
209
273
  */
210
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
+ */
211
281
  private conditionAllows;
212
282
  /**
213
283
  * Lookup cached segments for a route (single cache entry per request).
@@ -216,10 +286,11 @@ export declare class CacheScope {
216
286
  * @param pathname - URL pathname for cache key generation
217
287
  * @param params - Route params for cache key generation
218
288
  * @param isIntercept - Whether this is an intercept navigation (uses different cache key)
219
- * @param claimLoaderPushes - The handler context's _claimLoaderPushes, for
220
- * 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)
221
292
  */
222
- 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<{
223
294
  segments: ResolvedSegment[];
224
295
  shouldRevalidate: boolean;
225
296
  } | null>;
@@ -241,7 +312,7 @@ export declare class CacheScope {
241
312
  * A corrupt entry that was evicted reads as `miss`: the store was consulted
242
313
  * and holds nothing servable, which is the post-eviction truth.
243
314
  */
244
- 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>;
245
316
  /**
246
317
  * Record this scope's segment-DSL cache({ tags }) into the request tag union
247
318
  * synchronously, under the same gate cacheRoute() uses for a write.
@@ -257,6 +328,12 @@ export declare class CacheScope {
257
328
  * (the tag union is a Set), so the duplicate record in cacheRoute is harmless.
258
329
  */
259
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;
260
337
  /**
261
338
  * Cache all segments for a route (non-blocking via waitUntil)
262
339
  * Single cache entry per route request.
@@ -266,14 +343,19 @@ export declare class CacheScope {
266
343
  * @param params - Route params for cache key generation
267
344
  * @param segments - All resolved segments to cache
268
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.
269
350
  */
270
- 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>;
271
352
  }
272
353
  /**
273
354
  * @internal The request partition of a ppr route's shell: with a route
274
355
  * cache() scope, its CacheScope.resolvePartition (the chain's `key()`
275
- * results, composed, plus a keyGenerator's contribution for a scope without
276
- * its own `key()` (#970)); with none, the app store's keyGenerator result
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
277
359
  * given the document default key. Undefined (synchronously, with no work)
278
360
  * when neither applies: the shell key stays host + path + filtered search.
279
361
  * Resolves to null when the partition is the default key itself (a
@@ -11,6 +11,13 @@
11
11
  */
12
12
  import { type RequestContext } from "../server/request-context.js";
13
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;
14
21
  /**
15
22
  * Arm the record tag owners for `ctx`: its match resolved a cache scope, so
16
23
  * it can write a route cache() record (match-api.ts). A capture's or
@@ -148,17 +155,30 @@ export declare function markIdentityRead(set: Set<string>, read: LoaderIdentityR
148
155
  export declare function recordedIdentityRead(root: Set<string>): LoaderIdentityRead | undefined;
149
156
  /** `root`'s tags plus those of every set it links to, transitively. */
150
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;
151
168
  /**
152
- * Run a function within a cache tag scope. Any cacheTag() calls inside `fn`
153
- * accumulate into the returned Set.
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).
154
173
  *
155
174
  * The returned Set is the LIVE reference - the caller must await `result`
156
175
  * before reading `tags`, because an async cached function may call cacheTag()
157
- * 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.
158
178
  *
159
179
  * @internal Used by cache-runtime.ts to wrap "use cache" execution.
160
180
  */
161
- export declare function runWithCacheTagScope<T>(fn: () => T): {
181
+ export declare function runWithCacheTagScope<T>(fn: () => T, tagSet?: Set<string>): {
162
182
  result: T;
163
183
  tags: Set<string>;
164
184
  };