@rangojs/router 0.18.0 → 0.19.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (244) hide show
  1. package/dist/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 +37 -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 +48 -5
  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 +126 -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 +962 -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 +67 -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 +54 -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 +202 -144
  139. package/src/cache/cache-scope.ts +304 -112
  140. package/src/cache/cache-tag.ts +209 -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 +42 -5
  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 +152 -26
  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 +263 -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 +134 -87
  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
@@ -5,88 +5,72 @@
5
5
  * that must agree with them byte for byte. The handler layer's part of that
6
6
  * payload is the capture's own doc segment record: the capture writes it
7
7
  * first, renders its prelude from it, and every HIT replays it (no handler
8
- * runs on a HIT). What still executes on a HIT is the loader layer: live
9
- * loaders and promise-carrying bake-lane (`ssr: false`) loaders run, and the
10
- * values the bake-lane ones read must match what the capture baked (a
11
- * promise-free bake-lane loader is served from its pin and does not run). See docs/design/ppr-shell-resume.md.
8
+ * runs on a HIT). The loader layer's part is the bake-lane (`ssr: false`)
9
+ * loader pins: a promise-free bake-lane loader is served from its pin and
10
+ * does not run; one whose return carries promises (`holes`), or whose pushes
11
+ * the record could not keep (`runs`), runs on the HIT. See
12
+ * docs/design/ppr-shell-resume.md.
12
13
  *
13
- * The mechanism (Next.js resume-data-cache analog, adapted to Rango's cache
14
- * rings): the CAPTURE render records every cache-store read-hit and write it
15
- * performed (the {@link RecordingShellStore}); the records ride inside the
16
- * ShellCacheEntry as its `snapshot`; on a HIT the tail render reads through a
17
- * {@link SeededShellStore} overlay that serves those recorded values AS FRESH.
18
- * Everything NOT recorded (the holes — masked loaders were never executed at
19
- * capture, so their reads were never recorded) stays live.
20
- *
21
- * The invariant, verbatim: the snapshot is the doc record plus the cache-store
22
- * reads the capture's loaders performed; replaying them on a HIT reproduces
23
- * the shell content byte-identically; everything not recorded stays live.
24
- * {@link pruneShellSnapshot} drops the rest.
14
+ * The invariant, verbatim: the snapshot is the doc record and the bake-lane
15
+ * loader pins; replaying them on a HIT reproduces the shell content
16
+ * byte-identically; everything else a HIT reads comes from the store. A hole
17
+ * (a live-lane loader, or a promise nested in a bake-lane return) never reads
18
+ * the capture's copy of a value: it is dynamic or has its own cache. A
19
+ * bake-lane loader body that runs on a HIT reads the store too; its pinned
20
+ * container paths stay the pin's (overlayLoaderContainer), and its settled
21
+ * pushes stay the record's (HandleStore.pushRestored).
25
22
  */
26
23
  import type { SegmentCacheStore, CacheGetResult, CacheItemResult, CacheItemOptions, CachedEntryData, ShellCacheEntry, ShellSnapshotRecord, CacheReadError } from "./types.js";
27
24
  /**
28
- * A store wrapper the CAPTURE render reads through. Every call passes through to
29
- * the underlying store unchanged; for the item/segment/response families it also
30
- * RECORDS, last-write-wins per (family, key):
31
- * - read-hits (get/getItem/getResponse returning non-null) — the value that
32
- * fed the shell,
33
- * - writes (set/setItem/putResponse) — the value a MISS computed and baked.
34
- * The shell family (getShell/putShell) is never recorded (the snapshot rides
35
- * inside a shell entry). Reads that MISS are not recorded (a miss produced no
36
- * shell content; if the render then computed and wrote, that write is recorded).
25
+ * A store wrapper the CAPTURE render reads through. Every call passes through
26
+ * to the underlying store unchanged; the one thing it records is the doc
27
+ * segment record ({@link recordSegmentWrite}). No cache read is recorded: a
28
+ * HIT replays the handler layer from the doc record and serves a promise-free
29
+ * bake-lane loader from its pin, and everything else a HIT reads (a hole, a
30
+ * bake-lane loader body that runs there) comes from the store.
37
31
  *
38
32
  * Deferred writes: cache writes run under waitUntil (fire-and-forget on Node,
39
- * executionContext on workerd), so their setItem/set calls — hence their records
40
- * — may land after the shell has quiesced. The capture collects those write
41
- * promises via {@link trackWrite} and awaits them ({@link settleWrites}) before
42
- * draining, so a MISS-at-capture value is still pinned.
43
- *
44
- * Loader attribution: every item/response access made inside a loader scope
45
- * (hit, miss or write) marks its key in {@link loaderKeys}. A loader re-runs
46
- * on every HIT, so those records stay pinned when the snapshot is pruned. A
47
- * miss counts too: a loader that joins a handler's in-flight "use cache" call
48
- * reads the store first (cache-runtime.ts) and finds nothing, and the record
49
- * then comes from the handler's write.
33
+ * executionContext on workerd), so the doc record's write may land after the
34
+ * capture's match returns. The capture collects those write promises via
35
+ * {@link trackWrite} and awaits them ({@link settleWrites}) before reading the
36
+ * record and draining.
50
37
  */
