@rangojs/router 0.17.0 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (293) hide show
  1. package/README.md +3 -2
  2. package/dist/types/browser/cookie-name.d.ts +6 -0
  3. package/dist/types/browser/event-controller.d.ts +3 -3
  4. package/dist/types/browser/history-state.d.ts +18 -0
  5. package/dist/types/browser/navigation-transaction.d.ts +20 -2
  6. package/dist/types/browser/partial-update.d.ts +16 -1
  7. package/dist/types/browser/prefetch/cache.d.ts +6 -6
  8. package/dist/types/browser/react/Link.d.ts +19 -9
  9. package/dist/types/browser/react/context.d.ts +2 -2
  10. package/dist/types/browser/react/location-state-shared.d.ts +95 -56
  11. package/dist/types/browser/react/location-state.d.ts +1 -1
  12. package/dist/types/browser/transition-when.d.ts +80 -0
  13. package/dist/types/browser/types.d.ts +104 -9
  14. package/dist/types/build/generate-manifest.d.ts +8 -0
  15. package/dist/types/cache/background-task.d.ts +24 -2
  16. package/dist/types/cache/cache-exec-scope.d.ts +31 -0
  17. package/dist/types/cache/cache-key-utils.d.ts +48 -2
  18. package/dist/types/cache/cache-policy.d.ts +18 -1
  19. package/dist/types/cache/cache-runtime.d.ts +7 -0
  20. package/dist/types/cache/cache-scope.d.ts +192 -26
  21. package/dist/types/cache/cache-tag.d.ts +116 -11
  22. package/dist/types/cache/cf/cf-base64.d.ts +6 -0
  23. package/dist/types/cache/cf/cf-cache-constants.d.ts +11 -0
  24. package/dist/types/cache/cf/cf-cache-store.d.ts +224 -72
  25. package/dist/types/cache/cf/cf-cache-types.d.ts +31 -4
  26. package/dist/types/cache/cf/cf-shell-frame.d.ts +95 -0
  27. package/dist/types/cache/cf/cf-tag-marker-memo.d.ts +19 -0
  28. package/dist/types/cache/document-cache.d.ts +10 -0
  29. package/dist/types/cache/handle-snapshot.d.ts +41 -8
  30. package/dist/types/cache/index.d.ts +1 -0
  31. package/dist/types/cache/invalidation-order.d.ts +23 -0
  32. package/dist/types/cache/isolate-tag-memo.d.ts +149 -0
  33. package/dist/types/cache/request-tag-mask.d.ts +54 -0
  34. package/dist/types/cache/shell-memo.d.ts +178 -0
  35. package/dist/types/cache/shell-snapshot.d.ts +141 -105
  36. package/dist/types/cache/tag-invalidation.d.ts +54 -3
  37. package/dist/types/cache/types.d.ts +267 -96
  38. package/dist/types/cache/vercel/vercel-cache-store.d.ts +123 -18
  39. package/dist/types/client-urls/navigation.d.ts +28 -2
  40. package/dist/types/client-urls/server-projection.d.ts +5 -2
  41. package/dist/types/client-urls/types.d.ts +5 -5
  42. package/dist/types/client.d.ts +1 -1
  43. package/dist/types/index.d.ts +1 -1
  44. package/dist/types/index.rsc.d.ts +1 -1
  45. package/dist/types/prerender/build-shell-capture.d.ts +11 -4
  46. package/dist/types/route-definition/helpers-types.d.ts +11 -11
  47. package/dist/types/route-name.d.ts +7 -0
  48. package/dist/types/router/error-handling.d.ts +1 -1
  49. package/dist/types/router/instrument.d.ts +8 -0
  50. package/dist/types/router/intercept-resolution.d.ts +2 -0
  51. package/dist/types/router/match-context.d.ts +5 -0
  52. package/dist/types/router/match-middleware/background-revalidation.d.ts +0 -4
  53. package/dist/types/router/middleware-types.d.ts +6 -1
  54. package/dist/types/router/router-options.d.ts +4 -1
  55. package/dist/types/router/segment-resolution/loader-cache.d.ts +58 -12
  56. package/dist/types/router/segment-resolution/loader-mask.d.ts +3 -4
  57. package/dist/types/router/segment-resolution/loader-snapshot.d.ts +33 -48
  58. package/dist/types/router/segment-resolution/mask-nested.d.ts +73 -24
  59. package/dist/types/router/segment-resolution/view-transition-default.d.ts +7 -5
  60. package/dist/types/router/transition-when.d.ts +24 -10
  61. package/dist/types/rsc/attach-transition-when.d.ts +18 -0
  62. package/dist/types/rsc/full-payload.d.ts +21 -0
  63. package/dist/types/rsc/handler.d.ts +1 -1
  64. package/dist/types/rsc/response-cache-serve.d.ts +15 -12
  65. package/dist/types/rsc/shell-build-manifest.d.ts +12 -3
  66. package/dist/types/rsc/shell-capture-constants.d.ts +116 -12
  67. package/dist/types/rsc/shell-capture.d.ts +202 -74
  68. package/dist/types/rsc/shell-serve.d.ts +144 -50
  69. package/dist/types/rsc/types.d.ts +9 -0
  70. package/dist/types/segment-system.d.ts +7 -0
  71. package/dist/types/server/context.d.ts +155 -33
  72. package/dist/types/server/cookie-store.d.ts +33 -0
  73. package/dist/types/server/handle-store.d.ts +52 -1
  74. package/dist/types/server/request-context.d.ts +210 -63
  75. package/dist/types/server/request-headers.d.ts +44 -0
  76. package/dist/types/server.d.ts +1 -0
  77. package/dist/types/ssr/index.d.ts +9 -0
  78. package/dist/types/ssr/ssr-root.d.ts +9 -8
  79. package/dist/types/testing/e2e/index.d.ts +1 -1
  80. package/dist/types/testing/flight.entry.d.ts +7 -0
  81. package/dist/types/testing/index.d.ts +3 -2
  82. package/dist/types/testing/internal/action-id.d.ts +7 -0
  83. package/dist/types/testing/location-state-key.d.ts +24 -0
  84. package/dist/types/testing/render-route.d.ts +17 -1
  85. package/dist/types/testing/run-loader.d.ts +18 -3
  86. package/dist/types/testing/run-transition-when.d.ts +44 -69
  87. package/dist/types/testing/serve-shell-request.d.ts +133 -0
  88. package/dist/types/testing/shell-status.d.ts +44 -19
  89. package/dist/types/transition-when-ref.d.ts +79 -0
  90. package/dist/types/types/cache-types.d.ts +15 -3
  91. package/dist/types/types/handler-context.d.ts +14 -1
  92. package/dist/types/types/index.d.ts +1 -1
  93. package/dist/types/types/segments.d.ts +86 -71
  94. package/dist/types/urls/path-helper-types.d.ts +3 -4
  95. package/dist/types/urls/pattern-types.d.ts +21 -18
  96. package/dist/types/vite/plugins/transition-when-hoist.d.ts +45 -0
  97. package/dist/types/vite/router-discovery.d.ts +29 -2
  98. package/dist/vite/index.js +1005 -105
  99. package/package.json +1 -1
  100. package/skills/breadcrumbs/SKILL.md +17 -7
  101. package/skills/cache-guide/SKILL.md +175 -76
  102. package/skills/caching/SKILL.md +361 -33
  103. package/skills/catalog.json +1 -1
  104. package/skills/client-urls/SKILL.md +12 -7
  105. package/skills/cloudflare/SKILL.md +15 -0
  106. package/skills/comparison/references/framework-comparison.md +52 -37
  107. package/skills/defer-hydration/SKILL.md +50 -30
  108. package/skills/deployment-caching/SKILL.md +5 -2
  109. package/skills/document-cache/SKILL.md +32 -2
  110. package/skills/handler-use/SKILL.md +2 -0
  111. package/skills/hooks/SKILL.md +3 -1
  112. package/skills/hooks/handle-and-actions.md +3 -1
  113. package/skills/hooks/navigation.md +23 -2
  114. package/skills/hooks/state.md +101 -7
  115. package/skills/intercept/SKILL.md +29 -12
  116. package/skills/loader/SKILL.md +197 -44
  117. package/skills/middleware/SKILL.md +34 -9
  118. package/skills/migrate-nextjs/SKILL.md +68 -37
  119. package/skills/migrate-react-router/SKILL.md +2 -1
  120. package/skills/migrate-react-router/component-migration.md +3 -1
  121. package/skills/migrate-react-router/data-and-actions.md +3 -1
  122. package/skills/migrate-react-router/route-mapping.md +7 -4
  123. package/skills/observability/SKILL.md +65 -0
  124. package/skills/parallel/SKILL.md +13 -8
  125. package/skills/ppr/SKILL.md +715 -371
  126. package/skills/prerender/SKILL.md +30 -17
  127. package/skills/rango/SKILL.md +72 -34
  128. package/skills/route/SKILL.md +14 -7
  129. package/skills/router-setup/SKILL.md +4 -2
  130. package/skills/scripts/SKILL.md +8 -1
  131. package/skills/server-actions/SKILL.md +11 -0
  132. package/skills/shell-manifest/SKILL.md +21 -12
  133. package/skills/testing/SKILL.md +49 -43
  134. package/skills/testing/cache-prerender.md +15 -14
  135. package/skills/testing/client-components.md +45 -3
  136. package/skills/testing/loader.md +19 -1
  137. package/skills/testing/middleware.md +1 -0
  138. package/skills/testing/predicates.md +49 -49
  139. package/skills/testing/render-handler.md +1 -0
  140. package/skills/testing/serve-shell-request.md +202 -0
  141. package/skills/testing/server-actions.md +9 -3
  142. package/skills/theme/SKILL.md +56 -4
  143. package/skills/typesafety/env-and-bindings.md +18 -2
  144. package/skills/use-cache/SKILL.md +91 -23
  145. package/skills/vercel/SKILL.md +14 -2
  146. package/skills/view-transitions/SKILL.md +150 -29
  147. package/src/__augment-tests__/augmented.check.ts +23 -0
  148. package/src/browser/cookie-name.ts +23 -0
  149. package/src/browser/event-controller.ts +4 -4
  150. package/src/browser/history-state.ts +75 -1
  151. package/src/browser/link-interceptor.ts +4 -0
  152. package/src/browser/navigation-bridge.ts +77 -18
  153. package/src/browser/navigation-client.ts +4 -3
  154. package/src/browser/navigation-store.ts +43 -1
  155. package/src/browser/navigation-transaction.ts +31 -11
  156. package/src/browser/partial-update.ts +146 -35
  157. package/src/browser/prefetch/cache.ts +6 -6
  158. package/src/browser/prefetch/fetch.ts +2 -2
  159. package/src/browser/react/Link.tsx +38 -33
  160. package/src/browser/react/NavigationProvider.tsx +2 -2
  161. package/src/browser/react/context.ts +2 -2
  162. package/src/browser/react/location-state-shared.ts +241 -49
  163. package/src/browser/react/location-state.ts +5 -2
  164. package/src/browser/rsc-router.tsx +9 -0
  165. package/src/browser/server-action-bridge.ts +84 -15
  166. package/src/browser/transition-when.ts +205 -0
  167. package/src/browser/types.ts +124 -10
  168. package/src/build/generate-manifest.ts +77 -1
  169. package/src/cache/background-task.ts +55 -2
  170. package/src/cache/cache-exec-scope.ts +48 -1
  171. package/src/cache/cache-key-utils.ts +62 -2
  172. package/src/cache/cache-policy.ts +31 -4
  173. package/src/cache/cache-runtime.ts +209 -129
  174. package/src/cache/cache-scope.ts +573 -69
  175. package/src/cache/cache-tag.ts +475 -19
  176. package/src/cache/cf/cf-base64.ts +36 -4
  177. package/src/cache/cf/cf-cache-constants.ts +12 -0
  178. package/src/cache/cf/cf-cache-store.ts +1094 -376
  179. package/src/cache/cf/cf-cache-types.ts +32 -4
  180. package/src/cache/cf/cf-shell-frame.ts +258 -0
  181. package/src/cache/cf/cf-tag-marker-memo.ts +55 -40
  182. package/src/cache/document-cache.ts +53 -7
  183. package/src/cache/handle-snapshot.ts +53 -9
  184. package/src/cache/index.ts +2 -0
  185. package/src/cache/invalidation-order.ts +84 -0
  186. package/src/cache/isolate-tag-memo.ts +308 -0
  187. package/src/cache/request-tag-mask.ts +147 -0
  188. package/src/cache/shell-memo.ts +341 -0
  189. package/src/cache/shell-snapshot.ts +249 -277
  190. package/src/cache/tag-invalidation.ts +109 -18
  191. package/src/cache/types.ts +274 -102
  192. package/src/cache/vercel/vercel-cache-store.ts +561 -74
  193. package/src/client-urls/client-root.tsx +8 -4
  194. package/src/client-urls/client-urls.ts +8 -6
  195. package/src/client-urls/navigation.ts +55 -4
  196. package/src/client-urls/server-projection.ts +31 -10
  197. package/src/client-urls/types.ts +12 -5
  198. package/src/client.tsx +2 -0
  199. package/src/host/cookie-handler.ts +2 -1
  200. package/src/index.rsc.ts +3 -0
  201. package/src/index.ts +3 -0
  202. package/src/loader-redirect.tsx +7 -3
  203. package/src/prerender/build-shell-capture.ts +173 -110
  204. package/src/route-definition/dsl-helpers.ts +6 -3
  205. package/src/route-definition/helpers-types.ts +11 -11
  206. package/src/route-name.ts +12 -0
  207. package/src/router/content-negotiation.ts +4 -1
  208. package/src/router/error-handling.ts +5 -2
  209. package/src/router/handler-context.ts +42 -5
  210. package/src/router/instrument.ts +54 -2
  211. package/src/router/intercept-resolution.ts +30 -8
  212. package/src/router/loader-resolution.ts +89 -33
  213. package/src/router/logging.ts +5 -3
  214. package/src/router/match-api.ts +89 -42
  215. package/src/router/match-context.ts +5 -0
  216. package/src/router/match-handlers.ts +14 -29
  217. package/src/router/match-middleware/background-revalidation.ts +3 -5
  218. package/src/router/match-middleware/cache-lookup.ts +52 -19
  219. package/src/router/match-middleware/cache-store.ts +36 -10
  220. package/src/router/match-result.ts +1 -0
  221. package/src/router/middleware-types.ts +6 -1
  222. package/src/router/middleware.ts +20 -4
  223. package/src/router/navigation-snapshot.ts +6 -5
  224. package/src/router/prerender-match.ts +10 -0
  225. package/src/router/request-classification.ts +7 -4
  226. package/src/router/router-options.ts +4 -1
  227. package/src/router/segment-resolution/fresh.ts +18 -13
  228. package/src/router/segment-resolution/helpers.ts +4 -2
  229. package/src/router/segment-resolution/loader-cache.ts +366 -70
  230. package/src/router/segment-resolution/loader-mask.ts +3 -4
  231. package/src/router/segment-resolution/loader-snapshot.ts +215 -126
  232. package/src/router/segment-resolution/mask-nested.ts +161 -42
  233. package/src/router/segment-resolution/revalidation.ts +27 -13
  234. package/src/router/segment-resolution/view-transition-default.ts +6 -21
  235. package/src/router/telemetry.ts +6 -3
  236. package/src/router/transition-when.ts +34 -52
  237. package/src/router.ts +2 -1
  238. package/src/rsc/attach-transition-when.ts +47 -0
  239. package/src/rsc/full-payload.ts +47 -3
  240. package/src/rsc/handler.ts +24 -10
  241. package/src/rsc/loader-fetch.ts +2 -1
  242. package/src/rsc/manifest-init.ts +4 -0
  243. package/src/rsc/origin-guard.ts +8 -5
  244. package/src/rsc/progressive-enhancement.ts +10 -32
  245. package/src/rsc/response-cache-serve.ts +69 -78
  246. package/src/rsc/response-route-handler.ts +2 -2
  247. package/src/rsc/rsc-rendering.ts +712 -338
  248. package/src/rsc/server-action.ts +8 -20
  249. package/src/rsc/shell-build-manifest.ts +49 -29
  250. package/src/rsc/shell-capture-constants.ts +182 -12
  251. package/src/rsc/shell-capture.ts +1126 -612
  252. package/src/rsc/shell-serve.ts +331 -85
  253. package/src/rsc/ssr-setup.ts +3 -2
  254. package/src/rsc/types.ts +9 -0
  255. package/src/segment-system.tsx +40 -7
  256. package/src/server/context.ts +328 -72
  257. package/src/server/cookie-store.ts +184 -133
  258. package/src/server/handle-store.ts +245 -27
  259. package/src/server/request-context.ts +364 -100
  260. package/src/server/request-headers.ts +134 -0
  261. package/src/server.ts +4 -0
  262. package/src/ssr/index.tsx +96 -37
  263. package/src/ssr/ssr-root.tsx +9 -8
  264. package/src/testing/dispatch.ts +4 -7
  265. package/src/testing/e2e/index.ts +1 -0
  266. package/src/testing/flight.entry.ts +14 -0
  267. package/src/testing/index.ts +5 -0
  268. package/src/testing/internal/action-id.ts +25 -0
  269. package/src/testing/internal/context.ts +1 -1
  270. package/src/testing/location-state-key.ts +45 -0
  271. package/src/testing/render-handler.ts +1 -1
  272. package/src/testing/render-route.tsx +109 -16
  273. package/src/testing/run-client-revalidate.ts +3 -19
  274. package/src/testing/run-loader.ts +67 -5
  275. package/src/testing/run-transition-when.ts +109 -172
  276. package/src/testing/serve-shell-request.ts +381 -0
  277. package/src/testing/shell-status.ts +73 -47
  278. package/src/theme/ThemeProvider.tsx +7 -6
  279. package/src/transition-when-ref.ts +220 -0
  280. package/src/types/cache-types.ts +15 -3
  281. package/src/types/handler-context.ts +14 -1
  282. package/src/types/index.ts +3 -0
  283. package/src/types/segments.ts +94 -91
  284. package/src/urls/path-helper-types.ts +3 -4
  285. package/src/urls/pattern-types.ts +21 -18
  286. package/src/vite/discovery/discover-routers.ts +3 -0
  287. package/src/vite/discovery/shell-prerender-phase.ts +9 -2
  288. package/src/vite/discovery/virtual-module-codegen.ts +16 -6
  289. package/src/vite/plugins/transition-when-hoist.ts +1064 -0
  290. package/src/vite/rango.ts +2 -0
  291. package/src/vite/router-discovery.ts +89 -9
  292. package/dist/types/rsc/transition-gate.d.ts +0 -23
  293. package/src/rsc/transition-gate.ts +0 -86
