@rangojs/router 0.5.1 → 0.6.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 (609) hide show
  1. package/README.md +5 -1
  2. package/dist/bin/rango.js +343 -125
  3. package/dist/types/browser/event-controller.d.ts +6 -0
  4. package/dist/types/browser/react/use-router.d.ts +10 -3
  5. package/dist/types/browser/react/use-search-params.d.ts +57 -10
  6. package/dist/types/browser/types.d.ts +22 -0
  7. package/dist/types/build/merge-full-manifests.d.ts +3 -0
  8. package/dist/types/build/route-trie.d.ts +4 -73
  9. package/dist/types/build/route-types/per-module-writer.d.ts +6 -4
  10. package/dist/types/build/route-types/router-processing.d.ts +2 -3
  11. package/dist/types/cache/cache-exec-scope.d.ts +31 -0
  12. package/dist/types/cache/cf/cf-cache-constants.d.ts +8 -1
  13. package/dist/types/cache/cf/cf-cache-store.d.ts +15 -1
  14. package/dist/types/cache/cf/cf-cache-types.d.ts +1 -1
  15. package/dist/types/cache/cf/cf-kv-utils.d.ts +21 -0
  16. package/dist/types/cache/taint.d.ts +12 -6
  17. package/dist/types/client-urls/client-root.d.ts +38 -0
  18. package/dist/types/client-urls/client-urls.d.ts +5 -0
  19. package/dist/types/client-urls/navigation.d.ts +38 -0
  20. package/dist/types/client-urls/revalidation-protocol.d.ts +25 -0
  21. package/dist/types/client-urls/server-projection.d.ts +62 -0
  22. package/dist/types/client-urls/types.d.ts +144 -0
  23. package/dist/types/client.d.ts +12 -4
  24. package/dist/types/client.rsc.d.ts +4 -1
  25. package/dist/types/decode-loader-results.d.ts +37 -0
  26. package/dist/types/errors.d.ts +1 -0
  27. package/dist/types/handles/is-thenable.d.ts +2 -4
  28. package/dist/types/index.d.ts +1 -1
  29. package/dist/types/loader-redirect.d.ts +27 -0
  30. package/dist/types/outlet-context.d.ts +12 -0
  31. package/dist/types/outlet-provider.d.ts +3 -1
  32. package/dist/types/redirect-origin.d.ts +4 -0
  33. package/dist/types/route-content-wrapper.d.ts +42 -1
  34. package/dist/types/route-definition/helpers-types.d.ts +13 -2
  35. package/dist/types/router/error-handling.d.ts +35 -1
  36. package/dist/types/router/intercept-resolution.d.ts +12 -0
  37. package/dist/types/router/loader-resolution.d.ts +24 -2
  38. package/dist/types/router/revalidation.d.ts +7 -0
  39. package/dist/{types.backup/build/route-trie.d.ts → types/router/route-trie-builder.d.ts} +4 -16
  40. package/dist/types/router/router-interfaces.d.ts +20 -0
  41. package/dist/types/router/segment-resolution/helpers.d.ts +1 -1
  42. package/dist/types/router/trie-matching.d.ts +1 -1
  43. package/dist/types/rsc/helpers.d.ts +3 -0
  44. package/dist/types/rsc/manifest-init.d.ts +5 -5
  45. package/dist/types/rsc/render-pipeline.d.ts +9 -0
  46. package/dist/types/rsc/routine-plan.d.ts +124 -0
  47. package/dist/types/rsc/shell-capture-constants.d.ts +17 -0
  48. package/dist/types/rsc/shell-capture.d.ts +9 -0
  49. package/dist/types/rsc/shell-serve.d.ts +11 -0
  50. package/dist/types/rsc/types.d.ts +30 -0
  51. package/dist/types/segment-system.d.ts +2 -0
  52. package/dist/types/server/context.d.ts +10 -0
  53. package/dist/types/server/handle-store.d.ts +34 -3
  54. package/dist/types/server/request-context.d.ts +14 -1
  55. package/dist/types/server.d.ts +1 -0
  56. package/dist/types/ssr/index.d.ts +22 -0
  57. package/dist/types/ssr/ssr-root.d.ts +10 -0
  58. package/dist/types/testing/dom.entry.d.ts +1 -1
  59. package/dist/types/testing/render-route.d.ts +16 -6
  60. package/dist/types/testing/run-loader.d.ts +9 -0
  61. package/dist/types/types/boundaries.d.ts +22 -0
  62. package/dist/types/types/index.d.ts +1 -1
  63. package/dist/types/types/loader-types.d.ts +57 -5
  64. package/dist/types/types/segments.d.ts +7 -0
  65. package/dist/types/urls/path-helper-types.d.ts +10 -4
  66. package/dist/types/vite/discovery/client-urls-projection.d.ts +53 -0
  67. package/dist/types/vite/discovery/discover-routers.d.ts +1 -1
  68. package/dist/types/vite/discovery/state.d.ts +8 -1
  69. package/dist/types/vite/encryption-key.d.ts +2 -0
  70. package/dist/types/vite/plugins/expose-internal-ids.d.ts +10 -0
  71. package/dist/types/vite/plugins/server-ref-hashing.d.ts +24 -0
  72. package/dist/types/vite/plugins/server-reference-pattern.d.ts +1 -0
  73. package/dist/types/vite/utils/shared-utils.d.ts +12 -0
  74. package/dist/vite/index.js +5101 -2056
  75. package/package.json +3 -3
  76. package/skills/breadcrumbs/SKILL.md +39 -9
  77. package/skills/caching/SKILL.md +1 -1
  78. package/skills/catalog.json +7 -1
  79. package/skills/client-urls/SKILL.md +338 -0
  80. package/skills/comparison/references/framework-comparison.md +23 -9
  81. package/skills/hooks/SKILL.md +2 -2
  82. package/skills/hooks/data.md +11 -2
  83. package/skills/hooks/handle-and-actions.md +7 -0
  84. package/skills/hooks/outlets.md +26 -5
  85. package/skills/hooks/urls.md +40 -3
  86. package/skills/loader/SKILL.md +132 -20
  87. package/skills/migrate-nextjs/SKILL.md +70 -10
  88. package/skills/migrate-react-router/SKILL.md +49 -13
  89. package/skills/migrate-react-router/component-migration.md +18 -13
  90. package/skills/migrate-react-router/data-and-actions.md +14 -3
  91. package/skills/migrate-react-router/route-mapping.md +15 -2
  92. package/skills/mime-routes/SKILL.md +3 -1
  93. package/skills/parallel/SKILL.md +32 -1
  94. package/skills/ppr/SKILL.md +16 -6
  95. package/skills/prerender/SKILL.md +8 -4
  96. package/skills/rango/SKILL.md +21 -17
  97. package/skills/react-compiler/SKILL.md +3 -3
  98. package/skills/response-routes/SKILL.md +4 -2
  99. package/skills/route/SKILL.md +5 -2
  100. package/skills/router-setup/SKILL.md +16 -2
  101. package/skills/scripts/SKILL.md +16 -6
  102. package/skills/shell-manifest/SKILL.md +16 -7
  103. package/skills/testing/SKILL.md +2 -2
  104. package/skills/testing/client-components.md +6 -0
  105. package/skills/testing/handles.md +30 -8
  106. package/skills/testing/loader.md +51 -49
  107. package/skills/testing/middleware.md +1 -1
  108. package/skills/theme/SKILL.md +8 -5
  109. package/skills/typesafety/generated-files-and-cli.md +16 -9
  110. package/skills/typesafety/route-types.md +5 -1
  111. package/skills/use-cache/SKILL.md +47 -0
  112. package/src/bin/rango.ts +7 -3
  113. package/src/browser/event-controller.ts +40 -15
  114. package/src/browser/navigation-bridge.ts +6 -0
  115. package/src/browser/navigation-client.ts +5 -0
  116. package/src/browser/partial-update.ts +77 -10
  117. package/src/browser/react/NavigationProvider.tsx +7 -0
  118. package/src/browser/react/use-router.ts +40 -11
  119. package/src/browser/react/use-search-params.ts +140 -17
  120. package/src/browser/rsc-router.tsx +59 -0
  121. package/src/browser/server-action-bridge.ts +26 -0
  122. package/src/browser/types.ts +22 -0
  123. package/src/build/merge-full-manifests.ts +161 -0
  124. package/src/build/route-trie.ts +9 -332
  125. package/src/build/route-types/include-resolution.ts +66 -11
  126. package/src/build/route-types/per-module-writer.ts +11 -6
  127. package/src/build/route-types/router-processing.ts +184 -153
  128. package/src/build/runtime-discovery.ts +23 -12
  129. package/src/cache/cache-exec-scope.ts +47 -0
  130. package/src/cache/cache-runtime.ts +113 -40
  131. package/src/cache/cf/cf-cache-constants.ts +8 -1
  132. package/src/cache/cf/cf-cache-store.ts +41 -64
  133. package/src/cache/cf/cf-cache-types.ts +1 -1
  134. package/src/cache/cf/cf-kv-utils.ts +38 -0
  135. package/src/cache/segment-codec.ts +21 -5
  136. package/src/cache/taint.ts +28 -9
  137. package/src/client-urls/client-root.tsx +168 -0
  138. package/src/client-urls/client-urls.ts +698 -0
  139. package/src/client-urls/navigation.ts +237 -0
  140. package/src/client-urls/revalidation-protocol.ts +56 -0
  141. package/src/client-urls/server-projection.ts +579 -0
  142. package/src/client-urls/types.ts +195 -0
  143. package/src/client.rsc.tsx +12 -0
  144. package/src/client.tsx +49 -6
  145. package/src/decode-loader-results.ts +113 -0
  146. package/src/errors.ts +14 -0
  147. package/src/handles/deferred-resolution.ts +14 -7
  148. package/src/handles/is-thenable.ts +2 -4
  149. package/src/index.ts +1 -0
  150. package/src/loader-redirect.tsx +64 -0
  151. package/src/outlet-context.ts +12 -0
  152. package/src/outlet-provider.tsx +15 -1
  153. package/src/redirect-origin.ts +29 -0
  154. package/src/route-content-wrapper.tsx +96 -3
  155. package/src/route-definition/dsl-helpers.ts +28 -3
  156. package/src/route-definition/helpers-types.ts +13 -0
  157. package/src/route-definition/redirect.ts +17 -18
  158. package/src/router/error-handling.ts +65 -11
  159. package/src/router/intercept-resolution.ts +29 -0
  160. package/src/router/loader-resolution.ts +261 -28
  161. package/src/router/match-middleware/cache-lookup.ts +24 -15
  162. package/src/router/match-result.ts +7 -0
  163. package/src/router/revalidation.ts +24 -11
  164. package/src/router/route-trie-builder.ts +334 -0
  165. package/src/router/router-interfaces.ts +38 -0
  166. package/src/router/segment-resolution/fresh.ts +47 -0
  167. package/src/router/segment-resolution/helpers.ts +9 -11
  168. package/src/router/segment-resolution/loader-cache.ts +14 -24
  169. package/src/router/segment-resolution/revalidation.ts +20 -1
  170. package/src/router/trie-matching.ts +3 -3
  171. package/src/router.ts +46 -1
  172. package/src/rsc/full-payload.ts +6 -0
  173. package/src/rsc/handler.ts +36 -12
  174. package/src/rsc/helpers.ts +13 -0
  175. package/src/rsc/loader-fetch.ts +2 -2
  176. package/src/rsc/manifest-init.ts +28 -9
  177. package/src/rsc/progressive-enhancement.ts +247 -70
  178. package/src/rsc/render-pipeline.ts +68 -24
  179. package/src/rsc/routine-plan.ts +359 -0
  180. package/src/rsc/rsc-rendering.ts +569 -302
  181. package/src/rsc/server-action.ts +180 -71
  182. package/src/rsc/shell-capture-constants.ts +18 -0
  183. package/src/rsc/shell-capture.ts +104 -21
  184. package/src/rsc/shell-serve.ts +15 -2
  185. package/src/rsc/ssr-setup.ts +10 -1
  186. package/src/rsc/types.ts +31 -2
  187. package/src/segment-system.tsx +83 -26
  188. package/src/server/context.ts +10 -0
  189. package/src/server/cookie-store.ts +19 -19
  190. package/src/server/handle-store.ts +185 -48
  191. package/src/server/request-context.ts +36 -6
  192. package/src/server.ts +7 -0
  193. package/src/ssr/index.tsx +37 -2
  194. package/src/ssr/ssr-root.tsx +30 -2
  195. package/src/testing/dom.entry.ts +1 -1
  196. package/src/testing/render-route.tsx +22 -8
  197. package/src/testing/run-loader.ts +51 -13
  198. package/src/types/boundaries.ts +19 -0
  199. package/src/types/index.ts +1 -0
  200. package/src/types/loader-types.ts +60 -5
  201. package/src/types/segments.ts +7 -0
  202. package/src/urls/include-helper.ts +22 -4
  203. package/src/urls/path-helper-types.ts +14 -1
  204. package/src/use-loader.tsx +67 -6
  205. package/src/vite/discovery/client-urls-projection.ts +322 -0
  206. package/src/vite/discovery/discover-routers.ts +43 -17
  207. package/src/vite/discovery/state.ts +11 -1
  208. package/src/vite/discovery/virtual-module-codegen.ts +20 -0
  209. package/src/vite/encryption-key.ts +29 -0
  210. package/src/vite/plugins/expose-action-id.ts +2 -2
  211. package/src/vite/plugins/expose-internal-ids.ts +46 -0
  212. package/src/vite/plugins/server-ref-hashing.ts +74 -0
  213. package/src/vite/plugins/server-reference-pattern.ts +10 -0
  214. package/src/vite/plugins/virtual-entries.ts +12 -3
  215. package/src/vite/rango.ts +9 -0
  216. package/src/vite/router-discovery.ts +184 -14
  217. package/src/vite/utils/shared-utils.ts +12 -7
  218. package/dist/types.backup/__internal.d.ts +0 -127
  219. package/dist/types.backup/bin/rango.d.ts +0 -1
  220. package/dist/types.backup/browser/action-coordinator.d.ts +0 -57
  221. package/dist/types.backup/browser/action-fence.d.ts +0 -33
  222. package/dist/types.backup/browser/app-shell.d.ts +0 -34
  223. package/dist/types.backup/browser/app-version.d.ts +0 -6
  224. package/dist/types.backup/browser/connection-warmup.d.ts +0 -31
  225. package/dist/types.backup/browser/cookie-name.d.ts +0 -66
  226. package/dist/types.backup/browser/event-controller.d.ts +0 -221
  227. package/dist/types.backup/browser/history-state.d.ts +0 -26
  228. package/dist/types.backup/browser/index.d.ts +0 -1
  229. package/dist/types.backup/browser/intercept-utils.d.ts +0 -30
  230. package/dist/types.backup/browser/invalidate-client-cache.d.ts +0 -17
  231. package/dist/types.backup/browser/link-interceptor.d.ts +0 -43
  232. package/dist/types.backup/browser/logging.d.ts +0 -33
  233. package/dist/types.backup/browser/merge-segment-loaders.d.ts +0 -38
  234. package/dist/types.backup/browser/navigation-bridge.d.ts +0 -27
  235. package/dist/types.backup/browser/navigation-client.d.ts +0 -17
  236. package/dist/types.backup/browser/navigation-store-handle.d.ts +0 -25
  237. package/dist/types.backup/browser/navigation-store.d.ts +0 -95
  238. package/dist/types.backup/browser/navigation-transaction.d.ts +0 -75
  239. package/dist/types.backup/browser/network-error-handler.d.ts +0 -35
  240. package/dist/types.backup/browser/partial-update.d.ts +0 -61
  241. package/dist/types.backup/browser/prefetch/cache.d.ts +0 -183
  242. package/dist/types.backup/browser/prefetch/fetch.d.ts +0 -52
  243. package/dist/types.backup/browser/prefetch/observer.d.ts +0 -27
  244. package/dist/types.backup/browser/prefetch/policy.d.ts +0 -13
  245. package/dist/types.backup/browser/prefetch/queue.d.ts +0 -48
  246. package/dist/types.backup/browser/prefetch/resource-ready.d.ts +0 -28
  247. package/dist/types.backup/browser/rango-state.d.ts +0 -52
  248. package/dist/types.backup/browser/react/Link.d.ts +0 -140
  249. package/dist/types.backup/browser/react/NavigationProvider.d.ts +0 -88
  250. package/dist/types.backup/browser/react/ScrollRestoration.d.ts +0 -78
  251. package/dist/types.backup/browser/react/context.d.ts +0 -54
  252. package/dist/types.backup/browser/react/filter-segment-order.d.ts +0 -35
  253. package/dist/types.backup/browser/react/index.d.ts +0 -1
  254. package/dist/types.backup/browser/react/location-state-shared.d.ts +0 -162
  255. package/dist/types.backup/browser/react/location-state.d.ts +0 -29
  256. package/dist/types.backup/browser/react/mount-context.d.ts +0 -23
  257. package/dist/types.backup/browser/react/nonce-context.d.ts +0 -14
  258. package/dist/types.backup/browser/react/shallow-equal.d.ts +0 -5
  259. package/dist/types.backup/browser/react/use-action.d.ts +0 -61
  260. package/dist/types.backup/browser/react/use-handle.d.ts +0 -21
  261. package/dist/types.backup/browser/react/use-href.d.ts +0 -32
  262. package/dist/types.backup/browser/react/use-link-status.d.ts +0 -36
  263. package/dist/types.backup/browser/react/use-mount.d.ts +0 -24
  264. package/dist/types.backup/browser/react/use-navigation.d.ts +0 -15
  265. package/dist/types.backup/browser/react/use-params.d.ts +0 -21
  266. package/dist/types.backup/browser/react/use-pathname.d.ts +0 -13
  267. package/dist/types.backup/browser/react/use-reverse.d.ts +0 -40
  268. package/dist/types.backup/browser/react/use-router.d.ts +0 -23
  269. package/dist/types.backup/browser/react/use-search-params.d.ts +0 -19
  270. package/dist/types.backup/browser/react/use-segments.d.ts +0 -29
  271. package/dist/types.backup/browser/response-adapter.d.ts +0 -58
  272. package/dist/types.backup/browser/rsc-router.d.ts +0 -141
  273. package/dist/types.backup/browser/scroll-restoration.d.ts +0 -103
  274. package/dist/types.backup/browser/segment-reconciler.d.ts +0 -74
  275. package/dist/types.backup/browser/segment-structure-assert.d.ts +0 -16
  276. package/dist/types.backup/browser/server-action-bridge.d.ts +0 -29
  277. package/dist/types.backup/browser/types.d.ts +0 -530
  278. package/dist/types.backup/browser/validate-redirect-origin.d.ts +0 -28
  279. package/dist/types.backup/build/collect-fallback-refs.d.ts +0 -5
  280. package/dist/types.backup/build/generate-manifest.d.ts +0 -100
  281. package/dist/types.backup/build/generate-route-types.d.ts +0 -8
  282. package/dist/types.backup/build/index.d.ts +0 -21
  283. package/dist/types.backup/build/prefix-tree-utils.d.ts +0 -56
  284. package/dist/types.backup/build/route-types/ast-helpers.d.ts +0 -3
  285. package/dist/types.backup/build/route-types/ast-route-extraction.d.ts +0 -13
  286. package/dist/types.backup/build/route-types/codegen.d.ts +0 -16
  287. package/dist/types.backup/build/route-types/include-resolution.d.ts +0 -74
  288. package/dist/types.backup/build/route-types/param-extraction.d.ts +0 -13
  289. package/dist/types.backup/build/route-types/per-module-writer.d.ts +0 -18
  290. package/dist/types.backup/build/route-types/router-processing.d.ts +0 -82
  291. package/dist/types.backup/build/route-types/scan-filter.d.ts +0 -17
  292. package/dist/types.backup/build/route-types/source-scan.d.ts +0 -13
  293. package/dist/types.backup/build/runtime-discovery.d.ts +0 -24
  294. package/dist/types.backup/cache/background-task.d.ts +0 -21
  295. package/dist/types.backup/cache/cache-error.d.ts +0 -71
  296. package/dist/types.backup/cache/cache-key-utils.d.ts +0 -35
  297. package/dist/types.backup/cache/cache-policy.d.ts +0 -59
  298. package/dist/types.backup/cache/cache-runtime.d.ts +0 -51
  299. package/dist/types.backup/cache/cache-scope.d.ts +0 -134
  300. package/dist/types.backup/cache/cache-tag.d.ts +0 -79
  301. package/dist/types.backup/cache/cf/cf-base64.d.ts +0 -4
  302. package/dist/types.backup/cache/cf/cf-cache-constants.d.ts +0 -105
  303. package/dist/types.backup/cache/cf/cf-cache-store.d.ts +0 -481
  304. package/dist/types.backup/cache/cf/cf-cache-types.d.ts +0 -300
  305. package/dist/types.backup/cache/cf/cf-kv-utils.d.ts +0 -22
  306. package/dist/types.backup/cache/cf/cf-tag-marker-memo.d.ts +0 -15
  307. package/dist/types.backup/cache/cf/index.d.ts +0 -3
  308. package/dist/types.backup/cache/document-cache.d.ts +0 -69
  309. package/dist/types.backup/cache/handle-capture.d.ts +0 -23
  310. package/dist/types.backup/cache/handle-snapshot.d.ts +0 -39
  311. package/dist/types.backup/cache/index.d.ts +0 -7
  312. package/dist/types.backup/cache/memory-segment-store.d.ts +0 -163
  313. package/dist/types.backup/cache/profile-registry.d.ts +0 -40
  314. package/dist/types.backup/cache/read-through-swr.d.ts +0 -60
  315. package/dist/types.backup/cache/segment-codec.d.ts +0 -78
  316. package/dist/types.backup/cache/shell-snapshot.d.ts +0 -162
  317. package/dist/types.backup/cache/tag-invalidation.d.ts +0 -74
  318. package/dist/types.backup/cache/taint.d.ts +0 -71
  319. package/dist/types.backup/cache/types.d.ts +0 -407
  320. package/dist/types.backup/cache/vercel/index.d.ts +0 -1
  321. package/dist/types.backup/cache/vercel/vercel-cache-store.d.ts +0 -267
  322. package/dist/types.backup/client.d.ts +0 -184
  323. package/dist/types.backup/client.rsc.d.ts +0 -39
  324. package/dist/types.backup/cloudflare/index.d.ts +0 -7
  325. package/dist/types.backup/cloudflare/tracing.d.ts +0 -53
  326. package/dist/types.backup/component-utils.d.ts +0 -46
  327. package/dist/types.backup/components/DefaultDocument.d.ts +0 -13
  328. package/dist/types.backup/context-var.d.ts +0 -84
  329. package/dist/types.backup/debug.d.ts +0 -57
  330. package/dist/types.backup/decode-loader-results.d.ts +0 -5
  331. package/dist/types.backup/default-error-boundary.d.ts +0 -10
  332. package/dist/types.backup/defer.d.ts +0 -89
  333. package/dist/types.backup/deps/browser.d.ts +0 -1
  334. package/dist/types.backup/deps/html-stream-client.d.ts +0 -1
  335. package/dist/types.backup/deps/html-stream-server.d.ts +0 -1
  336. package/dist/types.backup/deps/rsc.d.ts +0 -1
  337. package/dist/types.backup/deps/ssr.d.ts +0 -1
  338. package/dist/types.backup/encode-kv.d.ts +0 -35
  339. package/dist/types.backup/errors.d.ts +0 -226
  340. package/dist/types.backup/escape-script.d.ts +0 -44
  341. package/dist/types.backup/handle.d.ts +0 -93
  342. package/dist/types.backup/handles/MetaTags.d.ts +0 -17
  343. package/dist/types.backup/handles/Scripts.d.ts +0 -38
  344. package/dist/types.backup/handles/breadcrumbs.d.ts +0 -43
  345. package/dist/types.backup/handles/deferred-resolution.d.ts +0 -53
  346. package/dist/types.backup/handles/is-thenable.d.ts +0 -12
  347. package/dist/types.backup/handles/meta.d.ts +0 -43
  348. package/dist/types.backup/handles/script.d.ts +0 -139
  349. package/dist/types.backup/host/cookie-handler.d.ts +0 -8
  350. package/dist/types.backup/host/errors.d.ts +0 -40
  351. package/dist/types.backup/host/index.d.ts +0 -33
  352. package/dist/types.backup/host/pattern-matcher.d.ts +0 -30
  353. package/dist/types.backup/host/router.d.ts +0 -12
  354. package/dist/types.backup/host/testing.d.ts +0 -41
  355. package/dist/types.backup/host/types.d.ts +0 -148
  356. package/dist/types.backup/host/utils.d.ts +0 -20
  357. package/dist/types.backup/href-client.d.ts +0 -214
  358. package/dist/types.backup/index.d.ts +0 -112
  359. package/dist/types.backup/index.rsc.d.ts +0 -51
  360. package/dist/types.backup/internal-debug.d.ts +0 -1
  361. package/dist/types.backup/loader-store.d.ts +0 -193
  362. package/dist/types.backup/loader.d.ts +0 -18
  363. package/dist/types.backup/loader.rsc.d.ts +0 -18
  364. package/dist/types.backup/missing-id-error.d.ts +0 -1
  365. package/dist/types.backup/outlet-context.d.ts +0 -12
  366. package/dist/types.backup/outlet-provider.d.ts +0 -12
  367. package/dist/types.backup/prerender/build-shell-capture.d.ts +0 -104
  368. package/dist/types.backup/prerender/param-hash.d.ts +0 -6
  369. package/dist/types.backup/prerender/shell-manifest-key.d.ts +0 -18
  370. package/dist/types.backup/prerender/store.d.ts +0 -62
  371. package/dist/types.backup/prerender.d.ts +0 -292
  372. package/dist/types.backup/redirect-origin.d.ts +0 -55
  373. package/dist/types.backup/regex-escape.d.ts +0 -6
  374. package/dist/types.backup/render-error-thrower.d.ts +0 -13
  375. package/dist/types.backup/response-utils.d.ts +0 -35
  376. package/dist/types.backup/reverse.d.ts +0 -206
  377. package/dist/types.backup/root-error-boundary.d.ts +0 -32
  378. package/dist/types.backup/route-content-wrapper.d.ts +0 -40
  379. package/dist/types.backup/route-definition/dsl-helpers.d.ts +0 -130
  380. package/dist/types.backup/route-definition/helper-factories.d.ts +0 -22
  381. package/dist/types.backup/route-definition/helpers-types.d.ts +0 -392
  382. package/dist/types.backup/route-definition/index.d.ts +0 -7
  383. package/dist/types.backup/route-definition/redirect.d.ts +0 -48
  384. package/dist/types.backup/route-definition/resolve-handler-use.d.ts +0 -19
  385. package/dist/types.backup/route-definition/use-item-types.d.ts +0 -1
  386. package/dist/types.backup/route-definition.d.ts +0 -1
  387. package/dist/types.backup/route-map-builder.d.ts +0 -127
  388. package/dist/types.backup/route-name.d.ts +0 -27
  389. package/dist/types.backup/route-types.d.ts +0 -172
  390. package/dist/types.backup/router/basename.d.ts +0 -10
  391. package/dist/types.backup/router/content-negotiation.d.ts +0 -91
  392. package/dist/types.backup/router/debug-manifest.d.ts +0 -7
  393. package/dist/types.backup/router/error-handling.d.ts +0 -76
  394. package/dist/types.backup/router/find-match.d.ts +0 -19
  395. package/dist/types.backup/router/handler-context.d.ts +0 -41
  396. package/dist/types.backup/router/instrument.d.ts +0 -161
  397. package/dist/types.backup/router/intercept-resolution.d.ts +0 -79
  398. package/dist/types.backup/router/lazy-includes.d.ts +0 -26
  399. package/dist/types.backup/router/loader-resolution.d.ts +0 -63
  400. package/dist/types.backup/router/logging.d.ts +0 -41
  401. package/dist/types.backup/router/manifest.d.ts +0 -8
  402. package/dist/types.backup/router/match-api.d.ts +0 -19
  403. package/dist/types.backup/router/match-context.d.ts +0 -184
  404. package/dist/types.backup/router/match-handlers.d.ts +0 -49
  405. package/dist/types.backup/router/match-middleware/background-revalidation.d.ts +0 -113
  406. package/dist/types.backup/router/match-middleware/cache-lookup.d.ts +0 -113
  407. package/dist/types.backup/router/match-middleware/cache-store.d.ts +0 -112
  408. package/dist/types.backup/router/match-middleware/index.d.ts +0 -80
  409. package/dist/types.backup/router/match-middleware/intercept-resolution.d.ts +0 -116
  410. package/dist/types.backup/router/match-middleware/segment-resolution.d.ts +0 -94
  411. package/dist/types.backup/router/match-pipelines.d.ts +0 -103
  412. package/dist/types.backup/router/match-result.d.ts +0 -114
  413. package/dist/types.backup/router/metrics.d.ts +0 -6
  414. package/dist/types.backup/router/middleware-types.d.ts +0 -74
  415. package/dist/types.backup/router/middleware.d.ts +0 -116
  416. package/dist/types.backup/router/navigation-snapshot.d.ts +0 -22
  417. package/dist/types.backup/router/params-util.d.ts +0 -8
  418. package/dist/types.backup/router/parse-pattern.d.ts +0 -38
  419. package/dist/types.backup/router/pattern-matching.d.ts +0 -169
  420. package/dist/types.backup/router/prefetch-cache-ttl.d.ts +0 -27
  421. package/dist/types.backup/router/prefetch-limits.d.ts +0 -20
  422. package/dist/types.backup/router/prerender-match.d.ts +0 -50
  423. package/dist/types.backup/router/preview-match.d.ts +0 -22
  424. package/dist/types.backup/router/request-classification.d.ts +0 -104
  425. package/dist/types.backup/router/revalidation.d.ts +0 -57
  426. package/dist/types.backup/router/route-snapshot.d.ts +0 -112
  427. package/dist/types.backup/router/router-context.d.ts +0 -137
  428. package/dist/types.backup/router/router-interfaces.d.ts +0 -432
  429. package/dist/types.backup/router/router-options.d.ts +0 -738
  430. package/dist/types.backup/router/router-registry.d.ts +0 -15
  431. package/dist/types.backup/router/segment-resolution/fresh.d.ts +0 -55
  432. package/dist/types.backup/router/segment-resolution/helpers.d.ts +0 -93
  433. package/dist/types.backup/router/segment-resolution/loader-cache.d.ts +0 -33
  434. package/dist/types.backup/router/segment-resolution/loader-mask.d.ts +0 -44
  435. package/dist/types.backup/router/segment-resolution/loader-snapshot.d.ts +0 -90
  436. package/dist/types.backup/router/segment-resolution/mask-nested.d.ts +0 -53
  437. package/dist/types.backup/router/segment-resolution/revalidation.d.ts +0 -85
  438. package/dist/types.backup/router/segment-resolution/static-store.d.ts +0 -17
  439. package/dist/types.backup/router/segment-resolution/streamed-handler-telemetry.d.ts +0 -16
  440. package/dist/types.backup/router/segment-resolution/view-transition-default.d.ts +0 -28
  441. package/dist/types.backup/router/segment-resolution.d.ts +0 -3
  442. package/dist/types.backup/router/segment-wrappers.d.ts +0 -53
  443. package/dist/types.backup/router/state-cookie-name.d.ts +0 -1
  444. package/dist/types.backup/router/substitute-pattern-params.d.ts +0 -23
  445. package/dist/types.backup/router/telemetry-otel.d.ts +0 -113
  446. package/dist/types.backup/router/telemetry.d.ts +0 -215
  447. package/dist/types.backup/router/timeout.d.ts +0 -68
  448. package/dist/types.backup/router/tracing.d.ts +0 -125
  449. package/dist/types.backup/router/trie-matching.d.ts +0 -32
  450. package/dist/types.backup/router/types.d.ts +0 -98
  451. package/dist/types.backup/router/url-params.d.ts +0 -26
  452. package/dist/types.backup/router.d.ts +0 -7
  453. package/dist/types.backup/rsc/capture-queue.d.ts +0 -6
  454. package/dist/types.backup/rsc/full-payload.d.ts +0 -22
  455. package/dist/types.backup/rsc/handler-context.d.ts +0 -31
  456. package/dist/types.backup/rsc/handler.d.ts +0 -9
  457. package/dist/types.backup/rsc/helpers.d.ts +0 -213
  458. package/dist/types.backup/rsc/index.d.ts +0 -17
  459. package/dist/types.backup/rsc/json-route-result.d.ts +0 -20
  460. package/dist/types.backup/rsc/loader-fetch.d.ts +0 -14
  461. package/dist/types.backup/rsc/manifest-init.d.ts +0 -18
  462. package/dist/types.backup/rsc/nonce.d.ts +0 -28
  463. package/dist/types.backup/rsc/origin-guard.d.ts +0 -50
  464. package/dist/types.backup/rsc/progressive-enhancement.d.ts +0 -19
  465. package/dist/types.backup/rsc/redirect-guard.d.ts +0 -35
  466. package/dist/types.backup/rsc/response-cache-serve.d.ts +0 -46
  467. package/dist/types.backup/rsc/response-error.d.ts +0 -19
  468. package/dist/types.backup/rsc/response-route-handler.d.ts +0 -29
  469. package/dist/types.backup/rsc/rsc-rendering.d.ts +0 -23
  470. package/dist/types.backup/rsc/runtime-warnings.d.ts +0 -22
  471. package/dist/types.backup/rsc/server-action.d.ts +0 -68
  472. package/dist/types.backup/rsc/shell-build-manifest.d.ts +0 -84
  473. package/dist/types.backup/rsc/shell-capture-constants.d.ts +0 -27
  474. package/dist/types.backup/rsc/shell-capture.d.ts +0 -374
  475. package/dist/types.backup/rsc/shell-serve.d.ts +0 -136
  476. package/dist/types.backup/rsc/ssr-setup.d.ts +0 -48
  477. package/dist/types.backup/rsc/transition-gate.d.ts +0 -27
  478. package/dist/types.backup/rsc/types.d.ts +0 -290
  479. package/dist/types.backup/runtime-env.d.ts +0 -1
  480. package/dist/types.backup/search-params.d.ts +0 -125
  481. package/dist/types.backup/segment-content-promise.d.ts +0 -13
  482. package/dist/types.backup/segment-fragments.d.ts +0 -56
  483. package/dist/types.backup/segment-loader-promise.d.ts +0 -22
  484. package/dist/types.backup/segment-system.d.ts +0 -84
  485. package/dist/types.backup/serialize.d.ts +0 -164
  486. package/dist/types.backup/server/context.d.ts +0 -494
  487. package/dist/types.backup/server/cookie-parse.d.ts +0 -10
  488. package/dist/types.backup/server/cookie-store.d.ts +0 -107
  489. package/dist/types.backup/server/fetchable-loader-store.d.ts +0 -20
  490. package/dist/types.backup/server/handle-store.d.ts +0 -100
  491. package/dist/types.backup/server/loader-registry.d.ts +0 -32
  492. package/dist/types.backup/server/request-context.d.ts +0 -596
  493. package/dist/types.backup/server/root-layout.d.ts +0 -3
  494. package/dist/types.backup/server.d.ts +0 -15
  495. package/dist/types.backup/ssr/index.d.ts +0 -233
  496. package/dist/types.backup/ssr/inject-rsc-eager.d.ts +0 -3
  497. package/dist/types.backup/ssr/preinit-client-references.d.ts +0 -71
  498. package/dist/types.backup/ssr/ssr-root.d.ts +0 -69
  499. package/dist/types.backup/static-handler.d.ts +0 -57
  500. package/dist/types.backup/testing/cache-status.d.ts +0 -63
  501. package/dist/types.backup/testing/collect-handle.d.ts +0 -20
  502. package/dist/types.backup/testing/dispatch.d.ts +0 -123
  503. package/dist/types.backup/testing/dom.entry.d.ts +0 -15
  504. package/dist/types.backup/testing/e2e/fixture.d.ts +0 -37
  505. package/dist/types.backup/testing/e2e/index.d.ts +0 -30
  506. package/dist/types.backup/testing/e2e/matchers.d.ts +0 -17
  507. package/dist/types.backup/testing/e2e/page-helpers.d.ts +0 -62
  508. package/dist/types.backup/testing/e2e/parity.d.ts +0 -111
  509. package/dist/types.backup/testing/e2e/server.d.ts +0 -35
  510. package/dist/types.backup/testing/flight-matchers.d.ts +0 -55
  511. package/dist/types.backup/testing/flight-normalize.d.ts +0 -1
  512. package/dist/types.backup/testing/flight-tree.d.ts +0 -192
  513. package/dist/types.backup/testing/flight.d.ts +0 -115
  514. package/dist/types.backup/testing/flight.entry.d.ts +0 -27
  515. package/dist/types.backup/testing/generated-routes.d.ts +0 -66
  516. package/dist/types.backup/testing/index.d.ts +0 -52
  517. package/dist/types.backup/testing/internal/context.d.ts +0 -225
  518. package/dist/types.backup/testing/internal/flight-client-globals.d.ts +0 -1
  519. package/dist/types.backup/testing/internal/seed-vars.d.ts +0 -30
  520. package/dist/types.backup/testing/render-handler.d.ts +0 -160
  521. package/dist/types.backup/testing/render-route.d.ts +0 -246
  522. package/dist/types.backup/testing/run-loader.d.ts +0 -186
  523. package/dist/types.backup/testing/run-middleware.d.ts +0 -132
  524. package/dist/types.backup/testing/run-transition-when.d.ts +0 -77
  525. package/dist/types.backup/testing/vitest-stubs/cloudflare-email.d.ts +0 -6
  526. package/dist/types.backup/testing/vitest-stubs/cloudflare-workers.d.ts +0 -13
  527. package/dist/types.backup/testing/vitest-stubs/plugin-rsc.d.ts +0 -7
  528. package/dist/types.backup/testing/vitest-stubs/version.d.ts +0 -1
  529. package/dist/types.backup/testing/vitest.d.ts +0 -205
  530. package/dist/types.backup/theme/ThemeProvider.d.ts +0 -13
  531. package/dist/types.backup/theme/ThemeScript.d.ts +0 -45
  532. package/dist/types.backup/theme/constants.d.ts +0 -39
  533. package/dist/types.backup/theme/index.d.ts +0 -29
  534. package/dist/types.backup/theme/theme-context.d.ts +0 -21
  535. package/dist/types.backup/theme/theme-script.d.ts +0 -26
  536. package/dist/types.backup/theme/types.d.ts +0 -162
  537. package/dist/types.backup/theme/use-theme.d.ts +0 -8
  538. package/dist/types.backup/types/boundaries.d.ts +0 -93
  539. package/dist/types.backup/types/cache-types.d.ts +0 -191
  540. package/dist/types.backup/types/error-types.d.ts +0 -114
  541. package/dist/types.backup/types/global-namespace.d.ts +0 -90
  542. package/dist/types.backup/types/handler-context.d.ts +0 -658
  543. package/dist/types.backup/types/index.d.ts +0 -11
  544. package/dist/types.backup/types/loader-types.d.ts +0 -182
  545. package/dist/types.backup/types/request-scope.d.ts +0 -93
  546. package/dist/types.backup/types/route-config.d.ts +0 -105
  547. package/dist/types.backup/types/route-entry.d.ts +0 -95
  548. package/dist/types.backup/types/segments.d.ts +0 -234
  549. package/dist/types.backup/types.d.ts +0 -1
  550. package/dist/types.backup/urls/include-helper.d.ts +0 -17
  551. package/dist/types.backup/urls/include-provider.d.ts +0 -27
  552. package/dist/types.backup/urls/index.d.ts +0 -6
  553. package/dist/types.backup/urls/path-helper-types.d.ts +0 -197
  554. package/dist/types.backup/urls/path-helper.d.ts +0 -12
  555. package/dist/types.backup/urls/pattern-types.d.ts +0 -166
  556. package/dist/types.backup/urls/response-types.d.ts +0 -67
  557. package/dist/types.backup/urls/type-extraction.d.ts +0 -157
  558. package/dist/types.backup/urls/urls-function.d.ts +0 -24
  559. package/dist/types.backup/urls.d.ts +0 -1
  560. package/dist/types.backup/use-loader.d.ts +0 -150
  561. package/dist/types.backup/vercel/index.d.ts +0 -10
  562. package/dist/types.backup/vercel/tracing.d.ts +0 -70
  563. package/dist/types.backup/vite/debug.d.ts +0 -80
  564. package/dist/types.backup/vite/discovery/bundle-postprocess.d.ts +0 -12
  565. package/dist/types.backup/vite/discovery/dev-prerender-cache.d.ts +0 -65
  566. package/dist/types.backup/vite/discovery/discover-routers.d.ts +0 -17
  567. package/dist/types.backup/vite/discovery/discovery-errors.d.ts +0 -113
  568. package/dist/types.backup/vite/discovery/gate-state.d.ts +0 -79
  569. package/dist/types.backup/vite/discovery/prerender-collection.d.ts +0 -24
  570. package/dist/types.backup/vite/discovery/route-types-writer.d.ts +0 -32
  571. package/dist/types.backup/vite/discovery/self-gen-tracking.d.ts +0 -22
  572. package/dist/types.backup/vite/discovery/shell-prerender-phase.d.ts +0 -40
  573. package/dist/types.backup/vite/discovery/state.d.ts +0 -162
  574. package/dist/types.backup/vite/discovery/virtual-module-codegen.d.ts +0 -15
  575. package/dist/types.backup/vite/index.d.ts +0 -11
  576. package/dist/types.backup/vite/inject-client-debug.d.ts +0 -56
  577. package/dist/types.backup/vite/plugin-types.d.ts +0 -298
  578. package/dist/types.backup/vite/plugins/cjs-to-esm.d.ts +0 -6
  579. package/dist/types.backup/vite/plugins/client-ref-dedup.d.ts +0 -40
  580. package/dist/types.backup/vite/plugins/client-ref-hashing.d.ts +0 -35
  581. package/dist/types.backup/vite/plugins/cloudflare-protocol-stub.d.ts +0 -64
  582. package/dist/types.backup/vite/plugins/expose-action-id.d.ts +0 -18
  583. package/dist/types.backup/vite/plugins/expose-id-utils.d.ts +0 -37
  584. package/dist/types.backup/vite/plugins/expose-ids/export-analysis.d.ts +0 -19
  585. package/dist/types.backup/vite/plugins/expose-ids/handler-transform.d.ts +0 -10
  586. package/dist/types.backup/vite/plugins/expose-ids/loader-transform.d.ts +0 -8
  587. package/dist/types.backup/vite/plugins/expose-ids/router-transform.d.ts +0 -13
  588. package/dist/types.backup/vite/plugins/expose-ids/types.d.ts +0 -29
  589. package/dist/types.backup/vite/plugins/expose-internal-ids.d.ts +0 -6
  590. package/dist/types.backup/vite/plugins/performance-tracks.d.ts +0 -25
  591. package/dist/types.backup/vite/plugins/refresh-cmd.d.ts +0 -20
  592. package/dist/types.backup/vite/plugins/use-cache-transform.d.ts +0 -20
  593. package/dist/types.backup/vite/plugins/vercel-output.d.ts +0 -85
  594. package/dist/types.backup/vite/plugins/version-injector.d.ts +0 -21
  595. package/dist/types.backup/vite/plugins/version-plugin.d.ts +0 -19
  596. package/dist/types.backup/vite/plugins/virtual-entries.d.ts +0 -36
  597. package/dist/types.backup/vite/plugins/virtual-stub-plugin.d.ts +0 -7
  598. package/dist/types.backup/vite/rango.d.ts +0 -29
  599. package/dist/types.backup/vite/router-discovery.d.ts +0 -23
  600. package/dist/types.backup/vite/utils/ast-handler-extract.d.ts +0 -64
  601. package/dist/types.backup/vite/utils/banner.d.ts +0 -2
  602. package/dist/types.backup/vite/utils/bundle-analysis.d.ts +0 -28
  603. package/dist/types.backup/vite/utils/client-chunks.d.ts +0 -55
  604. package/dist/types.backup/vite/utils/directive-prologue.d.ts +0 -16
  605. package/dist/types.backup/vite/utils/forward-user-plugins.d.ts +0 -37
  606. package/dist/types.backup/vite/utils/manifest-utils.d.ts +0 -7
  607. package/dist/types.backup/vite/utils/package-resolution.d.ts +0 -6
  608. package/dist/types.backup/vite/utils/prerender-utils.d.ts +0 -32
  609. package/dist/types.backup/vite/utils/shared-utils.d.ts +0 -55