51
38
  export declare class RecordingShellStore<TEnv = unknown> implements SegmentCacheStore<TEnv> {
52
39
  private readonly inner;
40
+ /** The recorded segment records by key, last write wins. */
53
41
  private readonly records;
54
42
  private readonly writes;
55
- /** Record keys ({@link recordKey}) a loader read or wrote during the capture. */
56
- readonly loaderKeys: Set<string>;
57
43
  constructor(inner: SegmentCacheStore<TEnv>);
58
44
  get defaults(): SegmentCacheStore<TEnv>["defaults"];
59
45
  get keyGenerator(): SegmentCacheStore<TEnv>["keyGenerator"];
60
46
  get supportsPassiveShellReads(): true | undefined;
61
- private record;
62
- private noteLoaderAccess;
47
+ get isTagsInvalidatedSince(): SegmentCacheStore<TEnv>["isTagsInvalidatedSince"];
63
48
  /** Track a deferred cache-write promise so the capture can await it pre-drain. */
64
49
  trackWrite(p: Promise<unknown>): void;
65
50
  /**
66
- * Record a segment-family write into the snapshot WITHOUT touching the inner
67
- * store. The shell fast path's implicit doc-cache scope writes through this
68
- * (via {@link SnapshotOnlySegmentStore}): the recorded doc entry must ride
69
- * ONLY inside the shell entry — a passthrough write would leave a doc-keyed
70
- * entry in the real store that the NEXT capture's lookup would hit, replaying
71
- * the previous generation's segments instead of re-running handlers (breaking
51
+ * Record the doc segment record into the snapshot WITHOUT touching the
52
+ * inner store. The capture's implicit doc-cache scope writes through this
53
+ * (via {@link SnapshotOnlySegmentStore}): the record must ride ONLY inside
54
+ * the shell entry — a passthrough write would leave a doc-keyed entry in the
55
+ * real store that the NEXT capture's lookup would hit, replaying the
56
+ * previous generation's segments instead of re-running handlers (breaking
72
57
  * SWR recapture freshness).
73
58
  */
74
59
  recordSegmentWrite(key: string, data: CachedEntryData): void;
75
60
  /**
76
- * Await the tracked deferred writes so their records are present before drain.
77
- * Drains ITERATIVELY: a write task can schedule a NESTED write (the ring-3
78
- * cacheRoute path schedules its actual store.set in a second waitUntil while the
79
- * first is running), so each awaited batch may enqueue more. Loop until the
80
- * queue empties or the deadline passes. Bounded: a pathologically slow write
81
- * must never stall the capture task. Resolves true when every tracked write
82
- * settled, false at the deadline (settleCaptureRecord then fails the capture
83
- * when the doc record is among the writes still pending).
61
+ * Await the tracked deferred writes so their records are present before
62
+ * drain, including a NESTED write a settled one scheduled (the ring-3
63
+ * cacheRoute path schedules its actual store.set in a second waitUntil).
64
+ * Bounded: a pathologically slow write must never stall the capture task.
65
+ * Resolves true when every tracked write settled, false at the deadline
66
+ * (settleCaptureRecord then fails the capture when the doc record is among
67
+ * the writes still pending); a later call still waits for what is pending.
84
68
  */
85
69
  settleWrites(timeoutMs: number): Promise<boolean>;
86
- /** The recorded snapshot (last-write-wins per family+key), or undefined if empty. */
70
+ /** The recorded segment records, or undefined if none. */
87
71
  drainSnapshot(): ShellSnapshotRecord[] | undefined;
88
- /** One recorded value, by family and key. */
89
- getRecord(family: ShellSnapshotRecord["family"], key: string): ShellSnapshotRecord["value"] | undefined;
72
+ /** One recorded segment record, by key. */
73
+ getRecord(key: string): CachedEntryData | undefined;
90
74
  get(key: string): Promise<CacheGetResult | null | CacheReadError>;
91
75
  set(key: string, data: CachedEntryData, ttl: number, swr?: number): Promise<void>;
92
76
  delete(key: string): Promise<boolean>;
@@ -107,36 +91,23 @@ export declare class RecordingShellStore<TEnv = unknown> implements SegmentCache
107
91
  putShell(key: string, entry: ShellCacheEntry, ttlSeconds?: number, swrSeconds?: number, tags?: string[]): Promise<"stored" | "invalidated" | "uncacheable" | void>;
108
92
  invalidateTags(tags: string[]): Promise<void>;
109
93
  }