@@ -16,12 +16,8 @@
16
16
  * intent cannot be honored — unlike an undeclared route, which is silent).
17
17
  */
18
18
  import { type EntryData } from "../server/context.js";
19
- import type { SearchParamsFilter } from "../cache/search-params-filter.js";
20
- import type { DocumentShellCacheEntry, ShellCacheEntry, SegmentCacheStore } from "../cache/types.js";
21
- /** Debug/status header the browser (and e2e assertions) can read: HIT | MISS. */
22
- export declare const SHELL_STATUS_HEADER = "x-rango-shell";
23
- /** Partial-navigation status header set only after captured PPR segments are consumed. */
24
- export declare const PPR_REPLAY_STATUS_HEADER = "x-rango-ppr-replay";
19
+ import type { DocumentShellCacheEntry, ShellCacheEntry, ShellDocumentRead, ShellEntryHead, ShellReadStats, ShellSnapshotFailure, ShellSnapshotRecord, SegmentCacheStore } from "../cache/types.js";
20
+ export { PPR_REPLAY_STATUS_HEADER, SHELL_STATUS_HEADER, buildShellKey, navigationShellKey, partitionShellKey, shellSearchSeed, } from "./shell-capture-constants.js";
25
21
  /**
26
22
  * Default shell ttl (seconds) for `ppr: true` and for a PartialPrerenderProps
27
23
  * that omits `ttl`.
@@ -80,53 +76,56 @@ export declare function normalizeCaptureTimeout(value: unknown): number | undefi
80
76
  */
