@rangojs/router 0.5.1 → 0.5.2

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 (448) hide show
  1. package/README.md +5 -1
  2. package/dist/types/browser/event-controller.d.ts +6 -0
  3. package/dist/types/cache/cf/cf-cache-constants.d.ts +8 -1
  4. package/dist/types/cache/cf/cf-cache-store.d.ts +15 -1
  5. package/dist/types/cache/cf/cf-cache-types.d.ts +1 -1
  6. package/dist/types/cache/cf/cf-kv-utils.d.ts +21 -0
  7. package/dist/types/handles/is-thenable.d.ts +2 -4
  8. package/dist/types/rsc/helpers.d.ts +3 -0
  9. package/dist/types/rsc/render-pipeline.d.ts +9 -0
  10. package/dist/types/rsc/routine-plan.d.ts +124 -0
  11. package/dist/types/rsc/shell-capture-constants.d.ts +17 -0
  12. package/dist/types/server/request-context.d.ts +4 -1
  13. package/dist/types/vite/encryption-key.d.ts +2 -0
  14. package/dist/types/vite/plugins/expose-internal-ids.d.ts +10 -0
  15. package/dist/types/vite/plugins/server-ref-hashing.d.ts +24 -0
  16. package/dist/types/vite/plugins/server-reference-pattern.d.ts +1 -0
  17. package/dist/types/vite/utils/shared-utils.d.ts +12 -0
  18. package/dist/vite/index.js +154 -57
  19. package/package.json +3 -3
  20. package/skills/caching/SKILL.md +1 -1
  21. package/skills/mime-routes/SKILL.md +3 -1
  22. package/skills/response-routes/SKILL.md +4 -2
  23. package/skills/typesafety/generated-files-and-cli.md +16 -9
  24. package/skills/typesafety/route-types.md +5 -1
  25. package/skills/use-cache/SKILL.md +47 -0
  26. package/src/browser/event-controller.ts +40 -15
  27. package/src/browser/partial-update.ts +26 -11
  28. package/src/browser/react/NavigationProvider.tsx +7 -0
  29. package/src/cache/cache-runtime.ts +89 -15
  30. package/src/cache/cf/cf-cache-constants.ts +8 -1
  31. package/src/cache/cf/cf-cache-store.ts +41 -64
  32. package/src/cache/cf/cf-cache-types.ts +1 -1
  33. package/src/cache/cf/cf-kv-utils.ts +38 -0
  34. package/src/cache/segment-codec.ts +21 -5
  35. package/src/handles/is-thenable.ts +2 -4
  36. package/src/router/match-middleware/cache-lookup.ts +24 -15
  37. package/src/rsc/handler.ts +26 -5
  38. package/src/rsc/helpers.ts +13 -0
  39. package/src/rsc/progressive-enhancement.ts +247 -70
  40. package/src/rsc/render-pipeline.ts +68 -24
  41. package/src/rsc/routine-plan.ts +359 -0
  42. package/src/rsc/rsc-rendering.ts +555 -302
  43. package/src/rsc/server-action.ts +180 -71
  44. package/src/rsc/shell-capture-constants.ts +18 -0
  45. package/src/rsc/shell-capture.ts +92 -21
  46. package/src/server/request-context.ts +6 -0
  47. package/src/ssr/ssr-root.tsx +1 -0
  48. package/src/vite/encryption-key.ts +29 -0
  49. package/src/vite/plugins/expose-action-id.ts +2 -2
  50. package/src/vite/plugins/expose-internal-ids.ts +46 -0
  51. package/src/vite/plugins/server-ref-hashing.ts +74 -0
  52. package/src/vite/plugins/server-reference-pattern.ts +10 -0
  53. package/src/vite/rango.ts +9 -0
  54. package/src/vite/router-discovery.ts +21 -2
  55. package/src/vite/utils/shared-utils.ts +12 -7
  56. package/dist/types.backup/__internal.d.ts +0 -127
  57. package/dist/types.backup/bin/rango.d.ts +0 -1
  58. package/dist/types.backup/browser/action-coordinator.d.ts +0 -57
  59. package/dist/types.backup/browser/action-fence.d.ts +0 -33
  60. package/dist/types.backup/browser/app-shell.d.ts +0 -34
  61. package/dist/types.backup/browser/app-version.d.ts +0 -6
  62. package/dist/types.backup/browser/connection-warmup.d.ts +0 -31
  63. package/dist/types.backup/browser/cookie-name.d.ts +0 -66
  64. package/dist/types.backup/browser/event-controller.d.ts +0 -221
  65. package/dist/types.backup/browser/history-state.d.ts +0 -26
  66. package/dist/types.backup/browser/index.d.ts +0 -1
  67. package/dist/types.backup/browser/intercept-utils.d.ts +0 -30
  68. package/dist/types.backup/browser/invalidate-client-cache.d.ts +0 -17
  69. package/dist/types.backup/browser/link-interceptor.d.ts +0 -43
  70. package/dist/types.backup/browser/logging.d.ts +0 -33
  71. package/dist/types.backup/browser/merge-segment-loaders.d.ts +0 -38
  72. package/dist/types.backup/browser/navigation-bridge.d.ts +0 -27
  73. package/dist/types.backup/browser/navigation-client.d.ts +0 -17
  74. package/dist/types.backup/browser/navigation-store-handle.d.ts +0 -25
  75. package/dist/types.backup/browser/navigation-store.d.ts +0 -95
  76. package/dist/types.backup/browser/navigation-transaction.d.ts +0 -75
  77. package/dist/types.backup/browser/network-error-handler.d.ts +0 -35
  78. package/dist/types.backup/browser/partial-update.d.ts +0 -61
  79. package/dist/types.backup/browser/prefetch/cache.d.ts +0 -183
  80. package/dist/types.backup/browser/prefetch/fetch.d.ts +0 -52
  81. package/dist/types.backup/browser/prefetch/observer.d.ts +0 -27
  82. package/dist/types.backup/browser/prefetch/policy.d.ts +0 -13
  83. package/dist/types.backup/browser/prefetch/queue.d.ts +0 -48
  84. package/dist/types.backup/browser/prefetch/resource-ready.d.ts +0 -28
  85. package/dist/types.backup/browser/rango-state.d.ts +0 -52
  86. package/dist/types.backup/browser/react/Link.d.ts +0 -140
  87. package/dist/types.backup/browser/react/NavigationProvider.d.ts +0 -88
  88. package/dist/types.backup/browser/react/ScrollRestoration.d.ts +0 -78
  89. package/dist/types.backup/browser/react/context.d.ts +0 -54
  90. package/dist/types.backup/browser/react/filter-segment-order.d.ts +0 -35
  91. package/dist/types.backup/browser/react/index.d.ts +0 -1
  92. package/dist/types.backup/browser/react/location-state-shared.d.ts +0 -162
  93. package/dist/types.backup/browser/react/location-state.d.ts +0 -29
  94. package/dist/types.backup/browser/react/mount-context.d.ts +0 -23
  95. package/dist/types.backup/browser/react/nonce-context.d.ts +0 -14
  96. package/dist/types.backup/browser/react/shallow-equal.d.ts +0 -5
  97. package/dist/types.backup/browser/react/use-action.d.ts +0 -61
  98. package/dist/types.backup/browser/react/use-handle.d.ts +0 -21
  99. package/dist/types.backup/browser/react/use-href.d.ts +0 -32
  100. package/dist/types.backup/browser/react/use-link-status.d.ts +0 -36
  101. package/dist/types.backup/browser/react/use-mount.d.ts +0 -24
  102. package/dist/types.backup/browser/react/use-navigation.d.ts +0 -15
  103. package/dist/types.backup/browser/react/use-params.d.ts +0 -21
  104. package/dist/types.backup/browser/react/use-pathname.d.ts +0 -13
  105. package/dist/types.backup/browser/react/use-reverse.d.ts +0 -40
  106. package/dist/types.backup/browser/react/use-router.d.ts +0 -23
  107. package/dist/types.backup/browser/react/use-search-params.d.ts +0 -19
  108. package/dist/types.backup/browser/react/use-segments.d.ts +0 -29
  109. package/dist/types.backup/browser/response-adapter.d.ts +0 -58
  110. package/dist/types.backup/browser/rsc-router.d.ts +0 -141
  111. package/dist/types.backup/browser/scroll-restoration.d.ts +0 -103
  112. package/dist/types.backup/browser/segment-reconciler.d.ts +0 -74
  113. package/dist/types.backup/browser/segment-structure-assert.d.ts +0 -16
  114. package/dist/types.backup/browser/server-action-bridge.d.ts +0 -29
  115. package/dist/types.backup/browser/types.d.ts +0 -530
  116. package/dist/types.backup/browser/validate-redirect-origin.d.ts +0 -28
  117. package/dist/types.backup/build/collect-fallback-refs.d.ts +0 -5
  118. package/dist/types.backup/build/generate-manifest.d.ts +0 -100
  119. package/dist/types.backup/build/generate-route-types.d.ts +0 -8
  120. package/dist/types.backup/build/index.d.ts +0 -21
  121. package/dist/types.backup/build/prefix-tree-utils.d.ts +0 -56
  122. package/dist/types.backup/build/route-trie.d.ts +0 -89
  123. package/dist/types.backup/build/route-types/ast-helpers.d.ts +0 -3
  124. package/dist/types.backup/build/route-types/ast-route-extraction.d.ts +0 -13
  125. package/dist/types.backup/build/route-types/codegen.d.ts +0 -16
  126. package/dist/types.backup/build/route-types/include-resolution.d.ts +0 -74
  127. package/dist/types.backup/build/route-types/param-extraction.d.ts +0 -13
  128. package/dist/types.backup/build/route-types/per-module-writer.d.ts +0 -18
  129. package/dist/types.backup/build/route-types/router-processing.d.ts +0 -82
  130. package/dist/types.backup/build/route-types/scan-filter.d.ts +0 -17
  131. package/dist/types.backup/build/route-types/source-scan.d.ts +0 -13
  132. package/dist/types.backup/build/runtime-discovery.d.ts +0 -24
  133. package/dist/types.backup/cache/background-task.d.ts +0 -21
  134. package/dist/types.backup/cache/cache-error.d.ts +0 -71
  135. package/dist/types.backup/cache/cache-key-utils.d.ts +0 -35
  136. package/dist/types.backup/cache/cache-policy.d.ts +0 -59
  137. package/dist/types.backup/cache/cache-runtime.d.ts +0 -51
  138. package/dist/types.backup/cache/cache-scope.d.ts +0 -134
  139. package/dist/types.backup/cache/cache-tag.d.ts +0 -79
  140. package/dist/types.backup/cache/cf/cf-base64.d.ts +0 -4
  141. package/dist/types.backup/cache/cf/cf-cache-constants.d.ts +0 -105
  142. package/dist/types.backup/cache/cf/cf-cache-store.d.ts +0 -481
  143. package/dist/types.backup/cache/cf/cf-cache-types.d.ts +0 -300
  144. package/dist/types.backup/cache/cf/cf-kv-utils.d.ts +0 -22
  145. package/dist/types.backup/cache/cf/cf-tag-marker-memo.d.ts +0 -15
  146. package/dist/types.backup/cache/cf/index.d.ts +0 -3
  147. package/dist/types.backup/cache/document-cache.d.ts +0 -69
  148. package/dist/types.backup/cache/handle-capture.d.ts +0 -23
  149. package/dist/types.backup/cache/handle-snapshot.d.ts +0 -39
  150. package/dist/types.backup/cache/index.d.ts +0 -7
  151. package/dist/types.backup/cache/memory-segment-store.d.ts +0 -163
  152. package/dist/types.backup/cache/profile-registry.d.ts +0 -40
  153. package/dist/types.backup/cache/read-through-swr.d.ts +0 -60
  154. package/dist/types.backup/cache/segment-codec.d.ts +0 -78
  155. package/dist/types.backup/cache/shell-snapshot.d.ts +0 -162
  156. package/dist/types.backup/cache/tag-invalidation.d.ts +0 -74
  157. package/dist/types.backup/cache/taint.d.ts +0 -71
  158. package/dist/types.backup/cache/types.d.ts +0 -407
  159. package/dist/types.backup/cache/vercel/index.d.ts +0 -1
  160. package/dist/types.backup/cache/vercel/vercel-cache-store.d.ts +0 -267
  161. package/dist/types.backup/client.d.ts +0 -184
  162. package/dist/types.backup/client.rsc.d.ts +0 -39
  163. package/dist/types.backup/cloudflare/index.d.ts +0 -7
  164. package/dist/types.backup/cloudflare/tracing.d.ts +0 -53
  165. package/dist/types.backup/component-utils.d.ts +0 -46
  166. package/dist/types.backup/components/DefaultDocument.d.ts +0 -13
  167. package/dist/types.backup/context-var.d.ts +0 -84
  168. package/dist/types.backup/debug.d.ts +0 -57
  169. package/dist/types.backup/decode-loader-results.d.ts +0 -5
  170. package/dist/types.backup/default-error-boundary.d.ts +0 -10
  171. package/dist/types.backup/defer.d.ts +0 -89
  172. package/dist/types.backup/deps/browser.d.ts +0 -1
  173. package/dist/types.backup/deps/html-stream-client.d.ts +0 -1
  174. package/dist/types.backup/deps/html-stream-server.d.ts +0 -1
  175. package/dist/types.backup/deps/rsc.d.ts +0 -1
  176. package/dist/types.backup/deps/ssr.d.ts +0 -1
  177. package/dist/types.backup/encode-kv.d.ts +0 -35
  178. package/dist/types.backup/errors.d.ts +0 -226
  179. package/dist/types.backup/escape-script.d.ts +0 -44
  180. package/dist/types.backup/handle.d.ts +0 -93
  181. package/dist/types.backup/handles/MetaTags.d.ts +0 -17
  182. package/dist/types.backup/handles/Scripts.d.ts +0 -38
  183. package/dist/types.backup/handles/breadcrumbs.d.ts +0 -43
  184. package/dist/types.backup/handles/deferred-resolution.d.ts +0 -53
  185. package/dist/types.backup/handles/is-thenable.d.ts +0 -12
  186. package/dist/types.backup/handles/meta.d.ts +0 -43
  187. package/dist/types.backup/handles/script.d.ts +0 -139
  188. package/dist/types.backup/host/cookie-handler.d.ts +0 -8
  189. package/dist/types.backup/host/errors.d.ts +0 -40
  190. package/dist/types.backup/host/index.d.ts +0 -33
  191. package/dist/types.backup/host/pattern-matcher.d.ts +0 -30
  192. package/dist/types.backup/host/router.d.ts +0 -12
  193. package/dist/types.backup/host/testing.d.ts +0 -41
  194. package/dist/types.backup/host/types.d.ts +0 -148
  195. package/dist/types.backup/host/utils.d.ts +0 -20
  196. package/dist/types.backup/href-client.d.ts +0 -214
  197. package/dist/types.backup/index.d.ts +0 -112
  198. package/dist/types.backup/index.rsc.d.ts +0 -51
  199. package/dist/types.backup/internal-debug.d.ts +0 -1
  200. package/dist/types.backup/loader-store.d.ts +0 -193
  201. package/dist/types.backup/loader.d.ts +0 -18
  202. package/dist/types.backup/loader.rsc.d.ts +0 -18
  203. package/dist/types.backup/missing-id-error.d.ts +0 -1
  204. package/dist/types.backup/outlet-context.d.ts +0 -12
  205. package/dist/types.backup/outlet-provider.d.ts +0 -12
  206. package/dist/types.backup/prerender/build-shell-capture.d.ts +0 -104
  207. package/dist/types.backup/prerender/param-hash.d.ts +0 -6
  208. package/dist/types.backup/prerender/shell-manifest-key.d.ts +0 -18
  209. package/dist/types.backup/prerender/store.d.ts +0 -62
  210. package/dist/types.backup/prerender.d.ts +0 -292
  211. package/dist/types.backup/redirect-origin.d.ts +0 -55
  212. package/dist/types.backup/regex-escape.d.ts +0 -6
  213. package/dist/types.backup/render-error-thrower.d.ts +0 -13
  214. package/dist/types.backup/response-utils.d.ts +0 -35
  215. package/dist/types.backup/reverse.d.ts +0 -206
  216. package/dist/types.backup/root-error-boundary.d.ts +0 -32
  217. package/dist/types.backup/route-content-wrapper.d.ts +0 -40
  218. package/dist/types.backup/route-definition/dsl-helpers.d.ts +0 -130
  219. package/dist/types.backup/route-definition/helper-factories.d.ts +0 -22
  220. package/dist/types.backup/route-definition/helpers-types.d.ts +0 -392
  221. package/dist/types.backup/route-definition/index.d.ts +0 -7
  222. package/dist/types.backup/route-definition/redirect.d.ts +0 -48
  223. package/dist/types.backup/route-definition/resolve-handler-use.d.ts +0 -19
  224. package/dist/types.backup/route-definition/use-item-types.d.ts +0 -1
  225. package/dist/types.backup/route-definition.d.ts +0 -1
  226. package/dist/types.backup/route-map-builder.d.ts +0 -127
  227. package/dist/types.backup/route-name.d.ts +0 -27
  228. package/dist/types.backup/route-types.d.ts +0 -172
  229. package/dist/types.backup/router/basename.d.ts +0 -10
  230. package/dist/types.backup/router/content-negotiation.d.ts +0 -91
  231. package/dist/types.backup/router/debug-manifest.d.ts +0 -7
  232. package/dist/types.backup/router/error-handling.d.ts +0 -76
  233. package/dist/types.backup/router/find-match.d.ts +0 -19
  234. package/dist/types.backup/router/handler-context.d.ts +0 -41
  235. package/dist/types.backup/router/instrument.d.ts +0 -161
  236. package/dist/types.backup/router/intercept-resolution.d.ts +0 -79
  237. package/dist/types.backup/router/lazy-includes.d.ts +0 -26
  238. package/dist/types.backup/router/loader-resolution.d.ts +0 -63
  239. package/dist/types.backup/router/logging.d.ts +0 -41
  240. package/dist/types.backup/router/manifest.d.ts +0 -8
  241. package/dist/types.backup/router/match-api.d.ts +0 -19
  242. package/dist/types.backup/router/match-context.d.ts +0 -184
  243. package/dist/types.backup/router/match-handlers.d.ts +0 -49
  244. package/dist/types.backup/router/match-middleware/background-revalidation.d.ts +0 -113
  245. package/dist/types.backup/router/match-middleware/cache-lookup.d.ts +0 -113
  246. package/dist/types.backup/router/match-middleware/cache-store.d.ts +0 -112
  247. package/dist/types.backup/router/match-middleware/index.d.ts +0 -80
  248. package/dist/types.backup/router/match-middleware/intercept-resolution.d.ts +0 -116
  249. package/dist/types.backup/router/match-middleware/segment-resolution.d.ts +0 -94
  250. package/dist/types.backup/router/match-pipelines.d.ts +0 -103
  251. package/dist/types.backup/router/match-result.d.ts +0 -114
  252. package/dist/types.backup/router/metrics.d.ts +0 -6
  253. package/dist/types.backup/router/middleware-types.d.ts +0 -74
  254. package/dist/types.backup/router/middleware.d.ts +0 -116
  255. package/dist/types.backup/router/navigation-snapshot.d.ts +0 -22
  256. package/dist/types.backup/router/params-util.d.ts +0 -8
  257. package/dist/types.backup/router/parse-pattern.d.ts +0 -38
  258. package/dist/types.backup/router/pattern-matching.d.ts +0 -169
  259. package/dist/types.backup/router/prefetch-cache-ttl.d.ts +0 -27
  260. package/dist/types.backup/router/prefetch-limits.d.ts +0 -20
  261. package/dist/types.backup/router/prerender-match.d.ts +0 -50
  262. package/dist/types.backup/router/preview-match.d.ts +0 -22
  263. package/dist/types.backup/router/request-classification.d.ts +0 -104
  264. package/dist/types.backup/router/revalidation.d.ts +0 -57
  265. package/dist/types.backup/router/route-snapshot.d.ts +0 -112
  266. package/dist/types.backup/router/router-context.d.ts +0 -137
  267. package/dist/types.backup/router/router-interfaces.d.ts +0 -432
  268. package/dist/types.backup/router/router-options.d.ts +0 -738
  269. package/dist/types.backup/router/router-registry.d.ts +0 -15
  270. package/dist/types.backup/router/segment-resolution/fresh.d.ts +0 -55
  271. package/dist/types.backup/router/segment-resolution/helpers.d.ts +0 -93
  272. package/dist/types.backup/router/segment-resolution/loader-cache.d.ts +0 -33
  273. package/dist/types.backup/router/segment-resolution/loader-mask.d.ts +0 -44
  274. package/dist/types.backup/router/segment-resolution/loader-snapshot.d.ts +0 -90
  275. package/dist/types.backup/router/segment-resolution/mask-nested.d.ts +0 -53
  276. package/dist/types.backup/router/segment-resolution/revalidation.d.ts +0 -85
  277. package/dist/types.backup/router/segment-resolution/static-store.d.ts +0 -17
  278. package/dist/types.backup/router/segment-resolution/streamed-handler-telemetry.d.ts +0 -16
  279. package/dist/types.backup/router/segment-resolution/view-transition-default.d.ts +0 -28
  280. package/dist/types.backup/router/segment-resolution.d.ts +0 -3
  281. package/dist/types.backup/router/segment-wrappers.d.ts +0 -53
  282. package/dist/types.backup/router/state-cookie-name.d.ts +0 -1
  283. package/dist/types.backup/router/substitute-pattern-params.d.ts +0 -23
  284. package/dist/types.backup/router/telemetry-otel.d.ts +0 -113
  285. package/dist/types.backup/router/telemetry.d.ts +0 -215
  286. package/dist/types.backup/router/timeout.d.ts +0 -68
  287. package/dist/types.backup/router/tracing.d.ts +0 -125
  288. package/dist/types.backup/router/trie-matching.d.ts +0 -32
  289. package/dist/types.backup/router/types.d.ts +0 -98
  290. package/dist/types.backup/router/url-params.d.ts +0 -26
  291. package/dist/types.backup/router.d.ts +0 -7
  292. package/dist/types.backup/rsc/capture-queue.d.ts +0 -6
  293. package/dist/types.backup/rsc/full-payload.d.ts +0 -22
  294. package/dist/types.backup/rsc/handler-context.d.ts +0 -31
  295. package/dist/types.backup/rsc/handler.d.ts +0 -9
  296. package/dist/types.backup/rsc/helpers.d.ts +0 -213
  297. package/dist/types.backup/rsc/index.d.ts +0 -17
  298. package/dist/types.backup/rsc/json-route-result.d.ts +0 -20
  299. package/dist/types.backup/rsc/loader-fetch.d.ts +0 -14
  300. package/dist/types.backup/rsc/manifest-init.d.ts +0 -18
  301. package/dist/types.backup/rsc/nonce.d.ts +0 -28
  302. package/dist/types.backup/rsc/origin-guard.d.ts +0 -50
  303. package/dist/types.backup/rsc/progressive-enhancement.d.ts +0 -19
  304. package/dist/types.backup/rsc/redirect-guard.d.ts +0 -35
  305. package/dist/types.backup/rsc/response-cache-serve.d.ts +0 -46
  306. package/dist/types.backup/rsc/response-error.d.ts +0 -19
  307. package/dist/types.backup/rsc/response-route-handler.d.ts +0 -29
  308. package/dist/types.backup/rsc/rsc-rendering.d.ts +0 -23
  309. package/dist/types.backup/rsc/runtime-warnings.d.ts +0 -22
  310. package/dist/types.backup/rsc/server-action.d.ts +0 -68
  311. package/dist/types.backup/rsc/shell-build-manifest.d.ts +0 -84
  312. package/dist/types.backup/rsc/shell-capture-constants.d.ts +0 -27
  313. package/dist/types.backup/rsc/shell-capture.d.ts +0 -374
  314. package/dist/types.backup/rsc/shell-serve.d.ts +0 -136
  315. package/dist/types.backup/rsc/ssr-setup.d.ts +0 -48
  316. package/dist/types.backup/rsc/transition-gate.d.ts +0 -27
  317. package/dist/types.backup/rsc/types.d.ts +0 -290
  318. package/dist/types.backup/runtime-env.d.ts +0 -1
  319. package/dist/types.backup/search-params.d.ts +0 -125
  320. package/dist/types.backup/segment-content-promise.d.ts +0 -13
  321. package/dist/types.backup/segment-fragments.d.ts +0 -56
  322. package/dist/types.backup/segment-loader-promise.d.ts +0 -22
  323. package/dist/types.backup/segment-system.d.ts +0 -84
  324. package/dist/types.backup/serialize.d.ts +0 -164
  325. package/dist/types.backup/server/context.d.ts +0 -494
  326. package/dist/types.backup/server/cookie-parse.d.ts +0 -10
  327. package/dist/types.backup/server/cookie-store.d.ts +0 -107
  328. package/dist/types.backup/server/fetchable-loader-store.d.ts +0 -20
  329. package/dist/types.backup/server/handle-store.d.ts +0 -100
  330. package/dist/types.backup/server/loader-registry.d.ts +0 -32
  331. package/dist/types.backup/server/request-context.d.ts +0 -596
  332. package/dist/types.backup/server/root-layout.d.ts +0 -3
  333. package/dist/types.backup/server.d.ts +0 -15
  334. package/dist/types.backup/ssr/index.d.ts +0 -233
  335. package/dist/types.backup/ssr/inject-rsc-eager.d.ts +0 -3
  336. package/dist/types.backup/ssr/preinit-client-references.d.ts +0 -71
  337. package/dist/types.backup/ssr/ssr-root.d.ts +0 -69
  338. package/dist/types.backup/static-handler.d.ts +0 -57
  339. package/dist/types.backup/testing/cache-status.d.ts +0 -63
  340. package/dist/types.backup/testing/collect-handle.d.ts +0 -20
  341. package/dist/types.backup/testing/dispatch.d.ts +0 -123
  342. package/dist/types.backup/testing/dom.entry.d.ts +0 -15
  343. package/dist/types.backup/testing/e2e/fixture.d.ts +0 -37
  344. package/dist/types.backup/testing/e2e/index.d.ts +0 -30
  345. package/dist/types.backup/testing/e2e/matchers.d.ts +0 -17
  346. package/dist/types.backup/testing/e2e/page-helpers.d.ts +0 -62
  347. package/dist/types.backup/testing/e2e/parity.d.ts +0 -111
  348. package/dist/types.backup/testing/e2e/server.d.ts +0 -35
  349. package/dist/types.backup/testing/flight-matchers.d.ts +0 -55
  350. package/dist/types.backup/testing/flight-normalize.d.ts +0 -1
  351. package/dist/types.backup/testing/flight-tree.d.ts +0 -192
  352. package/dist/types.backup/testing/flight.d.ts +0 -115
  353. package/dist/types.backup/testing/flight.entry.d.ts +0 -27
  354. package/dist/types.backup/testing/generated-routes.d.ts +0 -66
  355. package/dist/types.backup/testing/index.d.ts +0 -52
  356. package/dist/types.backup/testing/internal/context.d.ts +0 -225
  357. package/dist/types.backup/testing/internal/flight-client-globals.d.ts +0 -1
  358. package/dist/types.backup/testing/internal/seed-vars.d.ts +0 -30
  359. package/dist/types.backup/testing/render-handler.d.ts +0 -160
  360. package/dist/types.backup/testing/render-route.d.ts +0 -246
  361. package/dist/types.backup/testing/run-loader.d.ts +0 -186
  362. package/dist/types.backup/testing/run-middleware.d.ts +0 -132
  363. package/dist/types.backup/testing/run-transition-when.d.ts +0 -77
  364. package/dist/types.backup/testing/vitest-stubs/cloudflare-email.d.ts +0 -6
  365. package/dist/types.backup/testing/vitest-stubs/cloudflare-workers.d.ts +0 -13
  366. package/dist/types.backup/testing/vitest-stubs/plugin-rsc.d.ts +0 -7
  367. package/dist/types.backup/testing/vitest-stubs/version.d.ts +0 -1
  368. package/dist/types.backup/testing/vitest.d.ts +0 -205
  369. package/dist/types.backup/theme/ThemeProvider.d.ts +0 -13
  370. package/dist/types.backup/theme/ThemeScript.d.ts +0 -45
  371. package/dist/types.backup/theme/constants.d.ts +0 -39
  372. package/dist/types.backup/theme/index.d.ts +0 -29
  373. package/dist/types.backup/theme/theme-context.d.ts +0 -21
  374. package/dist/types.backup/theme/theme-script.d.ts +0 -26
  375. package/dist/types.backup/theme/types.d.ts +0 -162
  376. package/dist/types.backup/theme/use-theme.d.ts +0 -8
  377. package/dist/types.backup/types/boundaries.d.ts +0 -93
  378. package/dist/types.backup/types/cache-types.d.ts +0 -191
  379. package/dist/types.backup/types/error-types.d.ts +0 -114
  380. package/dist/types.backup/types/global-namespace.d.ts +0 -90
  381. package/dist/types.backup/types/handler-context.d.ts +0 -658
  382. package/dist/types.backup/types/index.d.ts +0 -11
  383. package/dist/types.backup/types/loader-types.d.ts +0 -182
  384. package/dist/types.backup/types/request-scope.d.ts +0 -93
  385. package/dist/types.backup/types/route-config.d.ts +0 -105
  386. package/dist/types.backup/types/route-entry.d.ts +0 -95
  387. package/dist/types.backup/types/segments.d.ts +0 -234
  388. package/dist/types.backup/types.d.ts +0 -1
  389. package/dist/types.backup/urls/include-helper.d.ts +0 -17
  390. package/dist/types.backup/urls/include-provider.d.ts +0 -27
  391. package/dist/types.backup/urls/index.d.ts +0 -6
  392. package/dist/types.backup/urls/path-helper-types.d.ts +0 -197
  393. package/dist/types.backup/urls/path-helper.d.ts +0 -12
  394. package/dist/types.backup/urls/pattern-types.d.ts +0 -166
  395. package/dist/types.backup/urls/response-types.d.ts +0 -67
  396. package/dist/types.backup/urls/type-extraction.d.ts +0 -157
  397. package/dist/types.backup/urls/urls-function.d.ts +0 -24
  398. package/dist/types.backup/urls.d.ts +0 -1
  399. package/dist/types.backup/use-loader.d.ts +0 -150
  400. package/dist/types.backup/vercel/index.d.ts +0 -10
  401. package/dist/types.backup/vercel/tracing.d.ts +0 -70
  402. package/dist/types.backup/vite/debug.d.ts +0 -80
  403. package/dist/types.backup/vite/discovery/bundle-postprocess.d.ts +0 -12
  404. package/dist/types.backup/vite/discovery/dev-prerender-cache.d.ts +0 -65
  405. package/dist/types.backup/vite/discovery/discover-routers.d.ts +0 -17
  406. package/dist/types.backup/vite/discovery/discovery-errors.d.ts +0 -113
  407. package/dist/types.backup/vite/discovery/gate-state.d.ts +0 -79
  408. package/dist/types.backup/vite/discovery/prerender-collection.d.ts +0 -24
  409. package/dist/types.backup/vite/discovery/route-types-writer.d.ts +0 -32
  410. package/dist/types.backup/vite/discovery/self-gen-tracking.d.ts +0 -22
  411. package/dist/types.backup/vite/discovery/shell-prerender-phase.d.ts +0 -40
  412. package/dist/types.backup/vite/discovery/state.d.ts +0 -162
  413. package/dist/types.backup/vite/discovery/virtual-module-codegen.d.ts +0 -15
  414. package/dist/types.backup/vite/index.d.ts +0 -11
  415. package/dist/types.backup/vite/inject-client-debug.d.ts +0 -56
  416. package/dist/types.backup/vite/plugin-types.d.ts +0 -298
  417. package/dist/types.backup/vite/plugins/cjs-to-esm.d.ts +0 -6
  418. package/dist/types.backup/vite/plugins/client-ref-dedup.d.ts +0 -40
  419. package/dist/types.backup/vite/plugins/client-ref-hashing.d.ts +0 -35
  420. package/dist/types.backup/vite/plugins/cloudflare-protocol-stub.d.ts +0 -64
  421. package/dist/types.backup/vite/plugins/expose-action-id.d.ts +0 -18
  422. package/dist/types.backup/vite/plugins/expose-id-utils.d.ts +0 -37
  423. package/dist/types.backup/vite/plugins/expose-ids/export-analysis.d.ts +0 -19
  424. package/dist/types.backup/vite/plugins/expose-ids/handler-transform.d.ts +0 -10
  425. package/dist/types.backup/vite/plugins/expose-ids/loader-transform.d.ts +0 -8
  426. package/dist/types.backup/vite/plugins/expose-ids/router-transform.d.ts +0 -13
  427. package/dist/types.backup/vite/plugins/expose-ids/types.d.ts +0 -29
  428. package/dist/types.backup/vite/plugins/expose-internal-ids.d.ts +0 -6
  429. package/dist/types.backup/vite/plugins/performance-tracks.d.ts +0 -25
  430. package/dist/types.backup/vite/plugins/refresh-cmd.d.ts +0 -20
  431. package/dist/types.backup/vite/plugins/use-cache-transform.d.ts +0 -20
  432. package/dist/types.backup/vite/plugins/vercel-output.d.ts +0 -85
  433. package/dist/types.backup/vite/plugins/version-injector.d.ts +0 -21
  434. package/dist/types.backup/vite/plugins/version-plugin.d.ts +0 -19
  435. package/dist/types.backup/vite/plugins/virtual-entries.d.ts +0 -36
  436. package/dist/types.backup/vite/plugins/virtual-stub-plugin.d.ts +0 -7
  437. package/dist/types.backup/vite/rango.d.ts +0 -29
  438. package/dist/types.backup/vite/router-discovery.d.ts +0 -23
  439. package/dist/types.backup/vite/utils/ast-handler-extract.d.ts +0 -64
  440. package/dist/types.backup/vite/utils/banner.d.ts +0 -2
  441. package/dist/types.backup/vite/utils/bundle-analysis.d.ts +0 -28
  442. package/dist/types.backup/vite/utils/client-chunks.d.ts +0 -55
  443. package/dist/types.backup/vite/utils/directive-prologue.d.ts +0 -16
  444. package/dist/types.backup/vite/utils/forward-user-plugins.d.ts +0 -37
  445. package/dist/types.backup/vite/utils/manifest-utils.d.ts +0 -7
  446. package/dist/types.backup/vite/utils/package-resolution.d.ts +0 -6
  447. package/dist/types.backup/vite/utils/prerender-utils.d.ts +0 -32
  448. package/dist/types.backup/vite/utils/shared-utils.d.ts +0 -55