110
- /** True iff `store` is a RecordingShellStore (duck-typed across module copies). */
94
+ /** `store` when it is a RecordingShellStore (an instanceof check). */
111
95
  export declare function getRecordingStore<TEnv>(store: SegmentCacheStore<TEnv> | undefined): RecordingShellStore<TEnv> | undefined;
112
96
  /**
113
97
  * True when `snapshot` carries the canonical doc segment record `docKey`
114
- * names, with at least one segment: the record partial replay and a
115
- * fast-path HIT tail consume.
98
+ * names, with at least one segment: the record partial replay and a document
99
+ * HIT tail consume.
116
100
  */
117
101
  export declare function hasDocRecord(snapshot: readonly ShellSnapshotRecord[] | undefined, docKey: string | undefined): boolean;
118
102
  /**
119
- * Who reads a stored snapshot besides the doc record
120
- * (docs/design/shell-entry-layout.md §2):
121
- * - "segments": nobody. A navigation-only entry is read only by partial
122
- * replay, which seeds the doc record alone (SeededShellStore
123
- * `segmentsOnly`, implicit doc scope only).
124
- * - "loaders": the loaders a document HIT re-runs. Every HIT tail replays
125
- * the handler layer from the doc record and never runs a handler, so the
126
- * records only handler code read are dead weight.
127
- */
128
- export type ShellSnapshotReaders = "segments" | "loaders";
129
- /**
130
- * Split a capture's snapshot into what its readers consume and the rest: the
131
- * doc record (`docKey`) always; with "loaders", the loader-family records and
132
- * the item/response records a loader touched during the capture
133
- * (RecordingShellStore.loaderKeys). Every other segment record goes: an
134
- * explicit cache() tier's reads and writes are keyed where no HIT tail and no
135
- * partial replay looks (both consult only the doc record). The doc record
136
- * stays a copy inside the entry: a reference to a separately stored record
137
- * could be evicted on its own, and the HIT would have nothing to replay.
103
+ * Split a capture's snapshot into what a HIT or a partial replay reads and
104
+ * the rest (docs/design/shell-entry-layout.md §2). Kept: the doc record
105
+ * (`docKey`), as a copy inside the entry (a separately stored record could be
106
+ * evicted on its own, and the HIT would have nothing to replay), and the
107
+ * loader-family pins unless the entry is navigation-only (partial replay of
108
+ * such an entry seeds the doc record alone). Every other record goes.
138
109
  */
