@rangojs/router 0.0.0-experimental.31 → 0.0.0-experimental.3232cd17

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 (376) hide show
  1. package/AGENTS.md +4 -0
  2. package/README.md +198 -44
  3. package/dist/bin/rango.js +287 -105
  4. package/dist/testing/vitest.js +82 -0
  5. package/dist/vite/index.js +3248 -1117
  6. package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
  7. package/package.json +73 -21
  8. package/skills/api-client/SKILL.md +211 -0
  9. package/skills/breadcrumbs/SKILL.md +107 -1
  10. package/skills/bundle-analysis/SKILL.md +159 -0
  11. package/skills/cache-guide/SKILL.md +245 -21
  12. package/skills/caching/SKILL.md +302 -6
  13. package/skills/composability/SKILL.md +27 -2
  14. package/skills/css/SKILL.md +76 -0
  15. package/skills/document-cache/SKILL.md +78 -55
  16. package/skills/handler-use/SKILL.md +364 -0
  17. package/skills/hooks/SKILL.md +270 -30
  18. package/skills/host-router/SKILL.md +82 -22
  19. package/skills/i18n/SKILL.md +276 -0
  20. package/skills/intercept/SKILL.md +49 -5
  21. package/skills/layout/SKILL.md +35 -9
  22. package/skills/links/SKILL.md +249 -17
  23. package/skills/loader/SKILL.md +294 -30
  24. package/skills/middleware/SKILL.md +52 -13
  25. package/skills/migrate-nextjs/SKILL.md +584 -0
  26. package/skills/migrate-react-router/SKILL.md +769 -0
  27. package/skills/mime-routes/SKILL.md +27 -0
  28. package/skills/observability/SKILL.md +137 -0
  29. package/skills/parallel/SKILL.md +203 -7
  30. package/skills/prerender/SKILL.md +123 -100
  31. package/skills/rango/SKILL.md +250 -22
  32. package/skills/react-compiler/SKILL.md +168 -0
  33. package/skills/response-routes/SKILL.md +122 -47
  34. package/skills/route/SKILL.md +97 -5
  35. package/skills/router-setup/SKILL.md +90 -5
  36. package/skills/server-actions/SKILL.md +775 -0
  37. package/skills/streams-and-websockets/SKILL.md +283 -0
  38. package/skills/tailwind/SKILL.md +27 -3
  39. package/skills/testing/SKILL.md +129 -0
  40. package/skills/testing/bindings.md +89 -0
  41. package/skills/testing/cache-prerender.md +124 -0
  42. package/skills/testing/client-components.md +122 -0
  43. package/skills/testing/e2e-parity.md +125 -0
  44. package/skills/testing/flight.md +92 -0
  45. package/skills/testing/handles.md +129 -0
  46. package/skills/testing/loader.md +128 -0
  47. package/skills/testing/middleware.md +99 -0
  48. package/skills/testing/render-handler.md +121 -0
  49. package/skills/testing/response-routes.md +95 -0
  50. package/skills/testing/reverse-and-types.md +84 -0
  51. package/skills/testing/server-actions.md +107 -0
  52. package/skills/testing/server-tree.md +128 -0
  53. package/skills/testing/setup.md +120 -0
  54. package/skills/typesafety/SKILL.md +329 -27
  55. package/skills/use-cache/SKILL.md +36 -5
  56. package/skills/view-transitions/SKILL.md +294 -0
  57. package/src/__augment-tests__/augment.ts +81 -0
  58. package/src/__augment-tests__/augmented.check.ts +116 -0
  59. package/src/__internal.ts +67 -40
  60. package/src/browser/action-coordinator.ts +53 -36
  61. package/src/browser/action-fence.ts +47 -0
  62. package/src/browser/app-shell.ts +39 -0
  63. package/src/browser/app-version.ts +14 -0
  64. package/src/browser/cookie-name.ts +140 -0
  65. package/src/browser/event-controller.ts +86 -147
  66. package/src/browser/history-state.ts +21 -0
  67. package/src/browser/index.ts +3 -3
  68. package/src/browser/invalidate-client-cache.ts +52 -0
  69. package/src/browser/link-interceptor.ts +4 -0
  70. package/src/browser/navigation-bridge.ts +148 -19
  71. package/src/browser/navigation-client.ts +187 -67
  72. package/src/browser/navigation-store-handle.ts +38 -0
  73. package/src/browser/navigation-store.ts +76 -67
  74. package/src/browser/navigation-transaction.ts +18 -66
  75. package/src/browser/partial-update.ts +123 -94
  76. package/src/browser/prefetch/cache.ts +214 -36
  77. package/src/browser/prefetch/fetch.ts +260 -38
  78. package/src/browser/prefetch/policy.ts +6 -0
  79. package/src/browser/prefetch/queue.ts +126 -20
  80. package/src/browser/prefetch/resource-ready.ts +77 -0
  81. package/src/browser/rango-state.ts +158 -76
  82. package/src/browser/react/Link.tsx +93 -11
  83. package/src/browser/react/NavigationProvider.tsx +115 -34
  84. package/src/browser/react/ScrollRestoration.tsx +10 -6
  85. package/src/browser/react/context.ts +7 -2
  86. package/src/browser/react/filter-segment-order.ts +49 -7
  87. package/src/browser/react/index.ts +0 -48
  88. package/src/browser/react/location-state-shared.ts +166 -8
  89. package/src/browser/react/location-state.ts +39 -14
  90. package/src/browser/react/use-action.ts +6 -15
  91. package/src/browser/react/use-handle.ts +23 -69
  92. package/src/browser/react/use-link-status.ts +0 -4
  93. package/src/browser/react/use-navigation.ts +22 -5
  94. package/src/browser/react/use-params.ts +20 -10
  95. package/src/browser/react/use-reverse.ts +106 -0
  96. package/src/browser/react/use-router.ts +46 -11
  97. package/src/browser/react/use-search-params.ts +0 -5
  98. package/src/browser/react/use-segments.ts +11 -21
  99. package/src/browser/response-adapter.ts +52 -1
  100. package/src/browser/rsc-router.tsx +215 -76
  101. package/src/browser/scroll-restoration.ts +46 -39
  102. package/src/browser/segment-reconciler.ts +36 -9
  103. package/src/browser/segment-structure-assert.ts +2 -2
  104. package/src/browser/server-action-bridge.ts +176 -50
  105. package/src/browser/types.ts +95 -11
  106. package/src/browser/validate-redirect-origin.ts +43 -16
  107. package/src/build/collect-fallback-refs.ts +107 -0
  108. package/src/build/generate-manifest.ts +65 -40
  109. package/src/build/generate-route-types.ts +5 -0
  110. package/src/build/index.ts +8 -2
  111. package/src/build/prefix-tree-utils.ts +123 -0
  112. package/src/build/route-trie.ts +137 -32
  113. package/src/build/route-types/codegen.ts +4 -4
  114. package/src/build/route-types/include-resolution.ts +9 -2
  115. package/src/build/route-types/param-extraction.ts +6 -3
  116. package/src/build/route-types/per-module-writer.ts +7 -4
  117. package/src/build/route-types/router-processing.ts +278 -96
  118. package/src/build/route-types/scan-filter.ts +9 -2
  119. package/src/build/route-types/source-scan.ts +118 -0
  120. package/src/build/runtime-discovery.ts +9 -20
  121. package/src/cache/cache-error.ts +104 -0
  122. package/src/cache/cache-policy.ts +68 -28
  123. package/src/cache/cache-runtime.ts +149 -43
  124. package/src/cache/cache-scope.ts +148 -81
  125. package/src/cache/cache-tag.ts +98 -0
  126. package/src/cache/cf/cf-cache-store.ts +2550 -93
  127. package/src/cache/cf/index.ts +11 -17
  128. package/src/cache/document-cache.ts +78 -27
  129. package/src/cache/handle-snapshot.ts +63 -0
  130. package/src/cache/index.ts +23 -20
  131. package/src/cache/memory-segment-store.ts +136 -37
  132. package/src/cache/profile-registry.ts +6 -30
  133. package/src/cache/read-through-swr.ts +41 -11
  134. package/src/cache/segment-codec.ts +0 -16
  135. package/src/cache/tag-invalidation.ts +230 -0
  136. package/src/cache/taint.ts +55 -0
  137. package/src/cache/types.ts +33 -100
  138. package/src/cache/vercel/index.ts +11 -0
  139. package/src/cache/vercel/vercel-cache-store.ts +799 -0
  140. package/src/client.rsc.tsx +6 -21
  141. package/src/client.tsx +108 -290
  142. package/src/component-utils.ts +19 -0
  143. package/src/context-var.ts +84 -2
  144. package/src/debug.ts +2 -2
  145. package/src/decode-loader-results.ts +36 -0
  146. package/src/defer.ts +196 -0
  147. package/src/deps/ssr.ts +0 -1
  148. package/src/errors.ts +30 -4
  149. package/src/handle.ts +70 -22
  150. package/src/handles/MetaTags.tsx +0 -14
  151. package/src/handles/breadcrumbs.ts +16 -5
  152. package/src/handles/meta.ts +0 -39
  153. package/src/host/cookie-handler.ts +0 -36
  154. package/src/host/errors.ts +0 -24
  155. package/src/host/index.ts +8 -2
  156. package/src/host/pattern-matcher.ts +7 -50
  157. package/src/host/router.ts +107 -99
  158. package/src/host/testing.ts +40 -27
  159. package/src/host/types.ts +37 -4
  160. package/src/host/utils.ts +1 -1
  161. package/src/href-client.ts +137 -22
  162. package/src/index.rsc.ts +52 -26
  163. package/src/index.ts +100 -38
  164. package/src/internal-debug.ts +2 -4
  165. package/src/loader-store.ts +500 -0
  166. package/src/loader.rsc.ts +20 -13
  167. package/src/loader.ts +12 -11
  168. package/src/missing-id-error.ts +68 -0
  169. package/src/network-error-thrower.tsx +1 -6
  170. package/src/outlet-context.ts +1 -1
  171. package/src/outlet-provider.tsx +1 -5
  172. package/src/prerender/param-hash.ts +10 -11
  173. package/src/prerender/store.ts +37 -41
  174. package/src/prerender.ts +198 -82
  175. package/src/redirect-origin.ts +100 -0
  176. package/src/response-utils.ts +37 -0
  177. package/src/reverse.ts +65 -15
  178. package/src/root-error-boundary.tsx +1 -19
  179. package/src/route-content-wrapper.tsx +7 -72
  180. package/src/route-definition/dsl-helpers.ts +437 -274
  181. package/src/route-definition/helper-factories.ts +29 -139
  182. package/src/route-definition/helpers-types.ts +113 -37
  183. package/src/route-definition/index.ts +3 -0
  184. package/src/route-definition/redirect.ts +52 -10
  185. package/src/route-definition/resolve-handler-use.ts +161 -0
  186. package/src/route-definition/use-item-types.ts +32 -0
  187. package/src/route-map-builder.ts +7 -17
  188. package/src/route-types.ts +37 -41
  189. package/src/router/basename.ts +14 -0
  190. package/src/router/content-negotiation.ts +108 -9
  191. package/src/router/error-handling.ts +13 -17
  192. package/src/router/find-match.ts +45 -22
  193. package/src/router/handler-context.ts +83 -41
  194. package/src/router/intercept-resolution.ts +25 -23
  195. package/src/router/lazy-includes.ts +19 -53
  196. package/src/router/loader-resolution.ts +213 -30
  197. package/src/router/logging.ts +5 -8
  198. package/src/router/manifest.ts +49 -45
  199. package/src/router/match-api.ts +121 -205
  200. package/src/router/match-context.ts +0 -22
  201. package/src/router/match-handlers.ts +58 -58
  202. package/src/router/match-middleware/background-revalidation.ts +27 -6
  203. package/src/router/match-middleware/cache-lookup.ts +205 -249
  204. package/src/router/match-middleware/cache-store.ts +45 -32
  205. package/src/router/match-middleware/intercept-resolution.ts +8 -28
  206. package/src/router/match-middleware/segment-resolution.ts +52 -18
  207. package/src/router/match-pipelines.ts +1 -42
  208. package/src/router/match-result.ts +104 -40
  209. package/src/router/metrics.ts +5 -34
  210. package/src/router/middleware-types.ts +13 -142
  211. package/src/router/middleware.ts +173 -143
  212. package/src/router/navigation-snapshot.ts +131 -0
  213. package/src/router/params-util.ts +23 -0
  214. package/src/router/pattern-matching.ts +109 -63
  215. package/src/router/prerender-match.ts +192 -54
  216. package/src/router/preview-match.ts +32 -102
  217. package/src/router/request-classification.ts +276 -0
  218. package/src/router/revalidation.ts +63 -55
  219. package/src/router/route-snapshot.ts +244 -0
  220. package/src/router/router-context.ts +6 -28
  221. package/src/router/router-interfaces.ts +100 -35
  222. package/src/router/router-options.ts +91 -11
  223. package/src/router/router-registry.ts +2 -5
  224. package/src/router/segment-resolution/fresh.ts +242 -75
  225. package/src/router/segment-resolution/helpers.ts +64 -25
  226. package/src/router/segment-resolution/loader-cache.ts +41 -37
  227. package/src/router/segment-resolution/revalidation.ts +456 -372
  228. package/src/router/segment-resolution/static-store.ts +19 -5
  229. package/src/router/segment-resolution/streamed-handler-telemetry.ts +52 -0
  230. package/src/router/segment-resolution/view-transition-default.ts +36 -0
  231. package/src/router/segment-resolution.ts +4 -1
  232. package/src/router/segment-wrappers.ts +2 -3
  233. package/src/router/state-cookie-name.ts +33 -0
  234. package/src/router/substitute-pattern-params.ts +56 -0
  235. package/src/router/telemetry-otel.ts +0 -20
  236. package/src/router/telemetry.ts +96 -19
  237. package/src/router/timeout.ts +0 -20
  238. package/src/router/trie-matching.ts +91 -46
  239. package/src/router/types.ts +10 -63
  240. package/src/router/url-params.ts +44 -0
  241. package/src/router.ts +134 -43
  242. package/src/rsc/handler-context.ts +3 -2
  243. package/src/rsc/handler.ts +492 -383
  244. package/src/rsc/helpers.ts +162 -46
  245. package/src/rsc/index.ts +1 -1
  246. package/src/rsc/json-route-result.ts +38 -0
  247. package/src/rsc/loader-fetch.ts +23 -3
  248. package/src/rsc/manifest-init.ts +33 -42
  249. package/src/rsc/origin-guard.ts +39 -25
  250. package/src/rsc/progressive-enhancement.ts +30 -3
  251. package/src/rsc/redirect-guard.ts +99 -0
  252. package/src/rsc/response-error.ts +79 -12
  253. package/src/rsc/response-route-handler.ts +90 -63
  254. package/src/rsc/rsc-rendering.ts +56 -54
  255. package/src/rsc/runtime-warnings.ts +23 -10
  256. package/src/rsc/server-action.ts +74 -67
  257. package/src/rsc/ssr-setup.ts +18 -2
  258. package/src/rsc/types.ts +25 -6
  259. package/src/runtime-env.ts +18 -0
  260. package/src/search-params.ts +4 -20
  261. package/src/segment-content-promise.ts +67 -0
  262. package/src/segment-loader-promise.ts +134 -0
  263. package/src/segment-system.tsx +272 -129
  264. package/src/serialize.ts +243 -0
  265. package/src/server/context.ts +309 -61
  266. package/src/server/cookie-store.ts +80 -5
  267. package/src/server/handle-store.ts +26 -24
  268. package/src/server/loader-registry.ts +10 -28
  269. package/src/server/request-context.ts +348 -128
  270. package/src/ssr/index.tsx +23 -15
  271. package/src/static-handler.ts +27 -18
  272. package/src/testing/cache-status.ts +162 -0
  273. package/src/testing/collect-handle.ts +40 -0
  274. package/src/testing/dispatch.ts +618 -0
  275. package/src/testing/dom.entry.ts +22 -0
  276. package/src/testing/e2e/fixture.ts +188 -0
  277. package/src/testing/e2e/index.ts +128 -0
  278. package/src/testing/e2e/matchers.ts +35 -0
  279. package/src/testing/e2e/page-helpers.ts +272 -0
  280. package/src/testing/e2e/parity.ts +387 -0
  281. package/src/testing/e2e/server.ts +195 -0
  282. package/src/testing/flight-matchers.ts +97 -0
  283. package/src/testing/flight-normalize.ts +11 -0
  284. package/src/testing/flight-runtime.d.ts +57 -0
  285. package/src/testing/flight-tree.ts +682 -0
  286. package/src/testing/flight.entry.ts +52 -0
  287. package/src/testing/flight.ts +232 -0
  288. package/src/testing/generated-routes.ts +183 -0
  289. package/src/testing/index.ts +99 -0
  290. package/src/testing/internal/context.ts +348 -0
  291. package/src/testing/internal/flight-client-globals.ts +30 -0
  292. package/src/testing/internal/seed-vars.ts +54 -0
  293. package/src/testing/render-handler.ts +330 -0
  294. package/src/testing/render-route.tsx +566 -0
  295. package/src/testing/run-loader.ts +378 -0
  296. package/src/testing/run-middleware.ts +205 -0
  297. package/src/testing/vitest-stubs/cloudflare-email.ts +9 -0
  298. package/src/testing/vitest-stubs/cloudflare-workers.ts +21 -0
  299. package/src/testing/vitest-stubs/plugin-rsc.ts +16 -0
  300. package/src/testing/vitest-stubs/version.ts +5 -0
  301. package/src/testing/vitest.ts +305 -0
  302. package/src/theme/ThemeProvider.tsx +0 -52
  303. package/src/theme/ThemeScript.tsx +0 -6
  304. package/src/theme/constants.ts +0 -12
  305. package/src/theme/index.ts +0 -7
  306. package/src/theme/theme-context.ts +1 -5
  307. package/src/theme/theme-script.ts +0 -14
  308. package/src/theme/use-theme.ts +0 -3
  309. package/src/types/boundaries.ts +0 -35
  310. package/src/types/cache-types.ts +17 -8
  311. package/src/types/error-types.ts +30 -90
  312. package/src/types/global-namespace.ts +54 -41
  313. package/src/types/handler-context.ts +233 -81
  314. package/src/types/index.ts +1 -10
  315. package/src/types/loader-types.ts +44 -15
  316. package/src/types/request-scope.ts +107 -0
  317. package/src/types/route-config.ts +6 -50
  318. package/src/types/route-entry.ts +19 -7
  319. package/src/types/segments.ts +37 -14
  320. package/src/urls/include-helper.ts +33 -70
  321. package/src/urls/index.ts +1 -11
  322. package/src/urls/path-helper-types.ts +58 -11
  323. package/src/urls/path-helper.ts +57 -111
  324. package/src/urls/pattern-types.ts +48 -19
  325. package/src/urls/response-types.ts +25 -22
  326. package/src/urls/type-extraction.ts +58 -139
  327. package/src/urls/urls-function.ts +1 -18
  328. package/src/use-loader.tsx +346 -89
  329. package/src/vite/debug.ts +185 -0
  330. package/src/vite/discovery/bundle-postprocess.ts +36 -38
  331. package/src/vite/discovery/discover-routers.ts +130 -85
  332. package/src/vite/discovery/discovery-errors.ts +194 -0
  333. package/src/vite/discovery/gate-state.ts +171 -0
  334. package/src/vite/discovery/prerender-collection.ts +192 -99
  335. package/src/vite/discovery/route-types-writer.ts +40 -84
  336. package/src/vite/discovery/self-gen-tracking.ts +27 -1
  337. package/src/vite/discovery/state.ts +51 -6
  338. package/src/vite/discovery/virtual-module-codegen.ts +14 -34
  339. package/src/vite/index.ts +8 -0
  340. package/src/vite/plugin-types.ts +187 -69
  341. package/src/vite/plugins/cjs-to-esm.ts +8 -18
  342. package/src/vite/plugins/client-ref-dedup.ts +16 -11
  343. package/src/vite/plugins/client-ref-hashing.ts +28 -15
  344. package/src/vite/plugins/cloudflare-protocol-loader-hook.d.mts +23 -0
  345. package/src/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
  346. package/src/vite/plugins/cloudflare-protocol-stub.ts +194 -0
  347. package/src/vite/plugins/expose-action-id.ts +49 -98
  348. package/src/vite/plugins/expose-id-utils.ts +11 -50
  349. package/src/vite/plugins/expose-ids/export-analysis.ts +76 -34
  350. package/src/vite/plugins/expose-ids/handler-transform.ts +10 -48
  351. package/src/vite/plugins/expose-ids/loader-transform.ts +3 -20
  352. package/src/vite/plugins/expose-ids/router-transform.ts +20 -16
  353. package/src/vite/plugins/expose-internal-ids.ts +554 -317
  354. package/src/vite/plugins/performance-tracks.ts +89 -0
  355. package/src/vite/plugins/refresh-cmd.ts +89 -27
  356. package/src/vite/plugins/use-cache-transform.ts +73 -83
  357. package/src/vite/plugins/vercel-output.ts +258 -0
  358. package/src/vite/plugins/version-injector.ts +21 -25
  359. package/src/vite/plugins/version-plugin.ts +41 -20
  360. package/src/vite/plugins/virtual-entries.ts +2 -17
  361. package/src/vite/rango.ts +257 -289
  362. package/src/vite/router-discovery.ts +930 -140
  363. package/src/vite/utils/ast-handler-extract.ts +15 -31
  364. package/src/vite/utils/banner.ts +4 -4
  365. package/src/vite/utils/bundle-analysis.ts +10 -15
  366. package/src/vite/utils/client-chunks.ts +184 -0
  367. package/src/vite/utils/forward-user-plugins.ts +171 -0
  368. package/src/vite/utils/manifest-utils.ts +4 -59
  369. package/src/vite/utils/package-resolution.ts +20 -52
  370. package/src/vite/utils/prerender-utils.ts +27 -29
  371. package/src/vite/utils/shared-utils.ts +92 -42
  372. package/src/browser/action-response-classifier.ts +0 -99
  373. package/src/browser/react/use-client-cache.ts +0 -58
  374. package/src/browser/shallow.ts +0 -40
  375. package/src/handles/index.ts +0 -7
  376. package/src/router/middleware-cookies.ts +0 -55
