@rangojs/router 0.16.0 → 0.18.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 (309) hide show
  1. package/README.md +255 -89
  2. package/dist/testing/vitest.js +252 -0
  3. package/dist/types/browser/cookie-name.d.ts +6 -0
  4. package/dist/types/browser/history-state.d.ts +16 -0
  5. package/dist/types/browser/navigation-transaction.d.ts +6 -0
  6. package/dist/types/browser/partial-update.d.ts +11 -0
  7. package/dist/types/browser/react/ScrollRestoration.d.ts +6 -6
  8. package/dist/types/browser/rsc-router.d.ts +1 -1
  9. package/dist/types/browser/scroll-restoration.d.ts +7 -0
  10. package/dist/types/cache/cache-exec-scope.d.ts +18 -1
  11. package/dist/types/cache/cache-key-utils.d.ts +17 -2
  12. package/dist/types/cache/cache-policy.d.ts +17 -0
  13. package/dist/types/cache/cache-scope.d.ts +132 -19
  14. package/dist/types/cache/cache-tag.d.ts +92 -7
  15. package/dist/types/cache/cf/cf-base64.d.ts +6 -0
  16. package/dist/types/cache/cf/cf-cache-constants.d.ts +11 -0
  17. package/dist/types/cache/cf/cf-cache-store.d.ts +185 -44
  18. package/dist/types/cache/cf/cf-cache-types.d.ts +31 -4
  19. package/dist/types/cache/cf/cf-shell-frame.d.ts +95 -0
  20. package/dist/types/cache/cf/cf-tag-marker-memo.d.ts +34 -0
  21. package/dist/types/cache/handle-capture.d.ts +41 -3
  22. package/dist/types/cache/handle-snapshot.d.ts +54 -13
  23. package/dist/types/cache/index.d.ts +1 -0
  24. package/dist/types/cache/isolate-tag-memo.d.ts +149 -0
  25. package/dist/types/cache/segment-codec.d.ts +10 -3
  26. package/dist/types/cache/shell-memo.d.ts +178 -0
  27. package/dist/types/cache/shell-snapshot.d.ts +110 -21
  28. package/dist/types/cache/tag-invalidation.d.ts +30 -3
  29. package/dist/types/cache/taint.d.ts +1 -1
  30. package/dist/types/cache/types.d.ts +220 -44
  31. package/dist/types/cache/vercel/vercel-cache-store.d.ts +111 -13
  32. package/dist/types/client.d.ts +4 -4
  33. package/dist/types/client.rsc.d.ts +4 -5
  34. package/dist/types/components/DefaultDocument.d.ts +5 -2
  35. package/dist/types/context-var.d.ts +6 -5
  36. package/dist/types/escape-script.d.ts +1 -1
  37. package/dist/types/handles/MetaTags.d.ts +5 -4
  38. package/dist/types/handles/Scripts.d.ts +8 -7
  39. package/dist/types/handles/meta.d.ts +2 -2
  40. package/dist/types/handles/script.d.ts +17 -16
  41. package/dist/types/href-client.d.ts +1 -1
  42. package/dist/types/html.d.ts +12 -0
  43. package/dist/types/index.d.ts +1 -1
  44. package/dist/types/index.rsc.d.ts +3 -2
  45. package/dist/types/loader-store.d.ts +7 -4
  46. package/dist/types/prerender/build-shell-capture.d.ts +11 -4
  47. package/dist/types/reverse.d.ts +36 -4
  48. package/dist/types/route-definition/dsl-helpers.d.ts +3 -3
  49. package/dist/types/route-definition/helpers-types.d.ts +33 -9
  50. package/dist/types/route-definition/resolve-handler-use.d.ts +23 -1
  51. package/dist/types/route-types.d.ts +1 -1
  52. package/dist/types/router/debug-manifest.d.ts +3 -2
  53. package/dist/types/router/error-handling.d.ts +20 -0
  54. package/dist/types/router/handler-context.d.ts +6 -0
  55. package/dist/types/router/instrument.d.ts +8 -0
  56. package/dist/types/router/match-middleware/background-revalidation.d.ts +46 -5
  57. package/dist/types/router/middleware-types.d.ts +9 -3
  58. package/dist/types/router/router-context.d.ts +3 -1
  59. package/dist/types/router/router-interfaces.d.ts +24 -2
  60. package/dist/types/router/router-options.d.ts +8 -5
  61. package/dist/types/router/segment-resolution/fresh.d.ts +10 -0
  62. package/dist/types/router/segment-resolution/loader-cache.d.ts +66 -1
  63. package/dist/types/router/segment-resolution/loader-mask.d.ts +9 -19
  64. package/dist/types/router/segment-resolution/loader-snapshot.d.ts +23 -12
  65. package/dist/types/router/segment-resolution/mask-nested.d.ts +67 -26
  66. package/dist/types/router/segment-resolution/revalidation.d.ts +6 -2
  67. package/dist/types/router/segment-wrappers.d.ts +1 -1
  68. package/dist/types/rsc/full-payload.d.ts +14 -0
  69. package/dist/types/rsc/handler.d.ts +1 -1
  70. package/dist/types/rsc/response-route-handler.d.ts +1 -1
  71. package/dist/types/rsc/rsc-rendering.d.ts +1 -2
  72. package/dist/types/rsc/shell-build-manifest.d.ts +8 -0
  73. package/dist/types/rsc/shell-capture-constants.d.ts +61 -6
  74. package/dist/types/rsc/shell-capture.d.ts +151 -19
  75. package/dist/types/rsc/shell-serve.d.ts +140 -17
  76. package/dist/types/rsc/ssr-setup.d.ts +6 -6
  77. package/dist/types/rsc/types.d.ts +17 -0
  78. package/dist/types/server/context.d.ts +139 -15
  79. package/dist/types/server/cookie-store.d.ts +21 -0
  80. package/dist/types/server/handle-store.d.ts +26 -2
  81. package/dist/types/server/request-context.d.ts +182 -53
  82. package/dist/types/ssr/index.d.ts +24 -0
  83. package/dist/types/ssr/ssr-root.d.ts +9 -8
  84. package/dist/types/static-handler.d.ts +4 -1
  85. package/dist/types/testing/flight.entry.d.ts +7 -0
  86. package/dist/types/testing/generated-routes.d.ts +2 -2
  87. package/dist/types/testing/internal/context.d.ts +1 -1
  88. package/dist/types/testing/render-route.d.ts +32 -6
  89. package/dist/types/testing/run-loader.d.ts +18 -3
  90. package/dist/types/testing/serve-shell-request.d.ts +132 -0
  91. package/dist/types/testing/shell-status.d.ts +23 -10
  92. package/dist/types/testing/vitest-stubs/plugin-rsc.d.ts +28 -5
  93. package/dist/types/testing/vitest.d.ts +35 -1
  94. package/dist/types/theme/ThemeScript.d.ts +3 -3
  95. package/dist/types/types/cache-types.d.ts +14 -10
  96. package/dist/types/types/handler-context.d.ts +28 -1
  97. package/dist/types/types/loader-types.d.ts +8 -6
  98. package/dist/types/urls/pattern-types.d.ts +22 -18
  99. package/dist/types/urls/response-types.d.ts +3 -12
  100. package/dist/types/use-loader.d.ts +12 -3
  101. package/dist/types/vite/plugins/use-cache-transform.d.ts +33 -2
  102. package/dist/types/vite/rango.d.ts +1 -1
  103. package/dist/types/vite/router-discovery.d.ts +29 -2
  104. package/dist/vite/index.js +1420 -1348
  105. package/package.json +2 -2
  106. package/skills/api-client/SKILL.md +22 -10
  107. package/skills/breadcrumbs/SKILL.md +75 -22
  108. package/skills/bundle-analysis/SKILL.md +22 -22
  109. package/skills/cache-guide/SKILL.md +306 -124
  110. package/skills/caching/SKILL.md +530 -98
  111. package/skills/catalog.json +16 -16
  112. package/skills/client-urls/SKILL.md +65 -38
  113. package/skills/cloudflare/SKILL.md +87 -20
  114. package/skills/cloudflare/references/d1-and-local-dev.md +15 -2
  115. package/skills/cloudflare/references/streaming-and-deploy.md +10 -3
  116. package/skills/cloudflare/references/webhooks-and-crypto.md +9 -2
  117. package/skills/comparison/SKILL.md +5 -0
  118. package/skills/comparison/references/framework-comparison.md +75 -57
  119. package/skills/composability/SKILL.md +38 -29
  120. package/skills/css/SKILL.md +10 -13
  121. package/skills/debug-manifest/SKILL.md +74 -31
  122. package/skills/defer-hydration/SKILL.md +52 -31
  123. package/skills/deployment-caching/SKILL.md +11 -3
  124. package/skills/document-cache/SKILL.md +98 -28
  125. package/skills/fonts/SKILL.md +16 -8
  126. package/skills/handler-use/SKILL.md +55 -17
  127. package/skills/hooks/SKILL.md +28 -20
  128. package/skills/hooks/data.md +23 -3
  129. package/skills/hooks/handle-and-actions.md +34 -17
  130. package/skills/hooks/navigation.md +39 -10
  131. package/skills/hooks/outlets.md +13 -6
  132. package/skills/hooks/state.md +39 -15
  133. package/skills/hooks/urls.md +7 -5
  134. package/skills/host-router/SKILL.md +64 -12
  135. package/skills/i18n/SKILL.md +60 -34
  136. package/skills/intercept/SKILL.md +161 -26
  137. package/skills/layout/SKILL.md +106 -50
  138. package/skills/links/SKILL.md +51 -19
  139. package/skills/loader/SKILL.md +386 -128
  140. package/skills/middleware/SKILL.md +129 -26
  141. package/skills/migrate-nextjs/SKILL.md +156 -70
  142. package/skills/migrate-nextjs/backend-host-swap.md +5 -2
  143. package/skills/migrate-react-router/SKILL.md +51 -39
  144. package/skills/migrate-react-router/component-migration.md +28 -7
  145. package/skills/migrate-react-router/data-and-actions.md +21 -6
  146. package/skills/migrate-react-router/route-mapping.md +19 -10
  147. package/skills/mime-routes/SKILL.md +12 -7
  148. package/skills/observability/SKILL.md +92 -14
  149. package/skills/parallel/SKILL.md +88 -47
  150. package/skills/ppr/SKILL.md +866 -379
  151. package/skills/prerender/SKILL.md +210 -108
  152. package/skills/rango/SKILL.md +115 -65
  153. package/skills/react-compiler/SKILL.md +6 -5
  154. package/skills/response-routes/SKILL.md +133 -25
  155. package/skills/route/SKILL.md +170 -60
  156. package/skills/router-setup/SKILL.md +420 -240
  157. package/skills/scripts/SKILL.md +39 -21
  158. package/skills/server-actions/SKILL.md +231 -59
  159. package/skills/shell-manifest/SKILL.md +28 -14
  160. package/skills/streams-and-websockets/SKILL.md +32 -14
  161. package/skills/tailwind/SKILL.md +37 -33
  162. package/skills/testing/SKILL.md +73 -33
  163. package/skills/testing/cache-prerender.md +60 -33
  164. package/skills/testing/client-components.md +59 -6
  165. package/skills/testing/e2e-parity.md +12 -2
  166. package/skills/testing/flight.md +19 -11
  167. package/skills/testing/handles.md +8 -5
  168. package/skills/testing/loader.md +26 -6
  169. package/skills/testing/middleware.md +8 -3
  170. package/skills/testing/predicates.md +120 -0
  171. package/skills/testing/render-handler.md +70 -22
  172. package/skills/testing/response-routes.md +11 -4
  173. package/skills/testing/reverse-and-types.md +20 -14
  174. package/skills/testing/serve-shell-request.md +188 -0
  175. package/skills/testing/server-actions.md +12 -3
  176. package/skills/testing/server-tree.md +9 -0
  177. package/skills/testing/setup.md +9 -5
  178. package/skills/theme/SKILL.md +155 -30
  179. package/skills/typesafety/SKILL.md +20 -9
  180. package/skills/typesafety/env-and-bindings.md +137 -50
  181. package/skills/typesafety/generated-files-and-cli.md +26 -14
  182. package/skills/typesafety/params-and-search.md +44 -19
  183. package/skills/typesafety/route-types.md +50 -14
  184. package/skills/use-cache/SKILL.md +224 -83
  185. package/skills/vercel/SKILL.md +35 -13
  186. package/skills/view-transitions/SKILL.md +42 -21
  187. package/src/__augment-tests__/augmented.check.ts +26 -1
  188. package/src/browser/cookie-name.ts +23 -0
  189. package/src/browser/history-state.ts +23 -0
  190. package/src/browser/index.ts +2 -2
  191. package/src/browser/navigation-bridge.ts +3 -4
  192. package/src/browser/navigation-transaction.ts +22 -2
  193. package/src/browser/partial-update.ts +35 -25
  194. package/src/browser/react/ScrollRestoration.tsx +6 -6
  195. package/src/browser/rsc-router.tsx +6 -1
  196. package/src/browser/scroll-restoration.ts +53 -6
  197. package/src/cache/cache-exec-scope.ts +37 -4
  198. package/src/cache/cache-key-utils.ts +23 -8
  199. package/src/cache/cache-policy.ts +22 -0
  200. package/src/cache/cache-runtime.ts +264 -68
  201. package/src/cache/cache-scope.ts +419 -43
  202. package/src/cache/cache-tag.ts +375 -11
  203. package/src/cache/cf/cf-base64.ts +36 -4
  204. package/src/cache/cf/cf-cache-constants.ts +12 -0
  205. package/src/cache/cf/cf-cache-store.ts +990 -348
  206. package/src/cache/cf/cf-cache-types.ts +32 -4
  207. package/src/cache/cf/cf-shell-frame.ts +258 -0
  208. package/src/cache/cf/cf-tag-marker-memo.ts +143 -39
  209. package/src/cache/document-cache.ts +15 -0
  210. package/src/cache/handle-capture.ts +90 -10
  211. package/src/cache/handle-snapshot.ts +133 -28
  212. package/src/cache/index.ts +2 -0
  213. package/src/cache/isolate-tag-memo.ts +308 -0
  214. package/src/cache/segment-codec.ts +26 -7
  215. package/src/cache/shell-memo.ts +341 -0
  216. package/src/cache/shell-snapshot.ts +217 -27
  217. package/src/cache/tag-invalidation.ts +108 -22
  218. package/src/cache/taint.ts +1 -1
  219. package/src/cache/types.ts +225 -44
  220. package/src/cache/vercel/vercel-cache-store.ts +545 -61
  221. package/src/client.rsc.tsx +10 -9
  222. package/src/client.tsx +5 -4
  223. package/src/components/DefaultDocument.tsx +7 -2
  224. package/src/context-var.ts +6 -5
  225. package/src/escape-script.ts +1 -1
  226. package/src/handles/MetaTags.tsx +13 -11
  227. package/src/handles/Scripts.tsx +11 -10
  228. package/src/handles/meta.ts +2 -2
  229. package/src/handles/script.ts +17 -16
  230. package/src/href-client.ts +1 -1
  231. package/src/html.ts +12 -0
  232. package/src/index.rsc.ts +5 -0
  233. package/src/index.ts +1 -0
  234. package/src/loader-store.ts +7 -4
  235. package/src/prerender/build-shell-capture.ts +73 -22
  236. package/src/reverse.ts +44 -4
  237. package/src/route-definition/dsl-helpers.ts +121 -33
  238. package/src/route-definition/helpers-types.ts +33 -9
  239. package/src/route-definition/resolve-handler-use.ts +121 -6
  240. package/src/route-types.ts +0 -3
  241. package/src/router/debug-manifest.ts +10 -2
  242. package/src/router/error-handling.ts +51 -15
  243. package/src/router/handler-context.ts +71 -28
  244. package/src/router/instrument.ts +54 -2
  245. package/src/router/intercept-resolution.ts +141 -43
  246. package/src/router/loader-resolution.ts +115 -39
  247. package/src/router/match-api.ts +40 -26
  248. package/src/router/match-handlers.ts +5 -0
  249. package/src/router/match-middleware/background-revalidation.ts +161 -96
  250. package/src/router/match-middleware/cache-lookup.ts +110 -10
  251. package/src/router/match-middleware/cache-store.ts +48 -101
  252. package/src/router/middleware-types.ts +9 -6
  253. package/src/router/middleware.ts +75 -13
  254. package/src/router/prerender-match.ts +59 -19
  255. package/src/router/route-snapshot.ts +11 -1
  256. package/src/router/router-context.ts +2 -0
  257. package/src/router/router-interfaces.ts +25 -2
  258. package/src/router/router-options.ts +8 -5
  259. package/src/router/segment-resolution/fresh.ts +62 -21
  260. package/src/router/segment-resolution/helpers.ts +4 -2
  261. package/src/router/segment-resolution/loader-cache.ts +407 -76
  262. package/src/router/segment-resolution/loader-mask.ts +12 -22
  263. package/src/router/segment-resolution/loader-snapshot.ts +208 -56
  264. package/src/router/segment-resolution/mask-nested.ts +138 -44
  265. package/src/router/segment-resolution/revalidation.ts +61 -16
  266. package/src/router/segment-wrappers.ts +4 -0
  267. package/src/rsc/full-payload.ts +25 -1
  268. package/src/rsc/handler.ts +14 -2
  269. package/src/rsc/progressive-enhancement.ts +3 -2
  270. package/src/rsc/render-pipeline.ts +3 -0
  271. package/src/rsc/response-cache-serve.ts +9 -6
  272. package/src/rsc/response-route-handler.ts +11 -3
  273. package/src/rsc/rsc-rendering.ts +608 -225
  274. package/src/rsc/shell-build-manifest.ts +27 -9
  275. package/src/rsc/shell-capture-constants.ts +73 -6
  276. package/src/rsc/shell-capture.ts +1068 -296
  277. package/src/rsc/shell-serve.ts +316 -34
  278. package/src/rsc/ssr-setup.ts +14 -8
  279. package/src/rsc/types.ts +18 -0
  280. package/src/server/context.ts +300 -32
  281. package/src/server/cookie-store.ts +87 -61
  282. package/src/server/handle-store.ts +232 -7
  283. package/src/server/request-context.ts +297 -67
  284. package/src/ssr/index.tsx +136 -44
  285. package/src/ssr/ssr-root.tsx +9 -8
  286. package/src/static-handler.ts +4 -1
  287. package/src/testing/flight-runtime.d.ts +21 -0
  288. package/src/testing/flight.entry.ts +14 -0
  289. package/src/testing/generated-routes.ts +2 -2
  290. package/src/testing/internal/context.ts +1 -1
  291. package/src/testing/internal/flight-client-globals.ts +7 -5
  292. package/src/testing/render-route.tsx +85 -24
  293. package/src/testing/run-loader.ts +68 -6
  294. package/src/testing/serve-shell-request.ts +378 -0
  295. package/src/testing/shell-status.ts +34 -12
  296. package/src/testing/vitest-stubs/plugin-rsc.ts +123 -20
  297. package/src/testing/vitest.ts +62 -1
  298. package/src/theme/ThemeProvider.tsx +7 -6
  299. package/src/theme/ThemeScript.tsx +3 -3
  300. package/src/types/cache-types.ts +14 -10
  301. package/src/types/handler-context.ts +28 -1
  302. package/src/types/loader-types.ts +8 -6
  303. package/src/urls/pattern-types.ts +22 -18
  304. package/src/urls/response-types.ts +3 -23
  305. package/src/use-loader.tsx +12 -3
  306. package/src/vite/discovery/shell-prerender-phase.ts +9 -0
  307. package/src/vite/plugins/use-cache-transform.ts +118 -74
  308. package/src/vite/rango.ts +7 -3
  309. package/src/vite/router-discovery.ts +14 -4