139
- export declare function pruneShellSnapshot(snapshot: readonly ShellSnapshotRecord[], readers: ShellSnapshotReaders, loaderKeys: ReadonlySet<string>, docKey: string): {
110
+ export declare function pruneShellSnapshot(snapshot: readonly ShellSnapshotRecord[], navigationOnly: boolean, docKey: string | undefined): {
140
111
  kept: ShellSnapshotRecord[];
141
112
  pruned: ShellSnapshotRecord[];
142
113
  };
@@ -165,16 +136,16 @@ export declare function estimateShellEntryBytes(parts: {
165
136
  docKey: string | undefined;
166
137
  prunedRecords: string | undefined;
167
138
  }): number;
168
- /** Snapshot records by family, e.g. `segment:1/item:5` (no commas: it rides a Server-Timing desc). */
139
+ /** Snapshot records by family, e.g. `segment:1/loader:2` (no commas: it rides a Server-Timing desc). */
169
140
  export declare function countSnapshotFamilies(snapshot: readonly ShellSnapshotRecord[]): string;
170
141
  /**
171
- * The store the shell fast path's IMPLICIT doc-cache scope resolves during a
172
- * capture: reads pass through the recording store (a real-store hit is
173
- * recorded, exactly like any capture read), but segment WRITES are recorded
174
- * into the snapshot only — see {@link RecordingShellStore.recordSegmentWrite}
175
- * for why passthrough would break SWR recapture. Routes with their OWN
176
- * cache() config never see this store (their scope resolves the app-level
177
- * recording store and keeps today's record-and-write behavior).
142
+ * The store the capture's IMPLICIT doc-cache scope resolves: reads pass
143
+ * through the recording store to the real one, but segment WRITES are
144
+ * recorded into the snapshot only — see
145
+ * {@link RecordingShellStore.recordSegmentWrite} for why passthrough would
146
+ * break SWR recapture. A route with its OWN cache() scope keeps that scope's
147
+ * real-store reads and writes; recordShellCaptureDocRecord (cache-store.ts)
148
+ * writes its doc record through this store too.
178
149
  */
179
150
  export declare class SnapshotOnlySegmentStore<TEnv = unknown> implements SegmentCacheStore<TEnv> {
180
151
  private readonly recording;
@@ -185,28 +156,14 @@ export declare class SnapshotOnlySegmentStore<TEnv = unknown> implements Segment
185
156
  set(key: string, data: CachedEntryData): Promise<void>;
186
157
  delete(key: string): Promise<boolean>;
187
158
  }
188
- /**
189
- * Materialize the loader-family seed from a shell snapshot for a HIT's tail
190
- * render: Flight-deserialize each recorded (promise-elided) bake-lane
191
- * container into a segment-key -> container Map, which serveShellHit assigns
192
- * to the tail context's `_shellLoaderSeed` for the resolveLoaderData overlay.
193
- * Lives here so every snapshot family is decoded in this module (the
194
- * item/segment/response families via {@link SeededShellStore}); the loader
195
- * family is not a store read, so it seeds the context instead of a store.
196
- *
197
- * Deserializations run in parallel; a record that fails to decode is skipped
198
- * (that loader drifts — the pre-snapshot behavior — instead of failing the
199
- * HIT). Returns undefined when the snapshot carries no loader records, without
200
- * touching the Flight codec (kept lazy for cold paths and non-RSC configs).
201
- */
159
+ /** One decoded loader-family pin (buildShellLoaderSeed). */
202
160
  export interface ShellLoaderSeedEntry {
203
161
  /** The Flight-deserialized elided container (recorded paths + markers). */
204
162
  container: unknown;
205
163
  /**
206
- * True when the record carries hole markers (or predates the capture-side
207
- * hole bit, where hole-ness is unknown): the HIT overlay must gate on the
208
- * fresh run. False = fully pinned; the payload promise resolves immediately
209
- * from the pin (loader-cache.ts pin-first path).
164
+ * True when the record carries hole markers: the HIT overlay must gate on
165
+ * the fresh run. False = fully pinned; the payload promise resolves
166
+ * immediately from the pin (loader-cache.ts pin-first path).
210
167
  */
211
168
  holes: boolean;
212
169
  /**
@@ -216,51 +173,41 @@ export interface ShellLoaderSeedEntry {
216
173
  */
217
174
  runs: boolean;
218
175
  }
176
+ /**
177
+ * Materialize the loader-family seed from a shell snapshot for a HIT's tail
178
+ * render: Flight-deserialize each recorded (promise-elided) bake-lane
179
+ * container into a segment-key -> container Map, which serveShellHit assigns
180
+ * to the tail context's `_shellLoaderSeed` for the resolveLoaderData overlay.
181
+ * Lives here so every snapshot family is decoded in this module (the segment
182
+ * family via {@link SeededShellStore}); the loader family is not a store
183
+ * read, so it seeds the context instead of a store.
184
+ *
185
+ * Deserializations run in parallel; a record that fails to decode is skipped
186
+ * (that loader drifts — the pre-snapshot behavior — instead of failing the
187
+ * HIT). Returns undefined when the snapshot carries no loader records, without
188
+ * touching the Flight codec (kept lazy for cold paths and non-RSC configs).
189
+ */
219
190
  export declare function buildShellLoaderSeed(snapshot: ShellSnapshotRecord[]): Promise<Map<string, ShellLoaderSeedEntry> | undefined>;
220
191
  /**
221
- * A read-through overlay the HIT tail render reads through. For a key present in
222
- * the snapshot it serves the recorded value AS FRESH (shouldRevalidate: false —
223
- * a pinned key must NOT kick SWR background revalidation) so the tail's payload
224
- * matches the frozen prelude. Every other read falls through to the real store
225
- * (the holes — masked loaders were never recorded — stay live). Writes pass
226
- * through so a live hole may legitimately write, except that `segmentsOnly`
227
- * fully isolates the segment family: misses do not fall through, and writes or
228
- * deletes stay local to the navigation overlay. The shell family always passes
229
- * through. With `segmentsOnly`, item and response reads also pass through so a
230
- * partial navigation cannot freeze captured data or write a partial result into
231
- * the canonical document namespace.
192
+ * The implicit doc scope's store on a HIT tail and on a partial replay: it
193
+ * serves the snapshot's segment records, the doc record among them, and
194
+ * fully isolates the segment family. Misses do not fall through, and writes
195
+ * or deletes stay local to the overlay, so a partial navigation cannot write
196
+ * a partial result into the canonical document namespace. It carries nothing
197
+ * else: every other read on a HIT (a hole, a bake-lane loader body that runs
198
+ * there) resolves the request's own store. Loader-family records are not
199
+ * store reads: serveShellHit seeds them onto the tail context
200
+ * (_shellLoaderSeed) for the resolveLoaderData overlay instead.
232
201
  */
233
202
  export declare class SeededShellStore<TEnv = unknown> implements SegmentCacheStore<TEnv> {
234
203
  private readonly inner;
235
- private readonly items;
236
204
  private readonly segments;
237
- private readonly responses;
238
- private readonly segmentsOnly;
239
- constructor(inner: SegmentCacheStore<TEnv>, snapshot: ShellSnapshotRecord[], options?: {
240
- segmentsOnly?: boolean;
241
- });
205
+ constructor(inner: SegmentCacheStore<TEnv>, snapshot: readonly ShellSnapshotRecord[]);
242
206
  get defaults(): SegmentCacheStore<TEnv>["defaults"];
243
207
  get keyGenerator(): SegmentCacheStore<TEnv>["keyGenerator"];
244
- get supportsPassiveShellReads(): true | undefined;
245
208
  get(key: string): Promise<CacheGetResult | null | CacheReadError>;
246
- set(key: string, data: CachedEntryData, ttl: number, swr?: number): Promise<void>;
209
+ set(key: string, data: CachedEntryData): Promise<void>;
247
210
  delete(key: string): Promise<boolean>;
248
- clear(): Promise<void>;
249
- getResponse(key: string): Promise<{
250
- response: Response;
251
- shouldRevalidate: boolean;
252
- } | null>;
253
- putResponse(key: string, response: Response, ttl: number, swr?: number, tags?: string[]): Promise<void>;
254
- getItem(key: string): Promise<CacheItemResult | null>;
255
- setItem(key: string, value: string, options?: CacheItemOptions): Promise<void>;
256
- getShell(key: string, options?: {
257
- claimRevalidation?: boolean;
258
- }): Promise<{
259
- entry: ShellCacheEntry;
260
- shouldRevalidate?: boolean;
261
- } | null>;
262
- putShell(key: string, entry: ShellCacheEntry, ttlSeconds?: number, swrSeconds?: number, tags?: string[]): Promise<"stored" | "invalidated" | "uncacheable" | void>;
263
- invalidateTags(tags: string[]): Promise<void>;
264
211
  }
265
212
  /**
266
213
  * Thrown by a document HIT tail's match when its doc record cannot supply the
@@ -30,23 +30,47 @@
30
30
  * Invariant: the mask only turns the invalidating request's hits into misses.
31
31
  * If the durable write then fails, that request paid extra misses, never a
32
32
  * stale read, and other requests read the durable state. Both also record
33
- * the tags on the request (markInvalidated), so a "use cache" execution or
34
- * stale refresh this request started before one of its own tags was
35
- * invalidated is neither joined nor written (cache-runtime.ts). Its own tags
36
- * only: a nested "use cache" function's tags do not reach the enclosing
37
- * entry (#980), for this gate as for eviction. Not covered (#977): such an
38
- * execution in another request, or a loader cache() write, still lands, and
39
- * the store accepts it (docs/design/caching.md "Read-your-own-writes").
33
+ * the tags in the isolate's invalidation order (markInvalidated), so a cache
34
+ * execution that started before one of its tags was invalidated, in any
35
+ * request, is neither joined nor written (#973, #977): see
36
+ * predatesInvalidation, and docs/design/caching.md "Read-your-own-writes".
40
37
  */
41
- import { type RequestContext } from "../server/request-context.js";
42
- /** The current position in that order; an execution records it at start. */
43
- export declare function currentCacheSeq(): number;
38
+ import type { SegmentCacheStore } from "./types.js";
39
+ import { type ExecutionStart } from "./invalidation-order.js";
40
+ export { executionStart, invalidatedSince, type ExecutionStart, } from "./invalidation-order.js";
44
41
  /**
45
- * Whether the request invalidated any of `tags` after `since` (a
46
- * currentCacheSeq() value an execution recorded when it started): its value
47
- * may predate the invalidation, so the request neither joins nor writes it.
42
+ * The write gate of every cache execution (#977): whether one of `tags` was
43
+ * invalidated after the execution that produced the value started.
44
+ *
45
+ * Every built-in store stamps an entry when it is written (CFCacheStore's
46
+ * `taggedAt`, VercelCacheStore's `ta`) or checks nothing on write
47
+ * (MemorySegmentCacheStore), so a value read before an invalidation and
48
+ * written after it would be served as newer than the invalidation until it
49
+ * expires. Two answers, either one skips the write:
50
+ *
51
+ * - this isolate's order (invalidatedSince), which sees every request's
52
+ * updateTag()/revalidateTag() here, the calling one included;
53
+ * - the store's markers (isTagsInvalidatedSince), for another isolate's
54
+ * invalidation, as far as the store can tell: KV markers on CFCacheStore
55
+ * (KV-less, only this request's own), `tm` markers on VercelCacheStore,
56
+ * the process markers on MemorySegmentCacheStore. Asked about the
57
+ * milliseconds after the start's: this isolate's own invalidation that
58
+ * preceded the start usually shares its millisecond (Workers freeze
59
+ * Date.now() between I/O), and `>=` would skip every write started right
60
+ * after it. Another isolate's invalidation in the start's own millisecond
61
+ * is not caught. Asked fail-closed: a marker the store cannot read
62
+ * counts as an invalidation.
63
+ *
64
+ * The order is read again once the store answers: its read can be in flight
65
+ * while this isolate invalidates (an action awaits a "use cache" miss, whose
66
+ * write waits on the read, then runs updateTag()), and it then answers with
67
+ * the markers from before. markInvalidated is synchronous, so this second
68
+ * look sees every invalidation made before the answer.
69
+ *
70
+ * Invariant: a skipped write only costs a later miss, never a stale read.
71
+ * The caller still returns the value it computed.
48
72
  */
49
- export declare function invalidatedSince(ctx: RequestContext<any, any> | undefined, tags: readonly string[] | undefined, since: number): boolean;
73
+ export declare function predatesInvalidation(store: SegmentCacheStore, tags: readonly string[] | undefined, start: ExecutionStart): Promise<boolean>;
50
74
  /**
51
75
  * Immediately expire every cache entry tagged with any of `tags`, resolving
52
76
  * once in-process invalidation across all configured stores completes.
@@ -67,6 +67,12 @@ export interface SegmentCacheStore<TEnv = unknown> {
67
67
  * 2. Store-level `keyGenerator` (modifies default key)
68
68
  * 3. Default key generation (prefix:pathname:params)
69
69
  *
70
+ * Return `defaultKey` unchanged to leave a request unpartitioned. An empty
71
+ * string is not a key: where the result partitions a nested cache() on
72
+ * another store, or a ppr shell, it fails key resolution, so the request
73
+ * renders uncached (as when the keyGenerator throws), and the router warns
74
+ * once naming the store.
75
+ *
70
76
  * @example Using headers for cache segmentation
71
77
  * ```typescript
72
78
  * keyGenerator: (ctx, defaultKey) => {
@@ -277,12 +283,25 @@ export interface SegmentCacheStore<TEnv = unknown> {
277
283
  * freshness). Consulted by build-shell read-through and runtime shell stores:
278
284
  * "was it evicted" is answered by tag markers against the entry's createdAt,
279
285
  * including when invalidation races a capture that has not been written yet.
286
+ * Also the store's half of every cache write's gate (#977,
287
+ * tag-invalidation.ts predatesInvalidation): a "use cache", loader cache(),
288
+ * route cache() or document-cache write whose execution started before an
289
+ * invalidation another isolate made is skipped when this answers true, so
290
+ * it must reflect invalidations made after this request started reading
291
+ * (not a value memoized earlier in the request).
280
292
  * Optional: without it, TAGGED build entries are not served (untagged ones
281
- * are unaffected — they are evictable only by deploy/buildVersion anyway).
293
+ * are unaffected — they are evictable only by deploy/buildVersion anyway),
294
+ * and writes are gated by this isolate's invalidations only.
282
295
  * Fail open to `false` on marker-read errors: a transient store fault must
283
296
  * degrade to "still valid", the same posture as the envelope tag checks.
284
- */
285
- isTagsInvalidatedSince?(tags: string[], sinceMs: number): Promise<boolean>;
297
+ * The write gate passes `{ failClosed: true }` instead: a marker it cannot
298
+ * read (an error, a timed-out read) answers `true`, since a skipped write
299
+ * only costs a later miss. A store that ignores the option still works;
300
+ * its fault then lets that write through.
301
+ */
302
+ isTagsInvalidatedSince?(tags: string[], sinceMs: number, options?: {
303
+ failClosed?: boolean;
304
+ }): Promise<boolean>;
286
305
  }