@@ -51,6 +51,7 @@ import {
51
51
  recordRequestTags,
52
52
  runWithCacheTagScope,
53
53
  } from "./cache-tag.js";
54
+ import { runWithCacheExecScope } from "./cache-exec-scope.js";
54
55
  import { reportCacheError } from "./cache-error.js";
55
56
  import type { CacheItemResult } from "./types.js";
56
57
 
@@ -205,8 +206,63 @@ interface CacheEnvelope {
205
206
  * singleton), and cleared for a key as soon as the leader settles: a rejected
206
207
  * leader (function threw, or the result was not serializable) propagates to
207
208
  * current waiters, which then retry fresh.
209
+ *
210
+ * A leader that NEVER settles must not hang followers (scar tissue, autobarn
211
+ * pilot outage): a background shell capture's render became leader, awaited a
212
+ * tarpitting upstream fetch, and workerd killed the capture's waitUntil context
213
+ * — orphaning the leader promise as permanently pending, its map entry never
214
+ * cleared. Every later document render calling the same cached function (an
215
+ * isolate-global key for plain-args calls) awaited it forever before first
216
+ * byte: isolate-wide TTFB-0 until redeploy. Followers therefore trust an entry
217
+ * only for {@link IN_FLIGHT_LEADER_MAX_WAIT_MS} from registration; past it
218
+ * they evict the entry and run fresh — bounded duplicate upstream work instead
219
+ * of an unbounded hang. Eviction is age-based off `registeredAt`, so it also
220
+ * heals entries stranded by a killed context (no timer in that context needs
221
+ * to survive).
222
+ */
223
+ interface InFlightExecution {
224
+ promise: Promise<CacheEnvelope>;
225
+ registeredAt: number;
226
+ }
227
+
228
+ const inFlightExecutions = new Map<string, InFlightExecution>();
229
+
230
+ /**
231
+ * How long a follower trusts an in-flight leader before evicting it and
232
+ * running fresh. High enough that a slow-but-healthy upstream never triggers
233
+ * duplicate work (a legitimate cached call taking >15s is already pathological);
234
+ * low enough to bound the blast radius of a wedged leader to seconds, not the
235
+ * isolate lifetime. Aligned with SHELL_CAPTURE_MAX_WAIT_MS.
208
236
  */