@@ -112,7 +112,11 @@ export interface SegmentCacheStore<TEnv = unknown> {
112
112
  get(key: string): Promise<CacheGetResult | null | CacheReadError>;
113
113
 
114
114
  /**
115
- * Store entry data with TTL
115
+ * Store entry data with TTL. Resolve once a later `get(key)` from the same
116
+ * location observes the entry: the route cache() write runs in a background
117
+ * task, and a PPR shell capture awaits that task before it reads the entry
118
+ * back (#957). A store that resolves earlier makes the capture re-render
119
+ * the page instead of replaying the entry.
116
120
  * @param key - Cache key
117
121
  * @param data - Serialized entry data
118
122
  * @param ttl - Time-to-live in seconds
@@ -183,8 +187,14 @@ export interface SegmentCacheStore<TEnv = unknown> {
183
187
  * upgrade — the reactVersion field on the entry gates that at read time).
184
188
  * @param key - Cache key
185
189
  * @param entry - The shell prelude/postponed/version/createdAt bundle
186
- * @param ttlSeconds - Time-to-live in seconds
187
- * @param swrSeconds - Optional stale-while-revalidate window in seconds
190
+ * @param ttlSeconds - Time-to-live in seconds: the route's `ppr.ttl`
191
+ * (undefined: the store's default), capped to the route cache() entry
192
+ * the capture replayed or wrote. A capped window arrives in whole
193
+ * seconds, rounded up, with ttl + swr at least 1; its ttl can be 0 (a
194
+ * shell stale from the start, captured from a record inside its swr
195
+ * window).
196
+ * @param swrSeconds - Optional stale-while-revalidate window in seconds,
197
+ * under the same cap
188
198
  * @param tags - Optional cache tags for invalidation (participates in
189
199
  * invalidateTags via the same tag machinery as the item family)
190
200
  * @returns `invalidated` when a generation marker rejected the write,
@@ -203,6 +213,47 @@ export interface SegmentCacheStore<TEnv = unknown> {
203
213
  tags?: string[],
204
214
  ): Promise<"stored" | "invalidated" | "uncacheable" | void>;
205
215
 
216
+ /**
217
+ * @internal The document serve path's shell read, for built-in stores. The
218
+ * serve path prefers it over getShell: it resolves with the entry and the
219
+ * raw (decoded) prelude, the snapshot on its own promise, and the read's
220
+ * `debugPerformance` stats. CFCacheStore's layout keeps the snapshot behind
221
+ * the prelude, so its HIT can commit before the snapshot is read;
222
+ * VercelCacheStore reads the whole entry and decodes the prelude once per
223
+ * memoized shell (issue #941, docs/design/shell-entry-layout.md). Not part
224
+ * of the custom-store contract.
225
+ */
226
+ readShellDocument?(
227
+ key: string,
228
+ options?: ShellDocumentReadOptions,
229
+ ): Promise<ShellDocumentRead | null>;
230
+
231
+ /**
232
+ * @internal How long (ms) this store's isolate memos (shell, tag markers)
233
+ * can serve a read that predates a tag invalidation made elsewhere. After
234
+ * updateTag()/revalidateTag(), the response's fresh-reads cookie lasts the
235
+ * longest of these, and the same user's requests skip the memos meanwhile.
236
+ * Absent or 0: nothing to cover.
237
+ */
238
+ readonly freshReadsWindowMs?: number;
239
+
240
+ /**
241
+ * @internal Drop this isolate's memoized copy of a shell (shell-memo.ts) so
242
+ * the next read goes to the store. A HIT whose doc record failed to decode
243
+ * calls it after replacing the entry with a tombstone: the memoized copy
244
+ * holds the same record. Built-in stores only; not part of the custom-store
245
+ * contract.
246
+ */
247
+ dropShellMemo?(key: string): void;
248
+
249
+ /**
250
+ * @internal The largest shell entry (prelude, postponed state, and snapshot
251
+ * bytes) this store can hold in one value. The capture refuses a bigger
252
+ * entry instead of letting the write fail inside waitUntil. Absent: the
253
+ * capture applies DEFAULT_SHELL_ENTRY_MAX_BYTES (Cloudflare KV's 25 MiB).
254
+ */
255
+ readonly maxShellEntryBytes?: number;
256
+
206
257
  /**
207
258
  * Declares the shell family present-but-inert: getShell/putShell exist but
208
259
  * no-op (a custom store whose backing tier is conditionally unavailable).
@@ -252,6 +303,20 @@ export interface SegmentCacheStore<TEnv = unknown> {
252
303
  * APIs delegate to. Receives ALL of one invalidation call's tags at once so
253
304
  * stores can batch their work (e.g. a single CDN purge request rather than
254
305
  * one per tag). Stores that do not support tags simply omit this method.
306
+ *
307
+ * Both verbs call it synchronously, inside the request that invalidates
308
+ * (#973); revalidateTag() then hands the returned promise to waitUntil
309
+ * without awaiting it. Read-your-own-writes for that request comes from
310
+ * what the store does before its first await: record the tags as
311
+ * invalidated in state its reads consult for the rest of the request (the
312
+ * built-in stores key a map by the request's root context and the store,
313
+ * so contexts derived from the request share it, and compare each hit's
314
+ * tags and write time against it), then start the
315
+ * durable write. Such a mask must only ever turn hits into misses, and only
316
+ * in that request: a durable write that later fails then costs extra
317
+ * misses, never a stale read. A store that records nothing before its
318
+ * first await still works; the request that ran revalidateTag() can read
319
+ * entries the invalidation covers until the durable write lands.
255
320
  * @param tags - The cache tags to invalidate
256
321
  */
257
322
  invalidateTags?(tags: string[]): Promise<void>;
@@ -340,12 +405,13 @@ export interface ShellCacheEntry {
340
405
  */
341
406
  buildVersion?: string;
342
407
  /**
343
- * The initialTheme the CAPTURE render was built with (the derived context's
344
- * reqCtx.theme). The resume tail must render ThemeProvider with the SAME
408
+ * The initialTheme the CAPTURE render was built with: the no-cookie default
409
+ * (payloadInitialTheme, rsc/full-payload.ts), never the capturing visitor's
410
+ * theme (#971). The resume tail must render ThemeProvider with the SAME
345
411
  * initialTheme the frozen prelude was rendered with: React resume requires the
346
412
  * tree above the holes to match the prerendered tree, and initialTheme is
347
413
  * per-request METADATA, not part of the cached segments — a visitor whose
348
- * theme differs from the capturer's would otherwise produce a divergent resume
414
+ * theme differs from the capture's would otherwise produce a divergent resume
349
415
  * tree (broken stitching/hydration). The visitor's real theme is applied
350
416
  * pre-paint by the FOUC script and re-synced from the cookie post-mount by
351
417
  * ThemeProvider.
@@ -366,17 +432,21 @@ export interface ShellCacheEntry {
366
432
  */
367
433
  snapshot?: ShellSnapshotRecord[];
368
434
  /**
369
- * The key of the CANONICAL document segment record inside `snapshot` — the
370
- * one navigation replay can actually consume (resolved under the implicit
371
- * doc namespace at capture; see CacheScope.cacheRoute). Replay eligibility
372
- * requires this exact record: the snapshot also carries incidentally
373
- * recorded explicit-tier records (RecordingShellStore passthroughs) whose
374
- * keys a partial lookup can never resolve, and counting those declared
375
- * entries "replayable" that always missed (`snapshot-miss` flip-flop).
376
- * Absent on entries captured before the field existed OR when the capture
377
- * recorded no doc record (cache(false)/condition-false routes, prerender
378
- * short-circuit) — both read as `no-segment-snapshot`; recapture heals the
379
- * former.
435
+ * Records the capture dropped from `snapshot` because no reader of this
436
+ * entry consumes them, by family (`item:4/response:1`). Diagnostic only: the
437
+ * HIT tail timing reports it next to the kept records. See
438
+ * docs/design/shell-entry-layout.md ("Record only what a HIT reads").
439
+ */
440
+ prunedRecords?: string;
441
+ /**
442
+ * The key of the document segment record inside `snapshot`: the handler
443
+ * layer every HIT replays (a document HIT tail looks the record up by this
444
+ * key, so a store keyGenerator or a build-time capture host cannot send it
445
+ * elsewhere) and the record navigation replay consumes (resolved under the
446
+ * implicit doc namespace at capture; see CacheScope.cacheRoute). A capture
447
+ * that ran handlers always stores it; only a prerender-served entry (the
448
+ * prerender store supplies the handler layer) has none. A document entry
449
+ * without it is served as a MISS.
380
450
  */
381
451
  docKey?: string;
382
452
  /**
@@ -385,26 +455,6 @@ export interface ShellCacheEntry {
385
455
  * the partial request's headers and middleware state are not document state.
386
456
  */
387
457
  navigationOnly?: true;
388
- /**
389
- * True when the capture's HANDLER layer declared per-request liveness: a
390
- * handle pushed OUTSIDE a DSL loader scope carried a nested thenable (the
391
- * capture mask turns it into a never-filling hole), such a push was still
392
- * pending when the entry was written, or a handler-invoked loader
393
- * (ctx.use(loader) from a handler body — the consumption lane, #672)
394
- * executed during the capture. The serve tail then must NOT take the
395
- * handler-free fast path (the implicit doc-cache hit): only a handler
396
- * re-run can mint that hole's live promise or refresh that consumed value.
397
- * DSL-loader pushes never set this — loaders re-run fresh on every HIT, so
398
- * their holes always fill.
399
- */
400
- handlerLiveHoles?: boolean;
401
- /**
402
- * Legacy/fallback marker for a transition({ when }) predicate that was not
403
- * evaluated by the PPR pre-handler gate. Such an entry stays conservatively
404
- * ineligible for handler-free replay. New PPR matches evaluate known segment
405
- * predicates before the pipeline and do not set this marker.
406
- */
407
- transitionWhen?: true;
408
458
  /** Capture-generation start time; tag invalidations at or after it win. */
409
459
  createdAt: number;
410
460
  }
@@ -421,12 +471,119 @@ export type DocumentShellCacheEntry = ShellCacheEntry & {
421
471
  postponed: string | null;
422
472
  };
423
473
 
474
+ /** @internal Options of {@link SegmentCacheStore.readShellDocument}. */
475
+ export interface ShellDocumentReadOptions {
476
+ /**
477
+ * Tag names the route declares for its shell (`ppr.tags`), known before the
478
+ * entry is read: their marker reads can start alongside the entry read.
479
+ */
480
+ tagHints?: readonly string[];
481
+ }
482
+
483
+ /**
484
+ * @internal Why a prelude-first read's snapshot is missing although the entry
485
+ * stored one: `unavailable` (the read timed out or failed; the entry may be
486
+ * sound) or `corrupt` (the bytes arrived and do not parse; the store evicted
487
+ * the entry).
488
+ */
489
+ export type ShellSnapshotFailure = "unavailable" | "corrupt";
490
+
491
+ /**
492
+ * @internal Result of {@link SegmentCacheStore.readShellDocument}.
493
+ */
494
+ export interface ShellDocumentRead {
495
+ /** The entry without its prelude and snapshot (delivered separately). */
496
+ entry: ShellCacheEntry;
497
+ /** Raw prelude bytes (empty for a navigationOnly entry). */
498
+ prelude: Uint8Array;
499
+ shouldRevalidate?: boolean;
500
+ /**
501
+ * The capture snapshot, read after the prelude. Resolves undefined when the
502
+ * entry has none or it could not be read (`snapshotFailure` says which); a
503
+ * document HIT then cannot replay its doc record and degrades
504
+ * (rsc-rendering.ts serveShellHit).
505
+ * Never rejects.
506
+ */
507
+ snapshot: Promise<ShellSnapshotRecord[] | undefined>;
508
+ /**
509
+ * Why `snapshot` resolved undefined although the entry stored one, or
510
+ * undefined when it did not fail. A document HIT that cannot replay its
511
+ * doc record replaces the entry only when it is broken, not when its read
512
+ * was merely slow (rsc-rendering.ts serveShellHit). Never rejects.
513
+ */
514
+ snapshotFailure?: Promise<ShellSnapshotFailure | undefined>;
515
+ /**
516
+ * Where the read's time and bytes went, for the `debugPerformance` metrics
517
+ * (rsc-rendering.ts) and the store's own debug trace. Present only when one
518
+ * of them is on for the request.
519
+ */
520
+ stats?: ShellReadStats;
521
+ }
522
+
523
+ /**
524
+ * @internal Sub-steps of a prelude-first shell read. Times are ms. workerd
525
+ * advances its clock only on I/O, so a CPU-only field
526
+ * (`snapshot.parseMs`) reads 0 on a deployed worker: the byte counts are the
527
+ * cost signal there.
528
+ */
529
+ export interface ShellReadStats {
530
+ /**
531
+ * The tier that answered: `memo` (the store's shell memo), `l1`/`kv`
532
+ * (CFCacheStore), or `store` (a single-tier store, VercelCacheStore).
533
+ */
534
+ tier: "memo" | "l1" | "kv" | "store";
535
+ /** The per-isolate memo's outcome for this read and its size after it. */
536
+ memo?: { hit: boolean; bytes: number };
537
+ /**
538
+ * A KV read after an L1 attempt: how long the L1 attempt took and why it
539
+ * missed. The KV fields below then restart from the KV read.
540
+ */
541
+ l1MissMs?: number;
542
+ l1MissReason?: string;
543
+ /** Cache API match (L1) or KV get (KV) until the body was available. */
544
+ matchMs?: number;
545
+ /** Frame head read (I/O) and parse. */
546
+ headMs?: number;
547
+ /** Prelude bytes read. */
548
+ preludeMs?: number;
549
+ /** Tag-marker read, start to resolve (runs alongside the prelude read). */
550
+ markerMs?: number;
551
+ /** How long the read waited for the marker after the prelude was read. */
552
+ markerWaitMs?: number;
553
+ /**
554
+ * The marker check was awaited after the entry read (VercelCacheStore), not
555
+ * alongside the prelude read; hinted reads still start with the entry read.
556
+ */
557
+ markerSerial?: true;
558
+ /**
559
+ * How the isolate marker memo answered the shell's tags (the most
560
+ * store-bound outcome across them): `fresh`, `stale` (served, refreshing in
561
+ * the background), `read` (from the store), `bypass` (fresh-reads cookie).
562
+ */
563
+ markerMemo?: "fresh" | "stale" | "read" | "bypass";
564
+ /** Tags whose marker reads were started before the entry's head (hints). */
565
+ markerHinted?: readonly string[];
566
+ /** When the hinted marker reads started (performance.now()). */
567
+ markerHintStartedAt?: number;
568
+ /** How many of the shell's tags were hinted. */
569
+ markerHintHits?: number;
570
+ /** How long the hinted marker reads ran before the entry named its tags. */
571
+ markerLeadMs?: number;
572
+ /** The request carried the fresh-reads cookie: no isolate memo was used. */
573
+ freshReads?: true;
574
+ headBytes?: number;
575
+ preludeBytes?: number;
576
+ /** Number of the shell's tags the marker read covered. */
577
+ tags?: number;
578
+ /** Filled after the commit, once the snapshot has been read. */
579
+ snapshot?: { readMs: number; parseMs: number; bytes: number };
580
+ }
581
+
424
582
  /**
425
583
  * The families a shell snapshot pins. The item/segment/response families are
426
584
  * cache-store reads/writes (recorded by RecordingShellStore); the loader family
427
- * pins the settled CONTAINER of a bake-lane loader (a loader on an entry with
428
- * no renderable loading(), executed during capture — see
429
- * docs/design/loader-container-bake.md). Excludes the shell family itself
585
+ * pins the settled CONTAINER of a bake-lane loader (lane rule: see
586
+ * resolveLoaderData, loader-cache.ts). Excludes the shell family itself
430
587
  * (getShell/putShell) — the snapshot rides INSIDE a shell entry, so recording
431
588
  * it would be self-referential.
432
589
  */
@@ -447,12 +604,20 @@ export interface ShellSnapshotLoaderValue {
447
604
  * container carries hole markers, so a HIT must gate the overlay on the
448
605
  * fresh run (only the loader body can mint the live nested promises);
449
606
  * 0 = fully pinned, so a HIT resolves the payload promise immediately from
450
- * the pin while the fresh run proceeds ungated (side effects and cache
451
- * read-through writes preserved; its values were discarded either way —
452
- * recorded paths win wholesale). Absent on pre-bit snapshots, which keep
453
- * the gated path.
607
+ * the pin and does not run the loader body (unless `runs`). Absent on
608
+ * pre-bit snapshots, which keep the gated path.
454
609
  */
455
610
  holes?: 0 | 1;
611
+ /**
612
+ * 1 when the capture saw a loader push it could not record (a deferred
613
+ * push, one with masked nested promises, or one made outside any loader
614
+ * body): a HIT then still runs the loader body in the background
615
+ * (pin-first) so those pushes reach the page. 0: a hole-free record is
616
+ * served from the pin alone and its body does not run on a HIT. Absent (a
617
+ * snapshot written before the bit, which lacks the loader-owned pushes)
618
+ * reads as 1, so the body supplies them.
619
+ */
620
+ runs?: 0 | 1;
456
621
  }
457
622
 
458
623
  /** A serialized cached Response for the response family of a shell snapshot. */
@@ -542,6 +707,11 @@ export interface CachedEntryData {
542
707
  * (see handle-snapshot.ts encodeHandles) so Promise/ReactNode handle values
543
708
  * round-trip through JSON-serializing stores instead of being flattened. */
544
709
  handles: string;
710
+ /**
711
+ * Owning loader ids index-aligned with the decoded `handles` values
712
+ * (HandleOwners). Absent: every value restores as a plain replay.
713
+ */
714
+ handleOwners?: HandleOwners;
545
715
  /** Expiration timestamp (ms since epoch) */
546
716
  expiresAt: number;
547
717
  /** Cache tags for invalidation */
@@ -583,3 +753,14 @@ export interface CacheDefaults {
583
753
  * Structure: { handleName: [values...] }
584
754
  */
585
755
  export type SegmentHandleData = Record<string, unknown[]>;
756
+
757
+ /**
758
+ * segmentId -> handleName -> the owning loader id of each recorded value, by
759
+ * index (null: not owned). Only a PPR shell capture writes owners: the
760
+ * settled pushes of the loader bodies it ran (an `ssr: false` loader's own,
761
+ * the loaders it awaits, and its own cache() replays), which the record keeps
762
+ * because the prelude rendered them. restoreHandles replays an owned value
763
+ * through HandleStore.pushReplayed, so a loader that does run on the HIT
764
+ * replaces it with its live push.
765
+ */
766
+ export type HandleOwners = Record<string, Record<string, (string | null)[]>>;