287
306
  /**
288
307
  * Result from getItem() for function-level caching ("use cache").
@@ -347,11 +366,10 @@ export interface ShellCacheEntry {
347
366
  * on dev RSC-module edits). The second read-time gate: a persistent shared
348
367
  * store (KV/runtime-cache) survives deploys, and an app-code change that
349
368
  * keeps the same React version would otherwise leave a stale-build
350
- * prelude+postponed live under the same key. Optional only for entries
351
- * stored before the field existed — those are treated as a miss and the
352
- * recapture re-stamps them (pre-release, no compat shim).
369
+ * prelude+postponed live under the same key. A custom store returns it as
370
+ * putShell received it.
353
371
  */
354
- buildVersion?: string;
372
+ buildVersion: string;
355
373
  /**
356
374
  * The initialTheme the CAPTURE render was built with: the no-cookie default
357
375
  * (payloadInitialTheme, rsc/full-payload.ts), never the capturing visitor's
@@ -366,22 +384,20 @@ export interface ShellCacheEntry {
366
384
  */
367
385
  initialTheme?: string;
368
386
  /**
369
- * The CAPTURE DATA SNAPSHOT: every cache-store read-hit and write the capture
370
- * render performed, in stored/serialized form. Replaying these on a HIT (via
371
- * the SeededShellStore overlay, for the tail render only) reproduces the
372
- * shell's cached content byte-identically, so the freshly rendered hydration
373
- * payload matches the frozen prelude even after the underlying cache entries
374
- * have drifted (expired, been recomputed, or been tag-invalidated).
375
- *
376
- * Optional: an entry captured before this field existed simply has no
377
- * snapshot and keeps the pre-snapshot behavior (the tail reads live, so any
378
- * shell-baked cached value that drifted mismatches the prelude). Recapture
379
- * heals it. See docs/design/ppr-shell-resume.md ("the capture data snapshot").
387
+ * The CAPTURE DATA SNAPSHOT, in stored/serialized form: the doc segment
388
+ * record every HIT replays the handler layer from (`docKey`) and the
389
+ * bake-lane loader pins (pruned to what a HIT reads, issue #941). Replaying
390
+ * them on a HIT keeps the freshly rendered hydration payload equal to the
391
+ * frozen prelude after the underlying cache entries have drifted; every
392
+ * other read on a HIT, a hole's included, reads the store. Empty when the capture
393
+ * recorded nothing (a prerender-served capture without bake-lane loaders,
394
+ * a tombstone). A custom store returns it as putShell received it. See
395
+ * docs/design/ppr-shell-resume.md ("the capture data snapshot").
380
396
  */