209
- const inFlightExecutions = new Map<string, Promise<CacheEnvelope>>();
237
+ const IN_FLIGHT_LEADER_MAX_WAIT_MS = 15_000;
238
+
239
+ /** Distinguishes a leader timeout from a leader rejection in raceLeader. */
240
+ const LEADER_TIMED_OUT = Symbol("leader-timed-out");
241
+
242
+ /**
243
+ * Await a leader's envelope for at most `remainingMs`. Resolves with the
244
+ * envelope, `undefined` on leader rejection (the leader already cleared its
245
+ * entry — caller falls through to a fresh run), or {@link LEADER_TIMED_OUT}
246
+ * when the window expires first (caller must evict the entry itself).
247
+ */
248
+ function raceLeader(
249
+ promise: Promise<CacheEnvelope>,
250
+ remainingMs: number,
251
+ ): Promise<CacheEnvelope | undefined | typeof LEADER_TIMED_OUT> {
252
+ return new Promise((resolve) => {
253
+ const timer = setTimeout(() => resolve(LEADER_TIMED_OUT), remainingMs);
254
+ promise.then(
255
+ (envelope) => {
256
+ clearTimeout(timer);
257
+ resolve(envelope);
258
+ },
259
+ () => {
260
+ clearTimeout(timer);
261
+ resolve(undefined);
262
+ },
263
+ );
264
+ });
265
+ }
210
266
 