@@ -1,25 +1,19 @@
1
- /**
2
- * Cloudflare Cache Store Exports
3
- *
4
- * Main export:
5
- * - CFCacheStore - Production cache store using Cloudflare's Cache API
6
- *
7
- * Header constants (for inspection/debugging):
8
- * - CACHE_STALE_AT_HEADER - Header containing staleness timestamp
9
- * - CACHE_STATUS_HEADER - Header containing HIT/REVALIDATING status
10
- */
11
-
12
- // Public API
13
- export { CFCacheStore, type CFCacheStoreOptions } from "./cf-cache-store.js";
1
+ export {
2
+ CFCacheStore,
3
+ type CFCacheStoreOptions,
4
+ type CFCacheDebug,
5
+ type CFCacheReadDebugEvent,
6
+ type KVNamespace,
7
+ } from "./cf-cache-store.js";
14
8
 
15
- // Header constants for debugging and inspection
16
9
  export {
17
10
  CACHE_STALE_AT_HEADER,
18
11
  CACHE_STATUS_HEADER,
12
+ CACHE_REVALIDATING_AT_HEADER,
19
13
  } from "./cf-cache-store.js";
20
14
 
21
- // Internal exports (re-exported for backwards compatibility, marked @internal in source)
22
15
  export {
23
- type CacheStatus,
24
- MAX_REVALIDATION_INTERVAL,
16
+ EDGE_LOOKUP_TIMEOUT_MS,
17
+ EDGE_READ_TIMEOUT_MS,
18
+ KV_READ_TIMEOUT_MS,
25
19
  } from "./cf-cache-store.js";