381
- snapshot?: ShellSnapshotRecord[];
397
+ snapshot: ShellSnapshotRecord[];
382
398
  /**
383
399
  * Records the capture dropped from `snapshot` because no reader of this
384
- * entry consumes them, by family (`item:4/response:1`). Diagnostic only: the
400
+ * entry consumes them, by family (`loader:1`). Diagnostic only: the
385
401
  * HIT tail timing reports it next to the kept records. See
386
402
  * docs/design/shell-entry-layout.md ("Record only what a HIT reads").
387
403
  */
@@ -406,6 +422,12 @@ export interface ShellCacheEntry {
406
422
  /** Capture-generation start time; tag invalidations at or after it win. */
407
423
  createdAt: number;
408
424
  }
425
+ /**
426
+ * @internal A shell entry without its prelude and snapshot: what a
427
+ * prelude-first read (SegmentCacheStore.readShellDocument) resolves with
428
+ * before the snapshot bytes arrive.
429
+ */
430
+ export type ShellEntryHead = Omit<ShellCacheEntry, "prelude" | "snapshot">;
409
431
  /**
410
432
  * A shell entry whose document half is present — what the document HIT path
411
433
  * (serveShellHit / lookupBuildShell) consumes. hasIntactShellPayload
@@ -437,7 +459,7 @@ export type ShellSnapshotFailure = "unavailable" | "corrupt";
437
459
  */