81
77
  export declare function resolvePprConfig(entry: EntryData | undefined | null): ResolvedPprConfig | null;
82
78
  /**
83
- * Shell cache key: host + pathname + sorted search + a `:shell` namespace suffix
84
- * (so it can never collide with a document-cache key; the store further isolates
85
- * the shell family internally).
86
- *
87
- * The key includes the request HOST: in a multi-tenant host-router deployment
88
- * (one worker, one shared KV/runtime-cache store) a host-less key would serve
89
- * tenant A's captured shell to tenant B's users.
90
- *
91
- * `filter` is the request's compiled `cache.searchParams` config
92
- * (ctx._searchParamsFilter): excluded params collapse onto one shell slot.
93
- * Callers on the serve/capture path MUST pass it -- key drift between capture
94
- * and lookup makes every shell request a permanent miss.
79
+ * The forced-MISS query marker. A document HIT that cannot finish its tail
80
+ * reloads the page with it (shellReloadScript); the serve gate renders a
81
+ * request carrying it on axis 1, with no shell read and no capture, so the
82
+ * reload can never be a HIT that degrades again. The handler drops it from
83
+ * the request on entry (withoutShellMissMarker) and keeps only the flag
84
+ * (RequestContext._shellForcedMiss), so nothing else sees it.
85
+ */
86
+ export declare const SHELL_MISS_PARAM: string;
87
+ /**
88
+ * The request without the forced-MISS marker, or undefined when it carries
89
+ * none. The handler calls it before anything reads the request: left in, the
90
+ * marker reached `ctx.request.url`, `originalUrl`, middleware, and the SSR
91
+ * search seed (so `useSearchParams` rendered it), and a `"use cache"` key
92
+ * built from the request URL was never read again. GET and HEAD only: the
93
+ * reload is a navigation, and a request with a body cannot be re-created
94
+ * without re-streaming it. The substring test keeps the URL parse off every
95
+ * other request.
95
96
  */