@@ -12,22 +12,25 @@
12
12
  */
13
13
 
14
14
  import type { MiddlewareFn, MiddlewareContext } from "../router/middleware.js";
15
- import { getRequestContext } from "../server/request-context.js";
15
+ import { hasPerClientSignal } from "../browser/cookie-name.js";
16
+ import {
17
+ getRequestContext,
18
+ type RequestContext,
19
+ } from "../server/request-context.js";
20
+ import { mayNeedSSR } from "../rsc/ssr-setup.js";
16
21
  import { sortedSearchString } from "./cache-key-utils.js";
17
22
  import { runBackground } from "./background-task.js";
23
+ import { reportCacheError } from "./cache-error.js";
18
24
 
19
- // ============================================================================
20
- // Constants
21
- // ============================================================================
22
-
23
- /** Header indicating cache status for debugging */
24
25
  const CACHE_STATUS_HEADER = "x-document-cache-status";
25
26
 
26
- /**
27
- * Simple hash function for segment IDs.
28
- * Creates a short, deterministic hash to differentiate cache keys
29
- * based on which segments the client already has.
30
- */
27
+ function collectRequestTags(
28
+ requestCtx: RequestContext | undefined,
29
+ ): string[] | undefined {
30
+ const tags = requestCtx?._requestTags;
31
+ return tags && tags.size > 0 ? [...tags] : undefined;
32
+ }
33
+
31
34
  function hashSegmentIds(segmentIds: string): string {
32
35
  if (!segmentIds) return "";
33
36
 
@@ -36,12 +39,9 @@ function hashSegmentIds(segmentIds: string): string {
36
39
  const char = segmentIds.charCodeAt(i);
37
40
  hash = ((hash << 5) - hash + char) | 0;
38
41
  }
39
- // Convert to base36 for shorter string, take absolute value
40
42
  return Math.abs(hash).toString(36);
41
43
  }