438
460
  export interface ShellDocumentRead {
439
461
  /** The entry without its prelude and snapshot (delivered separately). */
440
- entry: ShellCacheEntry;
462
+ entry: ShellEntryHead;
441
463
  /** Raw prelude bytes (empty for a navigationOnly entry). */
442
464
  prelude: Uint8Array;
443
465
  shouldRevalidate?: boolean;
@@ -529,14 +551,13 @@ export interface ShellReadStats {
529
551
  };
530
552
  }
531
553
  /**
532
- * The families a shell snapshot pins. The item/segment/response families are
533
- * cache-store reads/writes (recorded by RecordingShellStore); the loader family
534
- * pins the settled CONTAINER of a bake-lane loader (lane rule: see
535
- * resolveLoaderData, loader-cache.ts). Excludes the shell family itself
536
- * (getShell/putShell) — the snapshot rides INSIDE a shell entry, so recording
537
- * it would be self-referential.
554
+ * The families a shell snapshot pins: the doc segment record (recorded by
555
+ * RecordingShellStore) and the settled CONTAINER of each bake-lane loader
556
+ * (lane rule: see resolveLoaderData, loader-cache.ts). No cache read is
557
+ * pinned: a HIT's holes, and a bake-lane loader body that runs on the HIT,
558
+ * read the store.
538
559
  */