96
- export declare function buildShellKey(url: URL, filter?: SearchParamsFilter): string;
97
+ export declare function withoutShellMissMarker(request: Request): Request | undefined;
97
98
  /**
98
- * The shell key's search portion (`?`-prefixed sorted search with the
99
- * cache.searchParams filter applied, or "") — ALSO the string the capture and
100
- * resume SSR renders seed their store location with (SSRRenderOptions.search
101
- * / ShellCaptureOptions.search / ShellResumeOptions.search). Search is part
102
- * of shell identity, so static-part `useSearchParams` reads render exactly
103
- * what the key names; deriving seed and key from this one helper is what
104
- * keeps capture, resume, and lookup byte-agreed (drift = replay mismatch or
105
- * permanent MISS).
99
+ * The inline script a degraded document HIT ends with: reload once into a
100
+ * forced MISS. The marker in the URL is the loop bound — a request carrying
101
+ * it is never a HIT, so this script never runs on the reload, and if it ever
102
+ * did (a server that ignores the marker) it would return without touching
103
+ * the page. window.stop() runs only when it reloads: it keeps the half-sent
104
+ * Flight stream from being closed by DOMContentLoaded, which would throw
105
+ * React's "Connection closed" (#412) before the reload lands.
106
106
  */
107
- export declare function shellSearchSeed(url: URL, filter?: SearchParamsFilter): string;
107
+ export declare function shellReloadScript(): string;
108
+ /**
109
+ * An inline script a shell HIT's tail appends. It carries no CSP nonce: a
110
+ * request with an active per-request nonce never reaches a HIT
111
+ * (rsc-rendering.ts shellServePlan serves it on axis 1).
112
+ */
113
+ export declare function inlineShellScript(body: string): string;
108
114
  /**
109
115
  * Version gates for a stored shell: reactVersion AND buildVersion must both
110
116
  * match the running server. The postponed blob encodes hole positions against
111
117
  * one exact tree, so resuming it under a different React OR a different app
112
118
  * build tree-mismatches inside resume() — after the 200 + prelude committed,
113
119
  * with no recovery. Either mismatch is a miss: the recapture overwrites the
114
- * same key (self-healing) and the entry otherwise ages out via TTL. An entry
115
- * with no buildVersion (stored before the field existed) is a miss for the
116
- * same reason — its build is unknown, so it cannot be proven resumable.
117
- */
118
- export declare function isValidShellHit(entry: ShellCacheEntry, buildVersion: string): boolean;
119
- /**
120
- * DOCUMENT-half integrity gate and type narrowing, run BEFORE the HIT response
121
- * commits: a stored entry whose prelude is not decodable base64 or whose
122
- * postponed blob is not parseable JSON would otherwise throw AFTER the 200 +
123
- * full static prelude flushed (`serveShellHit` decodes at stream construction,
124
- * `resumeShellHTML` parses in the tail) — the client gets a visually complete
125
- * page that never hydrates, re-served on every request until the entry ages
126
- * out (no eviction path exists; failure schedules no recapture by itself).
127
- * Checking here turns a corrupt entry (store-layer fault) into a plain MISS
128
- * the recapture overwrites. Cost: one duplicate decode/parse per HIT, sub-ms
129
- * against a prelude flush that dominates the path.
120
+ * same key (self-healing) and the entry otherwise ages out via TTL.
121
+ */
122
+ export declare function isValidShellHit(entry: Pick<ShellCacheEntry, "reactVersion" | "buildVersion">, buildVersion: string): boolean;
123
+ /**
124
+ * DOCUMENT-half structural gate and type narrowing, without decoding the
125
+ * prelude: the prelude is a string and the postponed blob parses. Partial
126
+ * replay (which never serves the prelude) and the build-manifest read-through
127
+ * use it as-is; the document HIT path runs it inside
128
+ * {@link openShellDocument}, whose single decode is the prelude's check.
130
129
  *
131
130
  * navigationOnly entries store no document half (prelude/postponed absent) and
132
131
  * therefore never pass. The partial-replay path skips this gate for them
@@ -135,8 +134,47 @@ export declare function isValidShellHit(entry: ShellCacheEntry, buildVersion: st
135
134
  * capture).
136
135
  */
137
136
  export declare function hasIntactShellPayload(entry: ShellCacheEntry): entry is DocumentShellCacheEntry;