@@ -307,6 +307,53 @@ intercept("@modal", ".product", async (ctx) => {
307
307
  }),
308
308
  ```
309
309
 
310
+ ## Embedding server actions in cached components
311
+
312
+ A cached function may return a component that creates an inline `"use server"`
313
+ action. The canonical case is a cached list whose items each carry an action --
314
+ e.g. a cached article list where every row has a like button:
315
+
316
+ ```typescript
317
+ export async function ArticleList() {
318
+ "use cache: articles";
319
+ const articles = await db.query("SELECT id, title FROM articles");
320
+ return (
321
+ <ul>
322
+ {articles.map((a) => {
323
+ const articleId = a.id; // captured -> frozen with the cache entry
324
+ async function like() {
325
+ "use server";
326
+ // runs live on click, in the CURRENT request scope
327
+ const user = cookies().get("session")?.value;
328
+ if (user) await db.like(articleId, user);
329
+ }
330
+ return <LikeButton key={a.id} title={a.title} action={like} />;
331
+ })}
332
+ </ul>
333
+ );
334
+ }
335
+ ```
336
+
337
+ Three behaviors, locked by `use-cache-inline-action.test.ts` (dev + prod):
338
+
339
+ | What | Behavior | Why |
340
+ | ----------------------------------------------- | ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
341
+ | Values the action **closes over** (`articleId`) | **Frozen** at cache-write | The closure compiles to encrypted bound args, snapshotted when the entry is written and replayed verbatim on a hit. Correct for stable identities; wrong for volatile values. |
342
+ | The action **body** | **Runs live** every call | Once invoked it is an ordinary server function: fresh computation and live request context. `cookies()` / `headers()` work here (the body executes in the live request). |
343
+ | Invocability on a **cache hit** | **Works** | The action survives serialize -> cache -> deserialize and stays callable. |
344
+
345
+ Rule of thumb: **capture stable identities, read volatile/request-scoped values
346
+ live in the body.** Do not close over a per-request token, the current user, or
347
+ the current time and expect freshness -- those are frozen at cache-write. The
348
+ `cookies()`/`headers()` read guard applies to the cached function body, NOT to an
349
+ inline action's body.
350
+
351
+ Deploy note: captured values are encrypted with a per-build key by default. If
352
+ cache entries can outlive a deploy (a persistent store such as the Cloudflare
353
+ cache store), set `RANGO_ENCRYPTION_KEY` (base64-encoded 32 bytes) in the build
354
+ environment so actions embedded in entries written by a previous deploy still
355
+ decrypt; without it they fail until the entry expires or revalidates.
356
+
310
357
  ## Vite Transform
311
358
 
312
359
  The `rango:use-cache` Vite plugin detects the directive and wraps exports with
@@ -234,6 +234,12 @@ export interface EventController {
234
234
  listener: ActionStateListener,
235
235
  ): () => void;
236
236
  subscribeToHandles(listener: HandleListener): () => void;
237
+ /**
238
+ * Deliver queued location, params, navigation, and handle notifications now.
239
+ * NavigationProvider calls this inside the payload update so React assigns
240
+ * every route-state hook update to the same normal or transition lane.
241
+ */
242
+ flushRouteState(): void;
237
243
 
238
244
  // Handle operations
239
245
  setHandleData(
@@ -367,19 +373,32 @@ function matchesActionId(
367
373
  return entryActionId.endsWith(`#${subscriptionId}`);