539
- export type ShellSnapshotFamily = "item" | "segment" | "response" | "loader";
560
+ export type ShellSnapshotFamily = "segment" | "loader";
540
561
  /**
541
562
  * The stored form of a loader-family snapshot value: the bake-lane loader's
542
563
  * settled container, Flight-serialized AFTER eliding every still-pending nested
@@ -552,50 +573,34 @@ export interface ShellSnapshotLoaderValue {
552
573
  * container carries hole markers, so a HIT must gate the overlay on the
553
574
  * fresh run (only the loader body can mint the live nested promises);
554
575
  * 0 = fully pinned, so a HIT resolves the payload promise immediately from
555
- * the pin and does not run the loader body (unless `runs`). Absent on
556
- * pre-bit snapshots, which keep the gated path.
576
+ * the pin and does not run the loader body (unless `runs`).
557
577
  */
558
- holes?: 0 | 1;
578
+ holes: 0 | 1;
559
579
  /**
560
580
  * 1 when the capture saw a loader push it could not record (a deferred
561
581
  * push, one with masked nested promises, or one made outside any loader
562
582
  * body): a HIT then still runs the loader body in the background
563
583
  * (pin-first) so those pushes reach the page. 0: a hole-free record is
564
- * served from the pin alone and its body does not run on a HIT. Absent (a
565
- * snapshot written before the bit, which lacks the loader-owned pushes)
566
- * reads as 1, so the body supplies them.
584
+ * served from the pin alone and its body does not run on a HIT.
585
+ *
586
+ * Both bits are always written. A record stored before they existed (v0.17)
587
+ * reads each missing bit as 1 (buildShellLoaderSeed): its snapshot lacks
588
+ * the loader-owned pushes, so the body supplies them.
567
589
  */
568
- runs?: 0 | 1;
569
- }
570
- /** A serialized cached Response for the response family of a shell snapshot. */
571
- export interface ShellSnapshotResponseValue {
572
- status: number;
573
- /** Client-facing header pairs (per-client signal headers excluded at record). */
574
- headers: [string, string][];
575
- /** base64-encoded response body (binary-safe, JSON-serializable). */
576
- body: string;
577
- }
578
- /** The stored form of an item-family (use cache / loader cache) snapshot value. */
579
- export interface ShellSnapshotItemValue {
580
- /** RSC-serialized return value. */
581
- value: string;
582
- /** RSC-encoded handle data, if any. */
583
- handles?: string;
584
- /** The entry's cache tags. */
585
- tags?: string[];
590
+ runs: 0 | 1;
586
591
  }
587
592
  /**
588
- * One recorded cache-store read-hit or write from the capture render. `value`
589
- * carries the entry in its stored/serialized shape so it round-trips through a
590
- * JSON-serializing store (KV, CF, Vercel) with the rest of the ShellCacheEntry:
591
- * - `item` -> {@link ShellSnapshotItemValue}
593
+ * One snapshot record: the doc segment record or a bake-lane loader pin.
594
+ * `value` carries it in its stored/serialized shape so it round-trips
595
+ * through a JSON-serializing store (KV, CF, Vercel) with the rest of the
596
+ * ShellCacheEntry:
592
597
  * - `segment` -> {@link CachedEntryData} (already JSON-able)
593
- * - `response`-> {@link ShellSnapshotResponseValue}
598
+ * - `loader` -> {@link ShellSnapshotLoaderValue}
594
599
  */
595
600
  export interface ShellSnapshotRecord {
596
601
  family: ShellSnapshotFamily;
597
602
  key: string;
598
- value: ShellSnapshotItemValue | CachedEntryData | ShellSnapshotResponseValue | ShellSnapshotLoaderValue;
603
+ value: CachedEntryData | ShellSnapshotLoaderValue;
599
604
  }
600
605
  /**
601
606
  * Options for setItem() for function-level caching ("use cache").
@@ -691,8 +696,10 @@ export type SegmentHandleData = Record<string, unknown[]>;
691
696
  * index (null: not owned). Only a PPR shell capture writes owners: the
692
697
  * settled pushes of the loader bodies it ran (an `ssr: false` loader's own,
693
698
  * the loaders it awaits, and its own cache() replays), which the record keeps
694
- * because the prelude rendered them. restoreHandles replays an owned value
695
- * through HandleStore.pushReplayed, so a loader that does run on the HIT
696
- * replaces it with its live push.
699
+ * because the prelude rendered them. A document HIT restores a bake-lane
700
+ * owner's value through HandleStore.pushRestored, and it stands against a run
701
+ * of its loader there; a live-lane owner's (a hole), and every owner's on
702
+ * any other replay of the record (a client navigation), go through
703
+ * pushReplayed, so a run of the loader replaces it with its live push.
697
704
  */
698
705
  export type HandleOwners = Record<string, Record<string, (string | null)[]>>;