138
- /** Decode a base64 prelude back into bytes for stream composition. */
139
- export declare function base64ToBytes(b64: string): Uint8Array;
137
+ /** A document shell ready to serve: its prelude decoded exactly once. */
138
+ export interface ShellDocument {
139
+ entry: ShellEntryHead;
140
+ prelude: Uint8Array;
141
+ /** React's postponed state, parse-checked (null = DATA variant). */
142
+ postponed: string | null;
143
+ /**
144
+ * The capture snapshot: on the entry, or still arriving on its own promise
145
+ * after a prelude-first read (SegmentCacheStore.readShellDocument), which
146
+ * only the tail awaits.
147
+ */
148
+ snapshot: ShellSnapshotRecord[] | Promise<ShellSnapshotRecord[] | undefined>;
149
+ /** Why a prelude-first read's snapshot is missing (ShellDocumentRead). */
150
+ snapshotFailure?: Promise<ShellSnapshotFailure | undefined>;
151
+ /** The prelude-first read's stats, when perf metrics are on. */
152
+ stats?: ShellReadStats;
153
+ }
154
+ /**
155
+ * Pre-commit integrity gate for a document HIT, and its only prelude decode.
156
+ * A stored entry whose prelude is not decodable base64 or whose postponed blob
157
+ * is not parseable JSON would otherwise throw AFTER the 200 + full static
158
+ * prelude flushed (`resumeShellHTML` parses in the tail) — the client gets a
159
+ * visually complete page that never hydrates, re-served on every request
160
+ * until the entry ages out. Returning null here turns a corrupt entry
161
+ * (store-layer fault) into a plain MISS the recapture overwrites. The decoded
162
+ * bytes are what serveShellHit enqueues, so the check costs no second decode.
163
+ * A prelude-first `read` (SegmentCacheStore.readShellDocument) already carries
164
+ * the raw bytes and a pending snapshot, so only its postponed blob is checked.
165
+ */
166
+ export declare function openShellDocument(entry: ShellCacheEntry): ShellDocument | null;
167
+ export declare function openShellDocument(entry: ShellEntryHead, read: Pick<ShellDocumentRead, "prelude" | "snapshot" | "snapshotFailure" | "stats">): ShellDocument | null;
168
+ /**
169
+ * Prelude enqueue granularity. A streaming compressor in front of the worker
170
+ * emits its first byte only after compressing the whole write it was handed:
171
+ * one 629 KB enqueue delayed the first compressed byte by the full prelude's
172
+ * compression time (brotli-6 5.1 ms, gzip-6 1.1 ms in Node zlib with a flush
173
+ * per write; workerd's CompressionStream emits nothing until the whole write is
174
+ * compressed), while 32 KB chunks emit within 0.1 ms and cost slightly more
175
+ * in total compression (0.55-0.70 ms) than one write (issue #941).
176
+ */
177
+ export declare const SHELL_PRELUDE_CHUNK_BYTES: number;
140
178
  /** True when the store implements the shell entry family. */