368
374
  }
369
375
 
370
- // Batch rapid notifications into one microtask to prevent render storms
371
- function makeDebouncedNotifier(listeners: Set<() => void>): () => void {
376
+ interface DebouncedNotifier {
377
+ schedule(): void;
378
+ flush(): void;
379
+ }
380
+
381
+ // Batch rapid notifications into one task to prevent render storms. The
382
+ // explicit flush lets a React update owner preserve its scheduling lane.
383
+ function makeDebouncedNotifier(listeners: Set<() => void>): DebouncedNotifier {
372
384
  let timeout: ReturnType<typeof setTimeout> | null = null;
373
- return () => {
374
- if (timeout !== null) clearTimeout(timeout);
375
- timeout = setTimeout(() => {
376
- timeout = null;
377
- notifyListeners(
378
- [...listeners],
379
- (listener) => listener(),
380
- (listener) => listeners.has(listener),
381
- );
382
- }, 0);
385
+ const flush = () => {
386
+ if (timeout === null) return;
387
+ clearTimeout(timeout);
388
+ timeout = null;
389
+ notifyListeners(
390
+ [...listeners],
391
+ (listener) => listener(),
392
+ (listener) => listeners.has(listener),
393
+ );
394
+ };
395
+
396
+ return {
397
+ schedule() {
398
+ if (timeout !== null) clearTimeout(timeout);
399
+ timeout = setTimeout(flush, 0);
400
+ },
401
+ flush,
383
402
  };
384
403
  }
385
404
 
@@ -459,7 +478,7 @@ export function createEventController(
459
478
 
460
479
  function notify(): void {
461
480
  cachedDerivedState = null;
462
- notifyStateListeners();
481
+ notifyStateListeners.schedule();
463
482
  }
464
483
 
465
484
  const actionNotifyTimeouts = new Map<string, ReturnType<typeof setTimeout>>();
@@ -517,6 +536,11 @@ export function createEventController(
517
536
 
518
537
  const notifyHandles = makeDebouncedNotifier(handleListeners);
519
538
 
539
+ function flushRouteState(): void {
540
+ notifyStateListeners.flush();
541
+ notifyHandles.flush();
542
+ }
543
+
520
544
  function getState(): DerivedNavigationState {
521
545
  if (cachedDerivedState) return cachedDerivedState;
522
546
 
@@ -1001,7 +1025,7 @@ export function createEventController(
1001
1025
  handleSegmentOrder = newSegmentOrder;
1002
1026
  routeSegmentIds = newRouteSegmentIds;
1003
1027
 
1004
- notifyHandles();
1028
+ notifyHandles.schedule();
1005
1029
  }
1006
1030
 
1007
1031
  function getHandleState(): HandleState {
@@ -1021,7 +1045,7 @@ export function createEventController(
1021
1045
  return;
1022
1046
  }
1023
1047
  routeSegmentIds = next;
1024
- notifyHandles();
1048
+ notifyHandles.schedule();
1025
1049
  }
1026
1050
 
1027
1051
  // ========================================================================
@@ -1102,6 +1126,7 @@ export function createEventController(
1102
1126
  subscribe,
1103
1127
  subscribeToAction,
1104
1128
  subscribeToHandles,
1129
+ flushRouteState,
1105
1130
 
1106
1131
  // Direct access
1107
1132
  getCurrentNavigation: () => currentNavigation,
@@ -421,10 +421,8 @@ export function createPartialUpdater(
421
421
  // forceAwait unwraps the ROUTER loader promises during render so they
422
422
  // land without a loading()/fallback frame. A fully-prefetched nav has
423
423
  // its router data already resolved (the prefetch stream drained), so
424
- // awaiting it here is free and lets us commit NORMALLY (not in a
425
- // transition) below a normal commit still shows fallbacks for any
426
- // CLIENT component that suspends on mount, which a transition would
427
- // wrongly suppress by holding the old UI until that suspense settles.
424
+ // awaiting it here is free; the commit below then runs in a transition
425
+ // (fullyPrefetched branch) so nothing router-owned can flash.
428
426
  forceAwait: mode.type === "stale-revalidation" || fullyPrefetched,
429
427
  interceptSegments:
430
428
  reconciled.interceptSegments.length > 0
@@ -549,14 +547,31 @@ export function createPartialUpdater(
549
547
  scroll: scrollPayload,
550
548
  });
551
549
  });
550
+ } else if (fullyPrefetched) {
551
+ // Fully-prefetched nav: the payload is fully resolved (forceAwait
552
+ // above), so commit inside a transition to hold the current UI across
553
+ // the synchronous resolution — no fallback flash. No addTransitionType:
554
+ // this is the React content-hold, not a view transition. Deliberate
555
+ // trade-off (#622 introduced, #624 reverted, then reinstated): a client
556
+ // component that suspends during its FIRST render (use() of a promise
557
+ // created at mount — see ClientMountSuspense in the e2e test-app) under
558
+ // an ALREADY-REVEALED boundary holds the old content until it resolves
559
+ // instead of revealing that boundary's fallback; its render happens
560
+ // pre-commit inside the transition, so userland effects cannot run
561
+ // first. Boundaries newly mounted by this nav still reveal their
562
+ // fallbacks (React shows new boundaries inside transitions).
563
+ startTransition(() => {
564
+ onUpdate({
565
+ root: newTree,
566
+ metadata: payload.metadata!,
567
+ scroll: scrollPayload,
568
+ });
569
+ });
552
570
  } else {
553
- // Normal commit for ALL navs here, never a transition (see the
554
- // forceAwait comment above for why prefetched router data lands
555
- // without fallback frames). A transition would hold the OLD UI until
556
- // every suspense in the new tree settles — including a client
557
- // component that only starts fetching post-mount, retaining the
558
- // previous page indefinitely. Explicit transition() routes keep the
559
- // content-hold via the hasTransition branch above (the opt-in).
571
+ // Cold/partially-prefetched nav: normal commit so fallbacks stream
572
+ // like a first load and the click has visible feedback. Explicit
573
+ // transition() routes keep the content-hold via the hasTransition
574
+ // branch above (the opt-in).
560
575
  onUpdate({
561
576
  root: newTree,
562
577
  metadata: payload.metadata!,
@@ -456,6 +456,13 @@ export function NavigationProvider({
456
456
  cached === undefined ? update.metadata.resolvedIds : undefined,
457
457
  );
458
458
  }
459
+
460
+ // tx.commit() and the metadata updates above mutate the controller
461
+ // synchronously, but its ordinary notifications are task-debounced. Flush
462
+ // them here so hook setState calls inherit this payload update's lane. In
463
+ // a transition that suspends, the source tree therefore keeps its source
464
+ // pathname/params until the destination payload commits with them.
465
+ eventController.flushRouteState();
459
466
  });
460
467
 
461
468
  return unsubscribe;
@@ -205,8 +205,63 @@ interface CacheEnvelope {
205
205
  * singleton), and cleared for a key as soon as the leader settles: a rejected
206
206
  * leader (function threw, or the result was not serializable) propagates to
207
207
  * current waiters, which then retry fresh.
208
+ *
209
+ * A leader that NEVER settles must not hang followers (scar tissue, autobarn
210
+ * pilot outage): a background shell capture's render became leader, awaited a
211
+ * tarpitting upstream fetch, and workerd killed the capture's waitUntil context
212
+ * — orphaning the leader promise as permanently pending, its map entry never
213
+ * cleared. Every later document render calling the same cached function (an
214
+ * isolate-global key for plain-args calls) awaited it forever before first
215
+ * byte: isolate-wide TTFB-0 until redeploy. Followers therefore trust an entry
216
+ * only for {@link IN_FLIGHT_LEADER_MAX_WAIT_MS} from registration; past it
217
+ * they evict the entry and run fresh — bounded duplicate upstream work instead
218
+ * of an unbounded hang. Eviction is age-based off `registeredAt`, so it also
219
+ * heals entries stranded by a killed context (no timer in that context needs
220
+ * to survive).
221
+ */
222
+ interface InFlightExecution {
223
+ promise: Promise<CacheEnvelope>;
224
+ registeredAt: number;
225
+ }
226
+
227
+ const inFlightExecutions = new Map<string, InFlightExecution>();
228
+
229
+ /**
230
+ * How long a follower trusts an in-flight leader before evicting it and
231
+ * running fresh. High enough that a slow-but-healthy upstream never triggers
232
+ * duplicate work (a legitimate cached call taking >15s is already pathological);
233
+ * low enough to bound the blast radius of a wedged leader to seconds, not the
234
+ * isolate lifetime. Aligned with SHELL_CAPTURE_MAX_WAIT_MS.
235
+ */
236
+ const IN_FLIGHT_LEADER_MAX_WAIT_MS = 15_000;
237
+
238
+ /** Distinguishes a leader timeout from a leader rejection in raceLeader. */
239
+ const LEADER_TIMED_OUT = Symbol("leader-timed-out");
240
+
241
+ /**
242
+ * Await a leader's envelope for at most `remainingMs`. Resolves with the
243
+ * envelope, `undefined` on leader rejection (the leader already cleared its
244
+ * entry — caller falls through to a fresh run), or {@link LEADER_TIMED_OUT}
245
+ * when the window expires first (caller must evict the entry itself).
208
246
  */
209
- const inFlightExecutions = new Map<string, Promise<CacheEnvelope>>();
247
+ function raceLeader(
248
+ promise: Promise<CacheEnvelope>,
249
+ remainingMs: number,
250
+ ): Promise<CacheEnvelope | undefined | typeof LEADER_TIMED_OUT> {
251
+ return new Promise((resolve) => {
252
+ const timer = setTimeout(() => resolve(LEADER_TIMED_OUT), remainingMs);
253
+ promise.then(
254
+ (envelope) => {
255
+ clearTimeout(timer);
256
+ resolve(envelope);
257
+ },
258
+ () => {
259
+ clearTimeout(timer);
260
+ resolve(undefined);
261
+ },
262
+ );
263
+ });
264
+ }
210
265
 
211
266
  // ============================================================================
212
267
  // Core: registerCachedFunction
@@ -541,20 +596,34 @@ export function registerCachedFunction<T extends (...args: any[]) => any>(
541
596
  // requests. The store write stays exactly once (the leader's).
542
597
  const existing = inFlightExecutions.get(cacheKey);
543
598
  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) {
599
+ const remainingMs =
600
+ IN_FLIGHT_LEADER_MAX_WAIT_MS - (Date.now() - existing.registeredAt);
601
+ const raced =
602
+ remainingMs <= 0
603
+ ? LEADER_TIMED_OUT
604
+ : await raceLeader(existing.promise, remainingMs);
605
+ if (raced === LEADER_TIMED_OUT) {
606
+ // Wedged (or context-orphaned) leader: evict so this call and every
607
+ // later one run fresh. Guard the delete so a newer leader's entry is
608
+ // never removed; the stale leader's own clearSelf is identity-guarded
609
+ // the same way, so it can't evict our replacement if it settles late.
610
+ if (inFlightExecutions.get(cacheKey) === existing) {
611
+ inFlightExecutions.delete(cacheKey);
612
+ }
613
+ reportCacheError(
614
+ new Error(
615
+ `in-flight leader did not settle within ${IN_FLIGHT_LEADER_MAX_WAIT_MS}ms; evicted — executing fresh`,
616
+ ),
617
+ "cache-read",
618
+ `[use cache] "${id}" inflight-timeout`,
619
+ );
620
+ // Fall through to a fresh execution below (this call becomes leader).
621
+ } else if (raced) {
553
622
  try {
554
623
  return await serveCached({
555
- value: envelope.serialized,
556
- handles: envelope.handles,
557
- tags: envelope.tags,
624
+ value: raced.serialized,
625
+ handles: raced.handles,
626
+ tags: raced.tags,
558
627
  shouldRevalidate: false,
559
628
  });
560
629
  } catch (error) {
@@ -566,6 +635,8 @@ export function registerCachedFunction<T extends (...args: any[]) => any>(
566
635
  // Fall through to a fresh execution below.
567
636
  }
568
637
  }
638
+ // raced === undefined: leader rejected; its map entry is already
639
+ // cleared, so fall through to a fresh run.
569
640
  }
570
641
 
571
642
  // This call becomes the leader. Register a deferred envelope so concurrent
@@ -582,9 +653,12 @@ export function registerCachedFunction<T extends (...args: any[]) => any>(
582
653
  // Followers attach their own catch; guard the map's own reference so a
583
654
  // rejected envelope with no waiter is not an unhandled rejection.
584
655
  envelopePromise.catch(() => {});
585
- inFlightExecutions.set(cacheKey, envelopePromise);
656
+ inFlightExecutions.set(cacheKey, {
657
+ promise: envelopePromise,
658
+ registeredAt: Date.now(),
659
+ });
586
660
  const clearSelf = (): void => {
587
- if (inFlightExecutions.get(cacheKey) === envelopePromise) {
661
+ if (inFlightExecutions.get(cacheKey)?.promise === envelopePromise) {
588
662
  inFlightExecutions.delete(cacheKey);
589
663
  }
590
664
  };
@@ -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