42
44
 
43
- // ============================================================================
44
- // Cache Control Parsing
45
45
  // ============================================================================
46
46
 
47
47
  interface CacheDirectives {
@@ -55,6 +55,16 @@ interface CacheDirectives {
55
55
  function parseCacheControl(header: string | null): CacheDirectives | null {
56
56
  if (!header) return null;
57
57
 
58
+ // RFC 7234: in a SHARED cache, `private` and `no-store` forbid storage and
59
+ // MUST win over `s-maxage` even though `private, s-maxage` is contradictory.
60
+ // The document cache is a shared edge store, so refuse both regardless of any
61
+ // s-maxage / stale-while-revalidate also present. Match standalone directive
62
+ // tokens (start/end, whitespace, comma, semicolon, or `=` bounded), not a
63
+ // substring, so a value containing "private" cannot false-veto.
64
+ if (/(^|[\s,;])(private|no-store)(?=$|[\s,;=])/i.test(header)) {
65
+ return null;
66
+ }
67
+
58
68
  const directives: CacheDirectives = {};
59
69
 
60
70
  // Parse s-maxage
@@ -86,6 +96,16 @@ function shouldCacheResponse(response: Response): CacheDirectives | null {
86
96
  return null;
87
97
  }
88
98
 
99
+ // Never cache a per-client signal into a SHARED response store. A Set-Cookie
100
+ // (e.g. a rango state rotation from invalidateClientCache(), or any cookie a
101
+ // loader set) would be replayed to every client on a hit — pinning them to
102
+ // one value and even rolling a rotated client back to a prior one. The
103
+ // x-rango-keep-cache directive header is the mirror image: a replayed "keep"
104
+ // would suppress invalidation for every replayed client. Refuse both.
105
+ if (hasPerClientSignal(response.headers)) {
106
+ return null;
107
+ }
108
+
89
109
  const cacheControl = response.headers.get("Cache-Control");
90
110
  return parseCacheControl(cacheControl);
91
111
  }
@@ -204,18 +224,24 @@ export function createDocumentCacheMiddleware<TEnv = any>(
204
224
  ): Promise<Response> {
205
225
  const url = ctx.url;
206
226
 
227
+ // Use the original request URL for _rsc* param detection and cache key
228
+ // differentiation. ctx.url is stripped of _rsc* params by the middleware
229
+ // pipeline (stripInternalParams), so _rsc_partial, _rsc_segments, etc.
230
+ // are not visible on ctx.url in production.
231
+ const rawUrl = new URL(ctx.request.url);
232
+
207
233
  // Only cache GET requests — mutations and other methods must not be cached
208
234
  if (ctx.request.method !== "GET") {
209
235
  return next();
210
236
  }
211
237
 
212
238
  // Skip RSC action requests (mutations shouldn't be cached)
213
- if (url.searchParams.has("_rsc_action")) {
239
+ if (rawUrl.searchParams.has("_rsc_action")) {
214
240
  return next();
215
241
  }
216
242
 
217
243
  // Skip loader requests (have their own caching)
218
- if (url.searchParams.has("_rsc_loader")) {
244
+ if (rawUrl.searchParams.has("_rsc_loader")) {
219
245
  return next();
220
246
  }
221
247
 
@@ -241,9 +267,12 @@ export function createDocumentCacheMiddleware<TEnv = any>(
241
267
  return next();
242
268
  }
243
269
 
244
- // Determine request type for cache key differentiation
245
- const isPartial = url.searchParams.has("_rsc_partial");
246
- const typeLabel = isPartial ? "RSC" : "HTML";
270
+ // Determine request type for cache key differentiation.
271
+ // Uses rawUrl for _rsc* param checks and mayNeedSSR for Accept-based
272
+ // detection. Full-document RSC fetches must not share the HTML cache slot.
273
+ const isPartial = rawUrl.searchParams.has("_rsc_partial");
274
+ const isRscRequest = !mayNeedSSR(ctx.request, rawUrl);
275
+ const typeLabel = isRscRequest ? "RSC" : "HTML";
247
276
 
248
277
  // Track whether next() has been called so the catch block knows
249
278
  // whether it is safe to fall through to the handler.
@@ -254,10 +283,10 @@ export function createDocumentCacheMiddleware<TEnv = any>(
254
283
  // gracefully to the origin handler instead of rejecting the request.
255
284
  // This is a deliberate fail-open-to-origin policy: the fallback is
256
285
  // "serve uncached from origin", not "use a different cache key".
257
- const clientSegments = url.searchParams.get("_rsc_segments") || "";
286
+ const clientSegments = rawUrl.searchParams.get("_rsc_segments") || "";
258
287
  const segmentHash =
259
288
  isPartial && clientSegments ? `:${hashSegmentIds(clientSegments)}` : "";
260
- const typeSuffix = isPartial ? ":rsc" : ":html";
289
+ const typeSuffix = isRscRequest ? ":rsc" : ":html";
261
290
 
262
291
  let searchSuffix = "";
263
292
  if (!keyGenerator) {
@@ -293,17 +322,26 @@ export function createDocumentCacheMiddleware<TEnv = any>(
293
322
  const fresh = await next();
294
323
  const directives = shouldCacheResponse(fresh);
295
324
 
296
- if (directives) {
325
+ if (directives && fresh.body) {
326
+ // Background revalidation: nothing streams to a client, so drain
327
+ // the fresh render fully before snapshotting tags (same
328
+ // render-complete barrier as the miss path).
329
+ const body = await new Response(fresh.body).arrayBuffer();
297
330
  await store.putResponse!(
298
331
  cacheKey,
299
- fresh,
332
+ new Response(body, fresh),
300
333
  directives.sMaxAge!,
301
334
  directives.staleWhileRevalidate,
335
+ collectRequestTags(requestCtx),
302
336
  );
303
337
  log(`[DocumentCache] REVALIDATED ${typeLabel}: ${url.pathname}`);
304
338
  }
305
339
  } catch (error) {
306
- console.error(`[DocumentCache] Revalidation failed:`, error);
340
+ reportCacheError(
341
+ error,
342
+ "cache-write",
343
+ "[DocumentCache] revalidation",
344
+ );
307
345
  }
308
346
  });
309
347
 
@@ -336,14 +374,27 @@ export function createDocumentCacheMiddleware<TEnv = any>(
336
374
  // Clone response for caching (non-blocking)
337
375
  runBackground(requestCtx, async () => {
338
376
  try {
377
+ // Drain the cache copy fully BEFORE snapshotting tags. Tags from
378
+ // Suspense-streamed "use cache"/cacheTag and loaders are recorded as
379
+ // each value resolves during the RSC/HTML render, which completes
380
+ // only when the stream ends - the handler-settlement barrier is too
381
+ // early. Buffering the body (the client streams the other tee branch,
382
+ // unaffected) is the render-complete barrier that keeps the cached
383
+ // body and its tag set consistent.
384
+ const body = await new Response(cacheStream).arrayBuffer();
339
385
  await store.putResponse!(
340
386
  cacheKey,
341
- new Response(cacheStream, originalResponse),
387
+ new Response(body, originalResponse),
342
388
  directives.sMaxAge!,
343
389
  directives.staleWhileRevalidate,
390
+ collectRequestTags(requestCtx),
344
391
  );
345
392
  } catch (error) {
346
- console.error(`[DocumentCache] Cache write failed:`, error);
393
+ reportCacheError(
394
+ error,
395
+ "cache-write",
396
+ "[DocumentCache] cache write",
397
+ );
347
398
  }
348
399
  });
349
400
 
@@ -356,7 +407,7 @@ export function createDocumentCacheMiddleware<TEnv = any>(
356
407
  // No cache headers - pass through
357
408
  return originalResponse;
358
409
  } catch (error) {
359
- console.error(`[DocumentCache] Error:`, error);
410
+ reportCacheError(error, "cache-read", "[DocumentCache] middleware");
360
411
  if (handlerCalled) {
361
412
  // Post-handler failure (e.g. body.tee()): do not call next() again
362
413
  // as that would re-run handler side effects.
@@ -9,6 +9,69 @@
9
9
  import type { ResolvedSegment } from "../types.js";
10
10
  import type { HandleStore } from "../server/handle-store.js";
11
11
  import type { SegmentHandleData } from "./types.js";
12
+ import { serializeResult, deserializeResult } from "./segment-codec.js";
13
+
14
+ const HANDLE_ENCODE_TIMEOUT_MS = 5000;
15
+
16
+ type HandleRecord = Record<string, SegmentHandleData>;
17
+
18
+ function hasHandleData(handles: HandleRecord): boolean {
19
+ for (const segId in handles) {
20
+ for (const _ in handles[segId]) return true;
21
+ }
22
+ return false;
23
+ }
24
+
25
+ function withTimeout<T>(p: Promise<T>, ms: number, onTimeout: T): Promise<T> {
26
+ let timer: ReturnType<typeof setTimeout>;
27
+ const timeout = new Promise<T>((resolve) => {
28
+ timer = setTimeout(() => resolve(onTimeout), ms);
29
+ });
30
+ return Promise.race([
31
+ p.then(
32
+ (v) => {
33
+ clearTimeout(timer);
34
+ return v;
35
+ },
36
+ (e) => {
37
+ clearTimeout(timer);
38
+ throw e;
39
+ },
40
+ ),
41
+ timeout,
42
+ ]);
43
+ }
44
+
45
+ export async function encodeHandles(handles: HandleRecord): Promise<string> {
46
+ if (!hasHandleData(handles)) return "";
47
+ return encodeHandleValue(handles);
48
+ }
49
+
50
+ export function decodeHandles(encoded: string): Promise<HandleRecord | null> {
51
+ return decodeHandleValue<HandleRecord>(encoded);
52
+ }
53
+
54
+ export async function encodeHandleValue(value: unknown): Promise<string> {
55
+ const encoded = await withTimeout(
56
+ serializeResult(value),
57
+ HANDLE_ENCODE_TIMEOUT_MS,
58
+ null,
59
+ );
60
+ return encoded ?? "";
61
+ }
62
+
63
+ /**
64
+ * Decode a Flight-encoded handle-data string. Returns null on any decode
65
+ * failure so the caller can skip handle restore without discarding valid
66
+ * cached/prerendered segments.
67
+ */
68
+ export async function decodeHandleValue<T>(encoded: string): Promise<T | null> {
69
+ try {
70
+ return await deserializeResult<T>(encoded);
71
+ } catch {
72
+ return null;
73
+ }
74
+ }
12
75
 
13
76
  /**
14
77
  * Capture handle data for a set of segments from the handle store.
@@ -1,43 +1,46 @@
1
- /**
2
- * Cache Store
3
- *
4
- * Server-side caching for RSC segments and loader data.
5
- *
6
- * Main exports for users:
7
- * - SegmentCacheStore - Interface for implementing custom cache stores
8
- * - MemorySegmentCacheStore - In-memory cache for development/testing
9
- * - CFCacheStore - Cloudflare edge cache store for production
10
- * - CacheScope / createCacheScope - Request-scoped cache provider
11
- */
12
-
13
- // Segment cache store types and implementations
14
1
  export type {
15
2
  SegmentCacheStore,
16
- SegmentCacheProvider,
17
3
  CachedEntryData,
18
- CachedEntryResult,
19
4
  CacheGetResult,
5
+ CacheItemResult,
6
+ CacheItemOptions,
20
7
  SerializedSegmentData,
21
8
  SegmentHandleData,
22
- CacheConfig,
23
- CacheConfigOrFactory,
24
9
  } from "./types.js";
25
10
 
26
11
  export { MemorySegmentCacheStore } from "./memory-segment-store.js";
27
12
 
28
- // Cloudflare cache store
29
13
  export {
30
14
  CFCacheStore,
31
15
  type CFCacheStoreOptions,
16
+ type CFCacheDebug,
17
+ type CFCacheReadDebugEvent,
18
+ type KVNamespace,
32
19
  CACHE_STALE_AT_HEADER,
33
20
  CACHE_STATUS_HEADER,
21
+ CACHE_REVALIDATING_AT_HEADER,
22
+ EDGE_LOOKUP_TIMEOUT_MS,
23
+ EDGE_READ_TIMEOUT_MS,
24
+ KV_READ_TIMEOUT_MS,
34
25
  } from "./cf/index.js";
35
26
 
36
- // Cache scope
27
+ export {
28
+ VercelCacheStore,
29
+ type VercelCacheStoreOptions,
30
+ type VercelRuntimeCache,
31
+ type VercelCacheDebug,
32
+ type VercelCacheReadDebugEvent,
33
+ type VercelCacheReadOutcome,
34
+ VERCEL_MAX_ITEM_BYTES,
35
+ VERCEL_MAX_TAGS_PER_ITEM,
36
+ VERCEL_MAX_TAG_BYTES,
37
+ } from "./vercel/index.js";
38
+
37
39
  export { CacheScope, createCacheScope } from "./cache-scope.js";
38
40
 
39
- // Document-level cache middleware
40
41
  export {
41
42
  createDocumentCacheMiddleware,
42
43
  type DocumentCacheOptions,
43
44
  } from "./document-cache.js";
45
+
46
+ export type { CacheErrorCategory } from "./cache-error.js";