141
179
  export declare function hasShellFamily(store: SegmentCacheStore | undefined): store is SegmentCacheStore & {
142
180
  getShell: NonNullable<SegmentCacheStore["getShell"]>;
@@ -152,8 +190,22 @@ export declare function hasShellFamily(store: SegmentCacheStore | undefined): st
152
190
  export interface ShellTailTiming {
153
191
  key: string;
154
192
  outcome: "complete" | "redirect" | "error";
193
+ /** The capture snapshot available to the tail (read and parsed). */
194
+ snapshotMs?: number;
195
+ /** Snapshot bytes read after the commit (prelude-first stores). */
196
+ snapshotBytes?: number;
197
+ /** Snapshot bytes read after the commit, excluding the parse. */
198
+ snapshotReadMs?: number;
199
+ /** Snapshot JSON.parse CPU (reads 0 on a deployed worker: see bytes). */
200
+ snapshotParseMs?: number;
201
+ /** Snapshot records by family, e.g. `segment:1/loader:2` (no commas: it rides a Server-Timing desc). */
202
+ snapshotRecords?: string;
203
+ /** Records the capture pruned from the snapshot, same format (ShellCacheEntry.prunedRecords). */
204
+ snapshotPruned?: string;
155
205
  /** Loader-family seed decode (only when the entry carried a snapshot). */
156
206
  seedMs?: number;
207
+ /** The seed decode's own duration: Flight deserialization, CPU only. */
208
+ seedCpuMs?: number;
157
209
  /** Tail router.match() settled. */
158
210
  matchMs?: number;
159
211
  /** Tail stream (resume output) handed to the response stream. */
@@ -168,12 +220,13 @@ export interface ShellTailTiming {
168
220
  tailBytes?: number;
169
221
  }
170
222
  /**
171
- * Compact single-line form for the console log and the dev Server-Timing
172
- * mirror's `desc`. Plain alphanumerics/`=`/`-` only — no quoted-string
173
- * escaping needed.
223
+ * Compact single-line form for the console log and the Server-Timing mirror's
224
+ * `desc`: alphanumerics and `=`, `-`, `:`, `/` only (records=segment:1/loader:2 pruned=loader:1),
225
+ * so no quoted-string escaping is needed. Offsets are from the commit; the
226
+ * `-cpu` fields are CPU-only durations.
174
227
  */
175
228
  export declare function describeShellTailTiming(timing: ShellTailTiming): string;
176
- /** Buffer one terminal tail timing for the dev Server-Timing mirror. */
229
+ /** Buffer one terminal tail timing for the Server-Timing mirror. */
177
230
  export declare function publishShellTailTiming(timing: ShellTailTiming): void;
178
231
  /**
179
232
  * Consume (read-and-clear) the buffered tail timing for `key`, so one tail
@@ -198,3 +251,44 @@ export declare function warnShellStoreMissingOnce(key: string): void;
198
251
  * the missing-store warning above (an undeclared route stays silent).
199
252
  */
200
253
  export declare function warnPprNonceActiveOnce(key: string): void;
254
+ /**
255
+ * Dev only (the caller gates on NODE_ENV): warn once per route that the
256
+ * route's cache() entry reduces an explicit `ppr.ttl` or `ppr.swr`. `shell`
257
+ * is what the capture stores for a record written just now
258
+ * (shell-capture.ts capShellWindow), so the message states the values the
259
+ * shell actually gets. A ppr value the cap leaves whole (or raises: the
260
+ * cap can move a record's stale time into the shell's swr) is silent, and
261
+ * so is a ppr config that sets no ttl/swr.
262
+ */
263
+ export declare function warnPprWindowCappedOnce(routeName: string, ppr: {
264
+ ttl?: number;
265
+ swr?: number;
266
+ }, shell: {
267
+ ttl: number;
268
+ swr: number;
269
+ }, cache: {
270
+ ttl: number;
271
+ swr: number;
272
+ }): void;
273
+ /**
274
+ * Whether a partitioned request needs no build-shell probe: its route was
275
+ * already warned, or its path was already found without one.
276
+ */
277
+ export declare function partitionBuildShellCheckDone(pathname: string, routeName: string | undefined): boolean;
278
+ /**
279
+ * Record a partitioned request's build-shell probe, and warn once per route
280
+ * when it found one: the route has a build-time shell its requests cannot
281
+ * read. The route's request partition (a `cache({ key })` enclosing it, at
282
+ * any depth (#970), or the store's keyGenerator) keys the shell per
283
+ * partition, and the build captured only
284
+ * the default one, so each partition captures at runtime. Same
285
+ * declared-intent-cannot-be-honored doctrine as the warnings above; a path
286
+ * with no build shell stays silent.
287
+ */
288
+ export declare function notePartitionBuildShellCheck(pathname: string, routeName: string | undefined, found: boolean): void;
289
+ /**
290
+ * @internal Reset the serve path's once-per-key warnings (the shell path's
291
+ * one registry), its build-shell probe memo and buffered tail timings. Tests
292
+ * only (testing/serve-shell-request.ts resetShellTestState).
293
+ */
294
+ export declare function resetShellServeStateForTests(): void;
@@ -32,6 +32,8 @@ export interface RscPayload {
32
32
  resolvedIds?: string[];
33
33
  /** Merged route params from the matched route */
34
34
  params?: Record<string, string>;
35
+ /** Matched route name, when named (transition({ when }) route names). */
36
+ routeName?: string;
35
37
  slots?: Record<string, SlotState>;
36
38
  /**
37
39
  * Intercept TARGET route names reachable from this location as a
@@ -208,6 +210,13 @@ export interface SSRModule {
208
210
  origin?: string;
209
211
  /** Each component error the prerender reports (never the capture's own abort). */
210
212
  onError?: (error: unknown) => void;
213
+ /**
214
+ * React's errorInfo for each task still pending at the capture's abort
215
+ * (its componentStack is computed when read).
216
+ */
217
+ onAbortedTask?: (errorInfo: {
218
+ componentStack?: string;
219
+ } | undefined) => void;
211
220
  }) => Promise<{
212
221
  prelude: Uint8Array;
213
222
  postponed: string | null;
@@ -19,6 +19,13 @@ export interface RenderSegmentsOptions {
19
19
  forceAwait?: boolean;
20
20
  /** Seeded descendant client-route pending state for testing. */
21
21
  outletPending?: boolean;
22
+ /**
23
+ * This commit's transition({ when }) decision was false
24
+ * (browser/transition-when.ts): the navigation is urgent, so no segment
25
+ * animates. Every router <ViewTransition> stays in the tree with each class
26
+ * "none", so the element type never changes; keys never read this (#995).
27
+ */
28
+ transitionGatedOff?: boolean;
22
29
  /**
23
30
  * Intercept segments to inject into the tree.
24
31
  * These are parallel segments from intercept routes that need to be
@@ -1,7 +1,7 @@
1
+ import type { ServerRouteLocation } from "../types/segments.js";
1
2
  import { AsyncLocalStorage } from "node:async_hooks";
2
3
  import type { ReactNode } from "react";
3
4
  import type { PartialCacheOptions, ErrorBoundaryHandler, Handler, LoaderDefinition, MiddlewareFn, NotFoundBoundaryHandler, ShouldRevalidateFn, TransitionConfig } from "../types";
4
- import type { DefaultRouteName } from "../types/global-namespace.js";
5
5
  import type { ContextVar } from "../context-var.js";
6
6
  /**
7
7
  * Performance metric entry for a single measured operation
@@ -106,6 +106,15 @@ export type LoaderEntry = {
106
106
  * appears on navigation-lane entries.
107
107
  */
108
108
  awaitBeforeFlush?: true;
109
+ /**
110
+ * loader(Def, { ssr: false }) on every DSL evaluation, document and
111
+ * navigation: the loader's PPR bake lane (loader-cache.ts
112
+ * resolveLoaderData). The bake/seed key rides on it, so a client
113
+ * navigation's shell replay pins exactly the loaders a document HIT pins,
114
+ * whatever the entry's loading(). awaitBeforeFlush cannot carry that: a
115
+ * navigation evaluation never has it.
116
+ */
117
+ bake?: true;
109
118
  };
110
119
  /**
111
120
  * Segments state for intercept context
@@ -123,20 +132,22 @@ export type InterceptSegmentsState = {
123
132
  * Context passed to intercept selector functions (when())
124
133
  * Contains navigation context to determine if interception should occur.
125
134
  *
135
+ * `from` / `to` have the shape transition({ when }) sees, without `state`:
136
+ * history state never reaches the server.
137
+ *
126
138
  * Note: when() is evaluated during route matching, BEFORE middleware runs.
127
139
  * So ctx.get()/ctx.use() are not available, but env (platform bindings) is.
128
140
  *
129
141
  * @internal This type is an implementation detail and may change without notice.
130
142
  */
131
143
  export type InterceptSelectorContext<TEnv = any> = {
132
- from: URL;
133
- to: URL;
134
- params: Record<string, string>;
144
+ /** Where the navigation comes from: the intercept source while one is open. */
145
+ from: ServerRouteLocation;
146
+ /** The navigation target. */
147
+ to: ServerRouteLocation;
135
148
  request: Request;
136
149
  env: TEnv;
137
150
  segments: InterceptSegmentsState;
138
- fromRouteName?: DefaultRouteName;
139
- toRouteName?: DefaultRouteName;
140
151
  };
141
152
  /**
142
153
  * Selector function for conditional interception
@@ -408,24 +419,130 @@ export declare function getParallelSlotCount(parallels: ParallelEntries | EntryD
408
419
  export declare function track(label: string, depth?: number): () => void;
409
420
  /**
410
421
  * Check if the current execution is inside a cache() DSL boundary.
411
- * Returns false inside loader execution — loaders are always fresh
412
- * (never cached), so non-cacheable reads are safe.
422
+ * Returns false inside loader execution: a route cache() does not store
423
+ * loader values, so non-cacheable reads are safe. A loader bound with its own
424
+ * cache() stores its value; recordLoaderIdentityRead guards that.
413
425
  */
414
426
  export declare function isInsideCacheScope(): boolean;
415
427
  /**
416
- * Read guard for a non-cacheable variable (`createVar({ cache: false })` or a
417
- * `ctx.set(..., { cache: false })` write). Callers check isNonCacheable() first
418
- * so ordinary reads never reach the scope lookups.
428
+ * What a refused identity read says (guardIdentityRead): its `verb`, and per
429
+ * refusing scope the text after "<surface> cannot be <verb> ...": why the
430
+ * value must not reach that scope, and what to do instead. `fix.warning` is
431
+ * the fix the capture's refusal warning gives (shell-capture.ts
432
+ * refuseOnCaptureGuard).
433
+ */
434
+ export interface IdentityReadWording {
435
+ /** "called" for a function (cookies(), ctx.get()), "read" for a property (the theme getters). */
436
+ verb: LoaderIdentityReadVerb;
437
+ fix: {
438
+ useCache: string;
439
+ cacheScope: string;
440
+ capture: string;
441
+ warning: string;
442
+ };
443
+ /**
444
+ * False when the read is recorded later, by the returned view's read
445
+ * methods (cookies(), headers()): a view taken outside a loader and read
446
+ * inside one still counts, and a cookie write alone is not a read.
447
+ */
448
+ record?: false;
449
+ }
450
+ /**
451
+ * The one guard every request-identity read goes through: cookies(),
452
+ * headers(), the theme reads (cookie-store.ts readGuardedTheme), a
453
+ * non-cacheable ctx.get() (assertNonCacheableReadAllowed), and the raw reads
454
+ * `ctx.request.headers` and `getRequestContext().cookie()` / `.cookies()`
455
+ * (cookie-store.ts guardRequestHeaders, guardRawCookieRead; #976). Each surface keeps
456
+ * its own wording; the ladder and its exemptions are shared, so the surfaces
457
+ * refuse in exactly the same places:
419
458
  *
420
- * Throws inside a "use cache" body: the key does not include the value, so the
421
- * first caller's value would be stored and served to later callers (#925). A
422
- * loader body entered INSIDE the cached function is exempt, the same baked-copy
423
- * trade isInsideCacheScope() makes for cache(). A cached function called FROM a
424
- * loader is not: the loader re-runs, the cached body does not. The body scope
425
- * records the exec scope it was entered in, so the exemption holds only while
426
- * that is still the innermost one.
459
+ * 0. A cache's own key(), store keyGenerator, condition() and tags(), and
460
+ * onError, read freely and record nothing (runIdentityExempt,
461
+ * cache/cache-exec-scope.ts): the value picks or labels the entry, or is
462
+ * only observed; it is never rendered. A cached body, loader body or
463
+ * funnel they start is guarded again.
464
+ * 1. A PPR shell capture (`ctx` is the capture's derived context,
465
+ * `_shellCaptureRun`) trips first: the capture context is flagged (so a
466
+ * caught throw still refuses the capture) and the read throws. The shell is
467
+ * shared per host+URL, and every HIT replays what the capture read. There
468
+ * is no loader-body exemption here: a bake-lane loader and a loader a
469
+ * handler awaits both bake.
470
+ * 2. A "use cache" body throws: the key does not include the value, so the
471
+ * first caller's would be stored and served to later callers. That holds
472
+ * for a loader body entered inside the cached function too (`await
473
+ * ctx.use(Loader)` there): its value is part of what the function returns.
474
+ * Before, a non-cacheable ctx.get() there was exempt while cookies() threw,
475
+ * and the entry stored the first request's value.
476
+ * 3. A cache() boundary throws, except inside a loader body
477
+ * (isInsideCacheScope): a route cache() never stores loader values.
478
+ * 4. The read is allowed, and recorded on the current loader execution for a
479
+ * loader cache() fill without key() (#972, recordLoaderIdentityRead)
480
+ * unless `wording.record` defers it to the returned view.
481
+ *
482
+ * Outside these scopes every read is allowed, a live loader's included. The
483
+ * response directives (invalidateClientCache(), keepClientCache()) record
484
+ * nothing and are not captured reads: they take refuseInCacheScope alone.
485
+ */
486
+ export declare function guardIdentityRead(ctx: unknown, surface: string, wording: IdentityReadWording): void;
487
+ /**
488
+ * Steps 2 and 3 of guardIdentityRead: throw when a "use cache" body or a
489
+ * cache() boundary would store what `surface` produces.
490
+ */
491
+ export declare function refuseInCacheScope(surface: string, { verb, fix }: IdentityReadWording): void;
492
+ /**
493
+ * The capture refusal for a request-scoped read (cookies(), headers(), a
494
+ * { cache: false } variable): the message after the surface, and the fix the
495
+ * refusal warning gives. `what` names the data that would leak.
427
496
  */
428
- export declare function assertNonCacheableReadAllowed(keyOrVar: string | ContextVar<unknown>): void;
497
+ export declare function requestReadCaptureFix(what: string): Pick<IdentityReadWording["fix"], "capture" | "warning">;
498
+ /**
499
+ * Read guard for a non-cacheable variable (`createVar({ cache: false })` or a
500
+ * `ctx.set(..., { cache: false })` write), through guardIdentityRead.
501
+ * Callers check isNonCacheable() first so ordinary reads never reach the
502
+ * scope lookups. `requestCtx` is the ambient request context, whose capture
503
+ * flag the guard reads.
504
+ */
505
+ export declare function assertNonCacheableReadAllowed(keyOrVar: string | ContextVar<unknown>, requestCtx?: unknown): void;
506
+ /** How the identity error words a read: a function call or a property read. */
507
+ export type LoaderIdentityReadVerb = "called" | "read";
508
+ /** A request-identity read a loader execution made (recordLoaderIdentityRead). */
509
+ export interface LoaderIdentityRead {
510
+ /**
511
+ * The read as the error names it: "cookies()", "headers()",
512
+ * `ctx.get() for a non-cacheable variable "x"`, "ctx.theme" or
513
+ * "getRequestContext().theme".
514
+ */
515
+ surface: string;
516
+ /** "called" for a function, "read" for a property (the theme getters). */
517
+ verb: LoaderIdentityReadVerb;
518
+ /** The loader body that made the read. */
519
+ bodyId: string | undefined;
520
+ /**
521
+ * The cached loader whose value carries a read another loader made
522
+ * (loader-cache.ts identity mark): its readers read it through `via`.
523
+ */
524
+ via?: string;
525
+ }
526
+ /**
527
+ * Record a request-identity read (cookies(), headers(), a non-cacheable
528
+ * ctx.get()) on the current loader execution (#972).
529
+ *
530
+ * isInsideCacheScope() exempts loader bodies because a route cache() never
531
+ * stores their values. A loader bound with its own cache() does: with no key()
532
+ * and no store keyGenerator its entry is keyed by loader, host, path and
533
+ * params only, and an enclosing route cache() key does not partition it
534
+ * (#974). A cookies() read there stored the first visitor's session and served
535
+ * it to everyone for the TTL. The read is recorded on the execution, not
536
+ * thrown at the call: a reader that starts the loader before its binding does
537
+ * runs it outside any fill, and the binding's MISS then reuses that run. The
538
+ * fill checks what its execution recorded, through the same links as its tags
539
+ * (cache-tag.ts recordedIdentityRead), so both orders fail the same way.
540
+ * Response directives do not record: a key cannot make a skipped body's side
541
+ * effect reach a HIT.
542
+ */
543
+ export declare function recordLoaderIdentityRead(surface: string, verb?: LoaderIdentityReadVerb): void;
544
+ /** The error a loader cache() fill with no declared key fails with (#972). */
545
+ export declare function loaderCacheIdentityError(read: LoaderIdentityRead, cachedLoaderId: string): Error;
429
546
  /**
430
547
  * Check if the current execution is inside a DSL loader scope
431
548
  * (wrapped by runInsideLoaderScope). Used by rendered() barrier
@@ -439,6 +556,10 @@ export declare function isInsideLoaderScope(): boolean;
439
556
  * Takes scalars so the already-latched path allocates nothing.
440
557
  */
441
558
  export declare function latchCachedHeaderScope(kind: CachedHeaderScope["kind"], routeKey?: string): void;
559
+ /** True inside ANY loader execution — DSL loader scope or a loader body
560
+ * (however invoked). The "loaders always re-run fresh" exemptions key off
561
+ * this. */
562
+ export declare function isInsideAnyLoaderScope(): boolean;
442
563
  /**
443
564
  * The one ppr opt-in predicate: a page route entry that DECLARED `ppr`
444
565
  * (`false` and undefined mean plain axis 1). Shared by the serve path
@@ -506,21 +627,32 @@ export declare function assertCachedHeaderWriteAllowed(surface: string, surfaceP
506
627
  export declare function runInsideLoaderScope<T>(fn: () => T): T;
507
628
  /**
508
629
  * Run `fn` inside a loader BODY scope. Marks loader-function execution for the
509
- * cache-purity guard only (isInsideCacheScope), WITHOUT affecting
630
+ * cache() purity guard only (isInsideCacheScope), WITHOUT affecting
510
631
  * isInsideLoaderScope()/rendered() gating. Applied to every loader body (DSL
511
632
  * and handler-invoked via ctx.use) so request-scoped reads inside a loader
512
- * never trip the cache-scope guards — loaders always run fresh.
633
+ * never trip the cache() guard — a route cache() never stores loader values.
634
+ * A "use cache" body the loader was entered in still refuses them
635
+ * (guardIdentityRead).
513
636
  */
514
- export declare function runInsideLoaderBodyScope<T>(fn: () => T, loaderId?: string, handlerInvoked?: boolean): T;
637
+ export declare function runInsideLoaderBodyScope<T>(fn: () => T, loaderId?: string, tags?: Set<string>): T;
638
+ /** The innermost loader body's execution tag set (runInsideLoaderBodyScope). */
639
+ export declare function getLoaderBodyTags(): Set<string> | undefined;
515
640
  /**
516
641
  * The $$id of the loader whose body is currently executing, or undefined
517
642
  * outside any loader body. Used by the shell-capture identity guard
518
- * (cookie-store.ts) so its refusal warning can name the loader that read
643
+ * (guardIdentityRead) so its refusal warning can name the loader that read
519
644
  * cookies()/headers() instead of blaming a lane it cannot see — the old
520
645
  * hardcoded "bake-lane loader" text misled a live-lane debugging session
521
646
  * (issue #672, secondary).
522
647
  */
523
648
  export declare function getCurrentLoaderBodyId(): string | undefined;
649
+ /**
650
+ * The innermost loader body running here, or around it through the
651
+ * ctx.use(Loader) chain, whose id `match` accepts (HandleStore.push, the
652
+ * shell capture's push funnel). A body `stop` accepts, and `match` does not,
653
+ * ends the walk: nothing around it is returned.
654
+ */
655
+ export declare function findEnclosingLoaderBody(match: (loaderId: string) => boolean, stop?: (loaderId: string) => boolean): string | undefined;
524
656
  /**
525
657
  * True while `loaderId`'s body, or a loader it awaits via ctx.use at any
526
658
  * depth, is executing. The loader-level cache records these pushes
@@ -528,16 +660,6 @@ export declare function getCurrentLoaderBodyId(): string | undefined;
528
660
  * reader runs in that request only reaches the page through the replay.
529
661
  */
530
662
  export declare function isInsideLoaderBody(loaderId: string): boolean;
531
- /**
532
- * True while a HANDLER-invoked loader body (`await ctx.use(Loader)` from a
533
- * handler, not the DSL segment funnel) is executing. The consumption-lane
534
- * rule keys off this: handler consumption yields a BAKED copy in every shared
535
- * artifact — cache(), "use cache", and the PPR shell — so the shell-capture
536
- * identity guard (cookie-store.ts) permits cookies()/headers() here, exactly
537
- * like the cache-purity guards do. DSL segment loaders (live lane masked at
538
- * capture, bake lane guarded) never set the flag.
539
- */
540
- export declare function isInsideHandlerInvokedLoaderBody(): boolean;
541
663
  /**
542
664
  * Check if the current execution is inside a handle push callback (sync or an
543
665
  * async callback's continuation). Used by the handler-to-loader deadlock guard
@@ -7,6 +7,8 @@
7
7
  * Writes append Set-Cookie to the response stub.
8
8
  */
9
9
  import type { CookieOptions } from "../router/middleware-types.js";
10
+ import type { Theme } from "../theme/types.js";
11
+ import { type RequestContext } from "./request-context.js";
10
12
  /**
11
13
  * A single cookie entry returned by get() and getAll().
12
14
  */
@@ -63,6 +65,37 @@ export interface ReadonlyHeaders {
63
65
  [Symbol.iterator](): HeadersIterator<[string, string]>;
64
66
  }
65
67
  type HeadersIterator<T> = IterableIterator<T>;
68
+ /**
69
+ * Run the identity guard for a raw cookie read on `ctx`
70
+ * (request-context.ts `cookie()` / `cookies()`). Before #976 they were plain
71
+ * reads of the Cookie header that no shared scope refused. The guard reads
72
+ * the ambient context, as cookies() does, and falls back to `ctx`.
73
+ */
74
+ export declare function guardRawCookieRead(ctx: RequestContext<any>, surface: "getRequestContext().cookie()" | "getRequestContext().cookies()"): void;
75
+ /**
76
+ * Guard `request.headers` like headers() (#976): the request every ctx
77
+ * exposes (request context, handler, middleware and loader `ctx.request`)
78
+ * gets an own getter that runs guardIdentityRead against the ambient request
79
+ * context, so a read inside a ppr capture, a cache() boundary, a "use cache"
80
+ * body or an unkeyed loader cache() fill refuses as cookies() does. A Request
81
+ * argument keys a "use cache" entry by its URL only (cache-runtime.ts), so a
82
+ * header read there baked the first caller's value into the shared entry.
83
+ * The router's own reads go through requestHeaders() (request-headers.ts).
84
+ * Idempotent; returns `request`.
85
+ */
86
+ export declare function guardRequestHeaders(request: Request): Request;
87
+ /** The public reads of the visitor's theme cookie that readGuardedTheme guards. */
88
+ export type ThemeReadSurface = "ctx.theme" | "getRequestContext().theme";
89
+ /**
90
+ * `ctx`'s theme through guardIdentityRead: the one body of every public theme
91
+ * read (the handler and middleware `ctx.theme`, handler-context.ts and
92
+ * middleware.ts, and `getRequestContext().theme`, request-context.ts). The
93
+ * guard reads the ambient context, as cookies() does, and falls back to
94
+ * `ctx`; the value is `ctx`'s unguarded `_readTheme()`, which the router's
95
+ * own payload read (payloadInitialTheme, rsc/full-payload.ts) calls directly.
96
+ * Undefined without a theme config.
97
+ */
98
+ export declare function readGuardedTheme(ctx: RequestContext<any> | undefined, surface: ThemeReadSurface): Theme | undefined;
66
99
  /**
67
100
  * Get the original request headers (read-only).
68
101
  *