211
267
  // ============================================================================
212
268
  // Core: registerCachedFunction
@@ -234,11 +290,12 @@ export function registerCachedFunction<T extends (...args: any[]) => any>(
234
290
  // cacheTag() call inside the function degrades to a no-op rather than
235
291
  // throwing "must be called inside a use cache function" - adopting cacheTag()
236
292
  // must not hard-fail in apps/tests without an item-capable cache configured.
237
- // Note: the INSIDE_CACHE_EXEC guard (cookies()/headers()/ctx.set() rejection)
238
- // is intentionally NOT stamped here. It is a cached-path-only check; in the
239
- // bypass the body actually executes, so the guarded side effects take effect
240
- // and nothing is lost on a (non-existent) hit. Same applies to the
241
- // non-serializable-args bypass below.
293
+ // Note: the cache-exec guards (cookies()/headers()/ctx.set() rejection
294
+ // runWithCacheExecScope + the INSIDE_CACHE_EXEC arg stamp) are intentionally
295
+ // NOT active here. They are cached-path-only checks; in the bypass the body
296
+ // actually executes, so the guarded side effects take effect and nothing is
297
+ // lost on a (non-existent) hit. Same applies to the non-serializable-args
298
+ // bypass below.
242
299
  if (!store?.getItem) {
243
300
  // Test-ergonomics guard: under a test runner, a "use cache" function that
244
301
  // executes with no item-capable store seeded is exercising the UNCACHED
@@ -452,9 +509,12 @@ export function registerCachedFunction<T extends (...args: any[]) => any>(
452
509
  // property stamped onto that SHARED object — so for the whole
453
510
  // revalidation window a concurrent foreground ctx.set() /
454
511
  // ctx.headers.*() would throw (issue #684, plan 010). requestCtx is
455
- // not stamped for the same reason. In-fn misuse is already caught
456
- // by the miss path's stamps on the function's FIRST execution — the
457
- // background re-runs the same function with the same request.
512
+ // never stamped anywhere for the same reason. In-body misuse IS
513
+ // still guarded here: runWithCacheExecScope below follows only this
514
+ // body's async chain, so a conditional cookies()/headers() read
515
+ // that fires only during revalidation throws instead of baking one
516
+ // user's value into the shared entry — with zero visibility to the
517
+ // concurrent foreground.
458
518
 
459
519
  try {
460
520
  // Re-establish the request-context ALS so a "use cache" body that
@@ -475,7 +535,7 @@ export function registerCachedFunction<T extends (...args: any[]) => any>(
475
535
  PHASES.background("use-cache-revalidation"),
476
536
  async () => {
477
537
  const scoped = runWithCacheTagScope(() =>
478
- fn.apply(this, args),
538
+ runWithCacheExecScope(() => fn.apply(this, args)),
479
539
  );
480
540
  const freshResult = await scoped.result;
481
541
  bgStopCapture?.();
@@ -541,20 +601,34 @@ export function registerCachedFunction<T extends (...args: any[]) => any>(
541
601
  // requests. The store write stays exactly once (the leader's).
542
602
  const existing = inFlightExecutions.get(cacheKey);
543
603
  if (existing) {
544
- let envelope: CacheEnvelope | undefined;
545
- try {
546
- envelope = await existing;
547
- } catch {
548
- // Leader rejected (function threw or its result was not serializable);
549
- // its map entry is already cleared, so fall through to a fresh run.
550
- envelope = undefined;
551
- }
552
- if (envelope) {
604
+ const remainingMs =
605
+ IN_FLIGHT_LEADER_MAX_WAIT_MS - (Date.now() - existing.registeredAt);
606
+ const raced =
607
+ remainingMs <= 0
608
+ ? LEADER_TIMED_OUT
609
+ : await raceLeader(existing.promise, remainingMs);
610
+ if (raced === LEADER_TIMED_OUT) {
611
+ // Wedged (or context-orphaned) leader: evict so this call and every
612
+ // later one run fresh. Guard the delete so a newer leader's entry is
613
+ // never removed; the stale leader's own clearSelf is identity-guarded
614
+ // the same way, so it can't evict our replacement if it settles late.
615
+ if (inFlightExecutions.get(cacheKey) === existing) {
616
+ inFlightExecutions.delete(cacheKey);
617
+ }
618
+ reportCacheError(
619
+ new Error(
620
+ `in-flight leader did not settle within ${IN_FLIGHT_LEADER_MAX_WAIT_MS}ms; evicted — executing fresh`,
621
+ ),
622
+ "cache-read",
623
+ `[use cache] "${id}" inflight-timeout`,
624
+ );
625
+ // Fall through to a fresh execution below (this call becomes leader).
626
+ } else if (raced) {
553
627
  try {
554
628
  return await serveCached({
555
- value: envelope.serialized,
556
- handles: envelope.handles,
557
- tags: envelope.tags,
629
+ value: raced.serialized,
630
+ handles: raced.handles,
631
+ tags: raced.tags,
558
632
  shouldRevalidate: false,
559
633
  });
560
634
  } catch (error) {
@@ -566,6 +640,8 @@ export function registerCachedFunction<T extends (...args: any[]) => any>(
566
640
  // Fall through to a fresh execution below.
567
641
  }
568
642
  }
643
+ // raced === undefined: leader rejected; its map entry is already
644
+ // cleared, so fall through to a fresh run.
569
645
  }
570
646
 
571
647
  // This call becomes the leader. Register a deferred envelope so concurrent
@@ -582,9 +658,12 @@ export function registerCachedFunction<T extends (...args: any[]) => any>(
582
658
  // Followers attach their own catch; guard the map's own reference so a
583
659
  // rejected envelope with no waiter is not an unhandled rejection.
584
660
  envelopePromise.catch(() => {});
585
- inFlightExecutions.set(cacheKey, envelopePromise);
661
+ inFlightExecutions.set(cacheKey, {
662
+ promise: envelopePromise,
663
+ registeredAt: Date.now(),
664
+ });
586
665
  const clearSelf = (): void => {
587
- if (inFlightExecutions.get(cacheKey) === envelopePromise) {
666
+ if (inFlightExecutions.get(cacheKey)?.promise === envelopePromise) {
588
667
  inFlightExecutions.delete(cacheKey);
589
668
  }
590
669
  };
@@ -604,12 +683,13 @@ export function registerCachedFunction<T extends (...args: any[]) => any>(
604
683
  // Uses ref-counted stamp/unstamp so overlapping executions
605
684
  // sharing the same ctx don't clear each other's guards.
606
685
  //
607
- // LOAD-BEARING for the stale-revalidation path above: the background
608
- // re-execution deliberately does NOT re-stamp (the objects are live
609
- // foreground state mid-render), relying on THIS stamp having caught in-fn
610
- // misuse on the function's first execution an entry only becomes
611
- // stale-revalidatable because a stamped miss ran clean and stored it. Do
612
- // not create a "use cache" entry via any path that skips this stamp.
686
+ // The ambient guards (cookies()/headers() and getRequestContext()-reached
687
+ // ctx methods) ride runWithCacheExecScope below instead an ALS scoped to
688
+ // the cached body's own async chain. The RequestContext itself is
689
+ // deliberately NOT stamped: a property on that SHARED object made every
690
+ // parallel read on the request throw for the cached body's whole
691
+ // execution window (a 2s cached fetch poisoned a sibling loader's
692
+ // cookies() — same hazard class as issue #684, plan 010).
613
693
  const taintedArgs: unknown[] = [];
614
694
  for (const arg of args) {
615
695
  if (isTainted(arg)) {
@@ -617,17 +697,13 @@ export function registerCachedFunction<T extends (...args: any[]) => any>(
617
697
  taintedArgs.push(arg);
618
698
  }
619
699
  }
620
- // Always stamp the ALS RequestContext so cookies()/headers() guards fire
621
- // even when the cached function receives no tainted args. The guard in
622
- // cookie-store.ts checks RequestContext, not function args.
623
- if (requestCtx) {
624
- stampCacheExec(requestCtx as object);
625
- }
626
700
 
627
701
  let result: any;
628
702
  let scoped: ReturnType<typeof runWithCacheTagScope>;
629
703
  try {
630
- scoped = runWithCacheTagScope(() => fn.apply(this, args));
704
+ scoped = runWithCacheTagScope(() =>
705
+ runWithCacheExecScope(() => fn.apply(this, args)),
706
+ );
631
707
  result = await scoped.result;
632
708
  } catch (execError) {
633
709
  // The function threw: drop the in-flight entry and reject any waiters so
@@ -640,9 +716,6 @@ export function registerCachedFunction<T extends (...args: any[]) => any>(
640
716
  for (const arg of taintedArgs) {
641
717
  unstampCacheExec(arg as object);
642
718
  }
643
- if (requestCtx) {
644
- unstampCacheExec(requestCtx as object);
645
- }
646
719
  // Remove this capture token (order-independent, safe for concurrent use)
647
720
  stopCapture?.();
648
721
  }
@@ -85,8 +85,15 @@ export const MAX_REVALIDATION_INTERVAL = 30;
85
85
  *
86
86
  * This is the default; override per store via
87
87
  * `CFCacheStoreOptions.edgeLookupTimeoutMs` (<= 0 disables the budget).
88
+ *
89
+ * 25ms, raised from 10: production Workers logs (autobarn pilot) showed the
90
+ * 10ms budget firing frequently on cold colos where the first Cache API touch
91
+ * is slow but healthy — each false positive downgrades a warm L1 HIT to an
92
+ * L2/render round trip that costs far more than the 15ms of extra patience.
93
+ * A genuinely degraded colo still gets cut off; tune per store for
94
+ * shell-critical routes.
88
95
  */
89
- export const EDGE_LOOKUP_TIMEOUT_MS = 10;
96
+ export const EDGE_LOOKUP_TIMEOUT_MS = 25;
90
97
 
91
98
  /**
92
99
  * Maximum time (ms) to wait for the BODY of a matched L1 entry to be read
@@ -57,10 +57,13 @@ import { reportCacheError, reportingAsync } from "../cache-error.js";
57
57
  import type { CacheErrorCategory } from "../cache-error.js";
58
58
  import { bufferToBase64, base64ToBuffer } from "./cf-base64.js";
59
59
  import {
60
+ KV_KEY_PRESERVED_PREFIX_BYTES,
60
61
  KV_MAX_KEY_BYTES,
61
62
  KV_MIN_EXPIRATION_TTL,
62
63
  kvKeyByteLength,
64
+ kvKeyDigest,
63
65
  remainingCacheControl,
66
+ truncateToBytes,
64
67
  } from "./cf-kv-utils.js";
65
68
  import {
66
69
  TAG_MARKER_CACHE_PREFIX,
@@ -1325,7 +1328,7 @@ export class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<TEnv> {
1325
1328
 
1326
1329
  // L2: delete from KV
1327
1330
  if (this.kv && this.waitUntil) {
1328
- const kvKey = this.toKVKey(key);
1331
+ const kvKey = await this.toKVKey(key);
1329
1332
  this.waitUntil(() =>
1330
1333
  reportingAsync(
1331
1334
  () => this.kv!.delete(kvKey),
@@ -1559,7 +1562,7 @@ export class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<TEnv> {
1559
1562
 
1560
1563
  // L2: persist to KV (KV requires expirationTtl >= 60s)
1561
1564
  if (this.kv && this.waitUntil && totalTtl >= 60) {
1562
- const kvKey = this.toDocKVKey(key);
1565
+ const kvKey = await this.toDocKVKey(key);
1563
1566
  // Finding #3: never persist a per-client signal in the KV envelope.
1564
1567
  const headersArray: [string, string][] = [];
1565
1568
  response.headers.forEach((v, k) => {
@@ -1848,7 +1851,7 @@ export class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<TEnv> {
1848
1851
  // JSON.stringify(KVItemEnvelope); field names differ from L1 so we assemble
1849
1852
  // from the pre-escaped value/handles pieces rather than re-stringifying.
1850
1853
  if (this.kv && this.waitUntil && totalTtl >= 60) {
1851
- const kvKey = this.toKVKey(`fn:${key}`);
1854
+ const kvKey = await this.toKVKey(`fn:${key}`);
1852
1855
  const expiresAt = staleAt + swrWindow * 1000;
1853
1856
  let envelopeJson = `{"v":${valueJson}`;
1854
1857
  if (handlesJson !== undefined) envelopeJson += `,"h":${handlesJson}`;
@@ -2073,20 +2076,8 @@ export class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<TEnv> {
2073
2076
  const taggedAt =
2074
2077
  Array.isArray(tags) && tags.length > 0 ? entry.createdAt : undefined;
2075
2078
 
2076
- const kvKey = this.toKVKey(`shell:${key}`);
2077
- let writeKv = retentionTtl >= KV_MIN_EXPIRATION_TTL;
2078
- const kvKeyBytes = kvKeyByteLength(kvKey);
2079
- if (kvKeyBytes > KV_MAX_KEY_BYTES) {
2080
- reportCacheError(
2081
- new Error(
2082
- `shell cache key produces a ${kvKeyBytes}-byte KV key, over the ` +
2083
- `${KV_MAX_KEY_BYTES}-byte limit; the shell was not persisted to KV (L2).`,
2084
- ),
2085
- "cache-write",
2086
- "[CFCacheStore] putShell",
2087
- );
2088
- writeKv = false;
2089
- }
2079
+ const kvKey = await this.toKVKey(`shell:${key}`);
2080
+ const writeKv = retentionTtl >= KV_MIN_EXPIRATION_TTL;
2090
2081
 
2091
2082
  const write = (async (): Promise<"stored" | "invalidated" | void> => {
2092
2083
  if (
@@ -2219,7 +2210,7 @@ export class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<TEnv> {
2219
2210
  if (!this.kv) return null;
2220
2211
  try {
2221
2212
  const readStartedAt = INTERNAL_RANGO_DEBUG ? Date.now() : 0;
2222
- const kvKey = this.toKVKey(`shell:${key}`);
2213
+ const kvKey = await this.toKVKey(`shell:${key}`);
2223
2214
  const { value: envelope, timedOut } =
2224
2215
  await this.kvGetOrEvict<CFShellEnvelope>(
2225
2216
  kvKey,
@@ -2330,11 +2321,27 @@ export class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<TEnv> {
2330
2321
  /**
2331
2322
  * Convert string key to KV key string.
2332
2323
  * Uses same version prefix as Cache API for consistent invalidation.
2324
+ *
2325
+ * Single chokepoint for EVERY KV family (segments, items, shells, documents
2326
+ * via toDocKVKey, tag markers via tagMarkerKey): a composed key over
2327
+ * Cloudflare KV's 512-byte limit is normalized to a preserved readable
2328
+ * prefix plus a SHA-256-derived 128-bit digest of the FULL key. Without
2329
+ * this, kv.put/get reject with `414 ... exceeds key length limit of 512`
2330
+ * and the entry silently never reaches L2 (observed in production for
2331
+ * "use cache" items whose serialized args — e.g. a CMS query object — blow
2332
+ * the cap; autobarn pilot). Keys are opaque storage identifiers, so
2333
+ * normalization is semantics-preserving as long as distinct logical keys
2334
+ * stay distinct: colliding requires an identical 400-byte prefix AND a
2335
+ * 128-bit SHA-256 collision. Deterministic, so every family's read, write,
2336
+ * and delete paths agree on the stored key.
2333
2337
  * @internal
2334
2338
  */
2335
- private toKVKey(key: string): string {
2339
+ private async toKVKey(key: string): Promise<string> {
2336
2340
  const versionPath = this.version ? `v/${this.version}/` : "";
2337
- return `${versionPath}${key}`;
2341
+ const composed = `${versionPath}${key}`;
2342
+ if (kvKeyByteLength(composed) <= KV_MAX_KEY_BYTES) return composed;
2343
+ const prefix = truncateToBytes(composed, KV_KEY_PRESERVED_PREFIX_BYTES);
2344
+ return `${prefix}~${await kvKeyDigest(composed)}`;
2338
2345
  }
2339
2346
 
2340
2347
  /**
@@ -2364,7 +2371,7 @@ export class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<TEnv> {
2364
2371
  * document tier is the one with a request-host context here.
2365
2372
  * @internal
2366
2373
  */
2367
- private toDocKVKey(key: string): string {
2374
+ private toDocKVKey(key: string): Promise<string> {
2368
2375
  return this.toKVKey(`h/${this.docKVHost()}/doc:${key}`);
2369
2376
  }
2370
2377
 
@@ -2480,7 +2487,7 @@ export class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<TEnv> {
2480
2487
  // ============================================================================
2481
2488
 
2482
2489
  /** KV key for a tag's invalidation marker. */
2483
- private tagMarkerKey(tag: string): string {
2490
+ private tagMarkerKey(tag: string): Promise<string> {
2484
2491
  return this.toKVKey(`${TAG_MARKER_PREFIX}${tag}`);
2485
2492
  }
2486
2493
 
@@ -2805,7 +2812,7 @@ export class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<TEnv> {
2805
2812
  // (reported cache-read), which also fails open. Either way one slow tag
2806
2813
  // never amplifies into a per-segment stall.
2807
2814
  const { value: raw, timedOut } = await this.readWithTimeout<string | null>(
2808
- () => this.kv!.get(this.tagMarkerKey(tag), { type: "text" }),
2815
+ async () => this.kv!.get(await this.tagMarkerKey(tag), { type: "text" }),
2809
2816
  this.kvReadTimeoutMs,
2810
2817
  "tag marker KV read",
2811
2818
  );
@@ -3012,7 +3019,8 @@ export class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<TEnv> {
3012
3019
  *
3013
3020
  * Durable-write integrity: the in-memory write-through (memo + L1) for a tag
3014
3021
  * runs ONLY after that tag's KV marker write is confirmed. If any KV write
3015
- * fails (transient error, or an over-512-byte key), this rejects with the
3022
+ * fails (transient error; over-limit keys are normalized by toKVKey rather
3023
+ * than rejected), this rejects with the
3016
3024
  * failed tags so an awaiting updateTag() surfaces the failure instead of
3017
3025
  * silently reporting success while other requests/colos serve stale data. The
3018
3026
  * eager purge still fires for the whole batch first (it is additive).
@@ -3065,18 +3073,10 @@ export class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<TEnv> {
3065
3073
  }
3066
3074
  await Promise.all(
3067
3075
  tags.map(async (tag) => {
3068
- const markerKey = this.tagMarkerKey(tag);
3069
- const markerKeyBytes = kvKeyByteLength(markerKey);
3070
- if (markerKeyBytes > KV_MAX_KEY_BYTES) {
3071
- failedTags.add(tag);
3072
- errors.push(
3073
- new Error(
3074
- `tag "${tag}" produces a ${markerKeyBytes}-byte KV ` +
3075
- `marker key, over the ${KV_MAX_KEY_BYTES}-byte limit`,
3076
- ),
3077
- );
3078
- return;
3079
- }
3076
+ // An over-limit tag no longer rejects: tagMarkerKey normalizes
3077
+ // through toKVKey, and the marker read path derives the key the same
3078
+ // way, so oversized tags invalidate correctly instead of erroring.
3079
+ const markerKey = await this.tagMarkerKey(tag);
3080
3080
  try {
3081
3081
  await this.kv!.put(markerKey, String(invalidatedAt), {
3082
3082
  ...(this.tagInvalidationTtl
@@ -3200,7 +3200,7 @@ export class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<TEnv> {
3200
3200
  if (!this.kv) return null;
3201
3201
 
3202
3202
  try {
3203
- const kvKey = this.toKVKey(key);
3203
+ const kvKey = await this.toKVKey(key);
3204
3204
  const { value: envelope, timedOut } =
3205
3205
  await this.kvGetOrEvict<KVSegmentEnvelope>(
3206
3206
  kvKey,
@@ -3294,29 +3294,6 @@ export class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<TEnv> {
3294
3294
  // KV requires expirationTtl >= 60s. Skip write for short-lived entries.
3295
3295
  if (!this.kv || !this.waitUntil || totalTtl < 60) return;
3296
3296
 
3297
- const kvKey = this.toKVKey(key);
3298
-
3299
- // Reject an oversized data-segment KV key the same way tag-marker keys are
3300
- // rejected in invalidateTags(). A key over KV_MAX_KEY_BYTES makes kv.put()
3301
- // fail, so the segment silently never lands in L2 (KV) and every cold-colo
3302
- // or TTL-expired read re-renders instead of serving stale. Segment keys can
3303
- // grow with user-controlled inputs (e.g. a route's search params), so report
3304
- // a clear, actionable error and skip the doomed write rather than letting it
3305
- // reject deep inside waitUntil as an opaque cache-write failure.
3306
- const kvKeyBytes = kvKeyByteLength(kvKey);
3307
- if (kvKeyBytes > KV_MAX_KEY_BYTES) {
3308
- reportCacheError(
3309
- new Error(
3310
- `cache segment key produces a ${kvKeyBytes}-byte KV key, over the ` +
3311
- `${KV_MAX_KEY_BYTES}-byte limit; the segment was not persisted to KV (L2). ` +
3312
- `Reduce the cache-key inputs (e.g. large search params on this route).`,
3313
- ),
3314
- "cache-write",
3315
- "[CFCacheStore] kvSetSegment",
3316
- );
3317
- return;
3318
- }
3319
-
3320
3297
  const expiresAt = staleAt + swrWindow * 1000;
3321
3298
  // Same wire shape as JSON.stringify({ d, s, e }) — dataJson is already
3322
3299
  // valid JSON for CachedEntryData, so embedding it avoids re-walking the tree.
@@ -3324,8 +3301,8 @@ export class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<TEnv> {
3324
3301
 
3325
3302
  this.waitUntil(() =>
3326
3303
  reportingAsync(
3327
- () =>
3328
- this.kv!.put(kvKey, envelopeJson, {
3304
+ async () =>
3305
+ this.kv!.put(await this.toKVKey(key), envelopeJson, {
3329
3306
  expirationTtl: totalTtl,
3330
3307
  }),
3331
3308
  "cache-write",
@@ -3386,7 +3363,7 @@ export class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<TEnv> {
3386
3363
  if (!this.kv) return null;
3387
3364
 
3388
3365
  try {
3389
- const kvKey = this.toKVKey(`fn:${key}`);
3366
+ const kvKey = await this.toKVKey(`fn:${key}`);
3390
3367
  const { value: envelope, timedOut } =
3391
3368
  await this.kvGetOrEvict<KVItemEnvelope>(
3392
3369
  kvKey,
@@ -3508,7 +3485,7 @@ export class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<TEnv> {
3508
3485
  if (!this.kv) return null;
3509
3486
 
3510
3487
  try {
3511
- const kvKey = this.toDocKVKey(key);
3488
+ const kvKey = await this.toDocKVKey(key);
3512
3489
  // The document path is debug-silent (op is only get/getItem): a KV-read
3513
3490
  // timeout here is bounded for resilience parity (kvGetOrEvict applies the
3514
3491
  // budget) but emits no kv-timeout event, so its absence from the debug
@@ -333,7 +333,7 @@ export interface CFCacheStoreOptions<TEnv = unknown> {
333
333
  * cannot stall the request; the read then falls through to its normal miss
334
334
  * path (L2/KV or render).
335
335
  *
336
- * Defaults to {@link EDGE_LOOKUP_TIMEOUT_MS} (10). Set to 0 (or any value
336
+ * Defaults to {@link EDGE_LOOKUP_TIMEOUT_MS} (25). Set to 0 (or any value
337
337
  * <= 0) to disable the budget and always await `match`.
338
338
  */
339
339
  edgeLookupTimeoutMs?: number;
@@ -26,6 +26,44 @@ export function kvKeyByteLength(key: string): number {
26
26
  return kvKeyEncoder.encode(key).length;
27
27
  }
28
28
 
29
+ /**
30
+ * Bytes of a composed key preserved verbatim when an over-limit key is
31
+ * normalized (toKVKey). 400 leaves room for the `~` separator and the 32-hex
32
+ * digest inside KV_MAX_KEY_BYTES while keeping the version prefix and most of
33
+ * the logical key readable (and prefix-listable) in the KV dashboard.
34
+ */
35
+ export const KV_KEY_PRESERVED_PREFIX_BYTES = 400;
36
+
37
+ /**
38
+ * Truncate to at most `maxBytes` of UTF-8. A multibyte sequence split at the
39
+ * boundary decodes to U+FFFD, which is stripped — the result is deterministic
40
+ * for a given input, which is all key normalization needs.
41
+ */
42
+ export function truncateToBytes(value: string, maxBytes: number): string {
43
+ const bytes = kvKeyEncoder.encode(value);
44
+ if (bytes.length <= maxBytes) return value;
45
+ return new TextDecoder()
46
+ .decode(bytes.subarray(0, maxBytes))
47
+ .replace(/�+$/, "");
48
+ }
49
+
50
+ /**
51
+ * 128-bit hex digest (SHA-256 truncated) of a composed KV key. WebCrypto is
52
+ * available on workerd and Node alike; SHA-256 (not a fast non-crypto hash)
53
+ * because cache keys embed user-influenced serialized args — an engineered
54
+ * collision between two normalized keys would serve one entry's payload for
55
+ * the other's.
56
+ */
57
+ export async function kvKeyDigest(value: string): Promise<string> {
58
+ const digest = await crypto.subtle.digest(
59
+ "SHA-256",
60
+ kvKeyEncoder.encode(value),
61
+ );
62
+ return Array.from(new Uint8Array(digest, 0, 16), (b) =>
63
+ b.toString(16).padStart(2, "0"),
64
+ ).join("");
65
+ }
66
+
29
67
  /**
30
68
  * Compute the Cache-Control directive for a stale-path REVALIDATING re-put from
31
69
  * the entry's stored hard-expiry deadline (CACHE_EXPIRES_AT_HEADER). Returns the
@@ -18,6 +18,12 @@ import {
18
18
  } from "@vitejs/plugin-rsc/rsc";
19
19
  import { createFromReadableStream } from "@vitejs/plugin-rsc/rsc";
20
20
 
21
+ // Preserve embedded server references on a cache/prerender HIT so they
22
+ // re-serialize to the client instead of resolving to a raw function React
23
+ // refuses to pass to a Client Component. Shared across the deserialize sites so
24
+ // the decode policy has a single owner.
25
+ const PRESERVE_SERVER_REFS = { preserveServerReferences: true } as const;
26
+
21
27
  /**
22
28
  * Convert a ReadableStream to a string.
23
29
  */
@@ -81,7 +87,11 @@ export async function rscDeserialize<T>(
81
87
 
82
88
  const temporaryReferences = createTemporaryReferenceSet();
83
89
  const stream = stringToStream(encoded);
84
- return createFromReadableStream<T>(stream, { temporaryReferences });
90
+ return createFromReadableStream<T>(
91
+ stream,
92
+ { temporaryReferences },
93
+ PRESERVE_SERVER_REFS,
94
+ );
85
95
  }
86
96
 
87
97
  /**
@@ -116,7 +126,11 @@ export async function serializeResult(value: unknown): Promise<string | null> {
116
126
  export async function deserializeResult<T>(encoded: string): Promise<T> {
117
127
  const temporaryReferences = createTemporaryReferenceSet();
118
128
  const stream = stringToStream(encoded);
119
- return createFromReadableStream<T>(stream, { temporaryReferences });
129
+ return createFromReadableStream<T>(
130
+ stream,
131
+ { temporaryReferences },
132
+ PRESERVE_SERVER_REFS,
133
+ );
120
134
  }
121
135
 
122
136
  /**
@@ -271,9 +285,11 @@ export async function deserializeSegments(
271
285
 
272
286
  const [component, layout, loaderData, loaderDataPromise, loadingData] =
273
287
  await Promise.all([
274
- createFromReadableStream(stringToStream(item.encoded), {
275
- temporaryReferences,
276
- }),
288
+ createFromReadableStream(
289
+ stringToStream(item.encoded),
290
+ { temporaryReferences },
291
+ PRESERVE_SERVER_REFS,
292
+ ),
277
293
  rscDeserialize(item.encodedLayout),
278
294
  rscDeserialize(item.encodedLoaderData),
279
295
  rscDeserialize(item.encodedLoaderDataPromise),
@@ -21,14 +21,19 @@ export function isTainted(value: unknown): boolean {
21
21
  }
22
22
 
23
23
  /**
24
- * Symbol stamped on tainted ctx during "use cache" function execution.
25
- * cookies(), headers(), ctx.set(), ctx.header(), etc. check this flag and
26
- * throw if present — reads would cache per-request data under a shared key,
27
- * and side effects would be lost on cache hit.
24
+ * Symbol stamped on tainted ctx objects PASSED AS ARGUMENTS to a "use cache"
25
+ * function during its execution. ctx.set(), ctx.header(), etc. check this
26
+ * flag and throw if present — those side effects are lost on cache hit.
27
+ *
28
+ * Argument objects only. Ambient access (cookies()/headers() and ctx methods
29
+ * reached via getRequestContext()) is guarded by the AsyncLocalStorage scope
30
+ * in cache-exec-scope.ts instead: stamping the SHARED RequestContext made
31
+ * every parallel read on the request throw for the cached body's whole
32
+ * execution window (a 2s cached fetch poisoned a sibling loader's cookies()).
28
33
  *
29
34
  * The value is a numeric reference count, not a boolean. Multiple concurrent
30
- * cached functions sharing the same ctx/requestCtx each increment on entry
31
- * and decrement on exit. Guards fire when count > 0.
35
+ * cached functions sharing the same ctx each increment on entry and decrement
36
+ * on exit. Guards fire when count > 0.
32
37
  */
33
38
  export const INSIDE_CACHE_EXEC: unique symbol = Symbol.for(
34
39
  "rango:inside-cache-exec",
@@ -56,6 +61,20 @@ export function unstampCacheExec(obj: object): void {
56
61
  }
57
62
  }
58
63
 
64
+ /**
65
+ * Probe for the AsyncLocalStorage-based "use cache" execution scope.
66
+ * Registered by cache-exec-scope.ts at module init; the default ("never
67
+ * inside") keeps this module free of node:async_hooks — taint.ts is reachable
68
+ * from browser-bundled DSL helpers. Covers AMBIENT ctx access inside a cached
69
+ * body (getRequestContext().set(...)) chain-scoped, where the object stamp
70
+ * below only covers ctx objects explicitly passed as arguments.
71
+ */
72
+ let cacheExecScopeProbe: () => boolean = () => false;
73
+
74
+ export function _setCacheExecScopeProbe(probe: () => boolean): void {
75
+ cacheExecScopeProbe = probe;
76
+ }
77
+
59
78
  /**
60
79
  * Throw if ctx is inside a "use cache" execution.
61
80
  * Call from side-effecting ctx methods (set, header, etc.) and cookie mutations.
@@ -64,12 +83,12 @@ export function assertNotInsideCacheExec(
64
83
  ctx: unknown,
65
84
  methodName: string,
66
85
  ): void {
67
- if (
86
+ const argStamped =
68
87
  ctx !== null &&
69
88
  ctx !== undefined &&
70
89
  typeof ctx === "object" &&
71
- (INSIDE_CACHE_EXEC as symbol) in (ctx as Record<symbol, unknown>)
72
- ) {
90
+ (INSIDE_CACHE_EXEC as symbol) in (ctx as Record<symbol, unknown>);
91
+ if (argStamped || cacheExecScopeProbe()) {
73
92
  throw new Error(
74
93
  `ctx.${methodName}() cannot be called inside a "use cache" function. ` +
75
94
  `Side effects on the request context are lost on cache hit because ` +