@rangojs/router 0.0.0-experimental.b9cb8739 → 0.0.0-experimental.bdaf10aa

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 (449) hide show
  1. package/AGENTS.md +8 -4
  2. package/README.md +303 -741
  3. package/dist/bin/rango.js +730 -184
  4. package/dist/testing/vitest.js +82 -0
  5. package/dist/vite/index.js +4344 -1335
  6. package/dist/vite/index.js.bak +5448 -0
  7. package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
  8. package/package.json +86 -15
  9. package/skills/api-client/SKILL.md +211 -0
  10. package/skills/breadcrumbs/SKILL.md +85 -6
  11. package/skills/bundle-analysis/SKILL.md +159 -0
  12. package/skills/cache-guide/SKILL.md +251 -24
  13. package/skills/caching/SKILL.md +375 -17
  14. package/skills/catalog.json +271 -0
  15. package/skills/comparison/SKILL.md +50 -0
  16. package/skills/comparison/agents/openai.yaml +4 -0
  17. package/skills/comparison/references/framework-comparison.md +837 -0
  18. package/skills/composability/SKILL.md +110 -4
  19. package/skills/css/SKILL.md +76 -0
  20. package/skills/debug-manifest/SKILL.md +5 -3
  21. package/skills/defer-hydration/SKILL.md +235 -0
  22. package/skills/document-cache/SKILL.md +87 -56
  23. package/skills/fonts/SKILL.md +1 -1
  24. package/skills/handler-use/SKILL.md +364 -0
  25. package/skills/hooks/SKILL.md +73 -691
  26. package/skills/hooks/data.md +273 -0
  27. package/skills/hooks/handle-and-actions.md +103 -0
  28. package/skills/hooks/navigation.md +110 -0
  29. package/skills/hooks/outlets.md +41 -0
  30. package/skills/hooks/state.md +228 -0
  31. package/skills/hooks/urls.md +135 -0
  32. package/skills/host-router/SKILL.md +129 -27
  33. package/skills/i18n/SKILL.md +276 -0
  34. package/skills/intercept/SKILL.md +94 -18
  35. package/skills/layout/SKILL.md +62 -19
  36. package/skills/links/SKILL.md +249 -17
  37. package/skills/loader/SKILL.md +302 -54
  38. package/skills/middleware/SKILL.md +59 -16
  39. package/skills/migrate-nextjs/SKILL.md +745 -0
  40. package/skills/migrate-react-router/SKILL.md +153 -0
  41. package/skills/migrate-react-router/cloudflare-workers.md +129 -0
  42. package/skills/migrate-react-router/component-migration.md +196 -0
  43. package/skills/migrate-react-router/data-and-actions.md +225 -0
  44. package/skills/migrate-react-router/route-mapping.md +271 -0
  45. package/skills/mime-routes/SKILL.md +29 -2
  46. package/skills/observability/SKILL.md +202 -0
  47. package/skills/parallel/SKILL.md +225 -10
  48. package/skills/ppr/SKILL.md +622 -0
  49. package/skills/prerender/SKILL.md +178 -124
  50. package/skills/rango/SKILL.md +318 -24
  51. package/skills/react-compiler/SKILL.md +168 -0
  52. package/skills/response-routes/SKILL.md +138 -49
  53. package/skills/route/SKILL.md +172 -9
  54. package/skills/router-setup/SKILL.md +131 -11
  55. package/skills/scripts/SKILL.md +179 -0
  56. package/skills/server-actions/SKILL.md +776 -0
  57. package/skills/shell-manifest/SKILL.md +185 -0
  58. package/skills/streams-and-websockets/SKILL.md +283 -0
  59. package/skills/tailwind/SKILL.md +28 -4
  60. package/skills/testing/SKILL.md +130 -0
  61. package/skills/testing/bindings.md +103 -0
  62. package/skills/testing/cache-prerender.md +127 -0
  63. package/skills/testing/client-components.md +124 -0
  64. package/skills/testing/e2e-parity.md +125 -0
  65. package/skills/testing/flight.md +91 -0
  66. package/skills/testing/handles.md +131 -0
  67. package/skills/testing/loader.md +128 -0
  68. package/skills/testing/middleware.md +99 -0
  69. package/skills/testing/render-handler.md +122 -0
  70. package/skills/testing/response-routes.md +95 -0
  71. package/skills/testing/reverse-and-types.md +85 -0
  72. package/skills/testing/server-actions.md +107 -0
  73. package/skills/testing/server-tree.md +128 -0
  74. package/skills/testing/setup.md +123 -0
  75. package/skills/theme/SKILL.md +1 -1
  76. package/skills/typesafety/SKILL.md +45 -616
  77. package/skills/typesafety/env-and-bindings.md +254 -0
  78. package/skills/typesafety/generated-files-and-cli.md +335 -0
  79. package/skills/typesafety/params-and-search.md +153 -0
  80. package/skills/typesafety/route-types.md +209 -0
  81. package/skills/use-cache/SKILL.md +74 -15
  82. package/skills/vercel/SKILL.md +128 -0
  83. package/skills/view-transitions/SKILL.md +337 -0
  84. package/src/__augment-tests__/augment.ts +81 -0
  85. package/src/__augment-tests__/augmented.check.ts +116 -0
  86. package/src/__internal.ts +1 -66
  87. package/src/browser/action-coordinator.ts +53 -36
  88. package/src/browser/action-fence.ts +47 -0
  89. package/src/browser/app-shell.ts +39 -0
  90. package/src/browser/app-version.ts +14 -0
  91. package/src/browser/connection-warmup.ts +134 -0
  92. package/src/browser/cookie-name.ts +140 -0
  93. package/src/browser/event-controller.ts +257 -158
  94. package/src/browser/history-state.ts +21 -0
  95. package/src/browser/index.ts +3 -3
  96. package/src/browser/invalidate-client-cache.ts +52 -0
  97. package/src/browser/logging.ts +28 -0
  98. package/src/browser/merge-segment-loaders.ts +6 -4
  99. package/src/browser/navigation-bridge.ts +132 -33
  100. package/src/browser/navigation-client.ts +218 -68
  101. package/src/browser/navigation-store-handle.ts +38 -0
  102. package/src/browser/navigation-store.ts +203 -80
  103. package/src/browser/navigation-transaction.ts +18 -66
  104. package/src/browser/network-error-handler.ts +34 -7
  105. package/src/browser/partial-update.ts +241 -127
  106. package/src/browser/prefetch/cache.ts +271 -44
  107. package/src/browser/prefetch/fetch.ts +367 -40
  108. package/src/browser/prefetch/queue.ts +144 -23
  109. package/src/browser/prefetch/resource-ready.ts +77 -0
  110. package/src/browser/rango-state.ts +158 -76
  111. package/src/browser/react/Link.tsx +121 -16
  112. package/src/browser/react/NavigationProvider.tsx +240 -122
  113. package/src/browser/react/ScrollRestoration.tsx +10 -6
  114. package/src/browser/react/context.ts +7 -2
  115. package/src/browser/react/filter-segment-order.ts +66 -7
  116. package/src/browser/react/index.ts +0 -48
  117. package/src/browser/react/location-state-shared.ts +178 -8
  118. package/src/browser/react/location-state.ts +39 -14
  119. package/src/browser/react/use-action.ts +6 -15
  120. package/src/browser/react/use-handle.ts +23 -69
  121. package/src/browser/react/use-href.tsx +8 -1
  122. package/src/browser/react/use-link-status.ts +33 -8
  123. package/src/browser/react/use-navigation.ts +32 -7
  124. package/src/browser/react/use-params.ts +20 -10
  125. package/src/browser/react/use-reverse.ts +106 -0
  126. package/src/browser/react/use-router.ts +46 -11
  127. package/src/browser/react/use-search-params.ts +0 -5
  128. package/src/browser/react/use-segments.ts +11 -21
  129. package/src/browser/response-adapter.ts +99 -8
  130. package/src/browser/rsc-router.tsx +272 -80
  131. package/src/browser/scroll-restoration.ts +56 -22
  132. package/src/browser/segment-reconciler.ts +44 -7
  133. package/src/browser/segment-structure-assert.ts +2 -2
  134. package/src/browser/server-action-bridge.ts +244 -71
  135. package/src/browser/types.ts +136 -12
  136. package/src/browser/validate-redirect-origin.ts +43 -16
  137. package/src/build/collect-fallback-refs.ts +107 -0
  138. package/src/build/generate-manifest.ts +207 -158
  139. package/src/build/generate-route-types.ts +6 -1
  140. package/src/build/index.ts +11 -3
  141. package/src/build/prefix-tree-utils.ts +123 -0
  142. package/src/build/route-trie.ts +198 -41
  143. package/src/build/route-types/ast-route-extraction.ts +15 -8
  144. package/src/build/route-types/codegen.ts +16 -5
  145. package/src/build/route-types/include-resolution.ts +464 -63
  146. package/src/build/route-types/param-extraction.ts +6 -3
  147. package/src/build/route-types/per-module-writer.ts +22 -6
  148. package/src/build/route-types/router-processing.ts +336 -110
  149. package/src/build/route-types/scan-filter.ts +9 -2
  150. package/src/build/route-types/source-scan.ts +216 -0
  151. package/src/build/runtime-discovery.ts +13 -21
  152. package/src/cache/cache-error.ts +104 -0
  153. package/src/cache/cache-key-utils.ts +58 -13
  154. package/src/cache/cache-policy.ts +108 -34
  155. package/src/cache/cache-runtime.ts +454 -97
  156. package/src/cache/cache-scope.ts +235 -103
  157. package/src/cache/cache-tag.ts +149 -0
  158. package/src/cache/cf/cf-base64.ts +33 -0
  159. package/src/cache/cf/cf-cache-constants.ts +127 -0
  160. package/src/cache/cf/cf-cache-store.ts +2446 -170
  161. package/src/cache/cf/cf-cache-types.ts +349 -0
  162. package/src/cache/cf/cf-kv-utils.ts +46 -0
  163. package/src/cache/cf/cf-tag-marker-memo.ts +105 -0
  164. package/src/cache/cf/index.ts +11 -17
  165. package/src/cache/document-cache.ts +144 -49
  166. package/src/cache/handle-snapshot.ts +70 -0
  167. package/src/cache/index.ts +24 -20
  168. package/src/cache/memory-segment-store.ts +243 -37
  169. package/src/cache/profile-registry.ts +46 -31
  170. package/src/cache/read-through-swr.ts +56 -12
  171. package/src/cache/segment-codec.ts +13 -21
  172. package/src/cache/shell-snapshot.ts +417 -0
  173. package/src/cache/tag-invalidation.ts +230 -0
  174. package/src/cache/taint.ts +55 -0
  175. package/src/cache/types.ts +194 -99
  176. package/src/cache/vercel/index.ts +11 -0
  177. package/src/cache/vercel/vercel-cache-store.ts +1132 -0
  178. package/src/client.rsc.tsx +41 -21
  179. package/src/client.tsx +116 -290
  180. package/src/cloudflare/index.ts +11 -0
  181. package/src/cloudflare/tracing.ts +108 -0
  182. package/src/component-utils.ts +19 -0
  183. package/src/components/DefaultDocument.tsx +8 -2
  184. package/src/context-var.ts +84 -2
  185. package/src/debug.ts +2 -2
  186. package/src/decode-loader-results.ts +52 -0
  187. package/src/defer.ts +185 -0
  188. package/src/deps/ssr.ts +0 -1
  189. package/src/encode-kv.ts +49 -0
  190. package/src/errors.ts +30 -4
  191. package/src/escape-script.ts +52 -0
  192. package/src/handle.ts +104 -34
  193. package/src/handles/MetaTags.tsx +24 -53
  194. package/src/handles/Scripts.tsx +183 -0
  195. package/src/handles/breadcrumbs.ts +35 -8
  196. package/src/handles/deferred-resolution.ts +127 -0
  197. package/src/handles/is-thenable.ts +18 -0
  198. package/src/handles/meta.ts +14 -40
  199. package/src/handles/script.ts +244 -0
  200. package/src/host/cookie-handler.ts +9 -60
  201. package/src/host/errors.ts +13 -22
  202. package/src/host/index.ts +9 -2
  203. package/src/host/pattern-matcher.ts +23 -52
  204. package/src/host/router.ts +107 -99
  205. package/src/host/testing.ts +40 -27
  206. package/src/host/types.ts +37 -4
  207. package/src/host/utils.ts +1 -1
  208. package/src/href-client.ts +137 -22
  209. package/src/index.rsc.ts +100 -13
  210. package/src/index.ts +143 -19
  211. package/src/internal-debug.ts +11 -10
  212. package/src/loader-store.ts +500 -0
  213. package/src/loader.rsc.ts +20 -13
  214. package/src/loader.ts +12 -11
  215. package/src/missing-id-error.ts +68 -0
  216. package/src/outlet-context.ts +1 -1
  217. package/src/outlet-provider.tsx +1 -5
  218. package/src/prerender/param-hash.ts +16 -16
  219. package/src/prerender/store.ts +37 -41
  220. package/src/prerender.ts +215 -86
  221. package/src/redirect-origin.ts +114 -0
  222. package/src/regex-escape.ts +8 -0
  223. package/src/render-error-thrower.tsx +20 -0
  224. package/src/response-utils.ts +62 -0
  225. package/src/reverse.ts +65 -15
  226. package/src/root-error-boundary.tsx +1 -19
  227. package/src/route-content-wrapper.tsx +19 -77
  228. package/src/route-definition/dsl-helpers.ts +485 -303
  229. package/src/route-definition/helper-factories.ts +28 -140
  230. package/src/route-definition/helpers-types.ts +153 -77
  231. package/src/route-definition/index.ts +4 -2
  232. package/src/route-definition/redirect.ts +53 -12
  233. package/src/route-definition/resolve-handler-use.ts +160 -0
  234. package/src/route-definition/use-item-types.ts +29 -0
  235. package/src/route-map-builder.ts +48 -21
  236. package/src/route-types.ts +37 -46
  237. package/src/router/basename.ts +14 -0
  238. package/src/router/content-negotiation.ts +164 -17
  239. package/src/router/error-handling.ts +45 -18
  240. package/src/router/find-match.ts +130 -29
  241. package/src/router/handler-context.ts +83 -39
  242. package/src/router/instrument.ts +355 -0
  243. package/src/router/intercept-resolution.ts +50 -24
  244. package/src/router/lazy-includes.ts +89 -63
  245. package/src/router/loader-resolution.ts +286 -56
  246. package/src/router/logging.ts +5 -8
  247. package/src/router/manifest.ts +105 -56
  248. package/src/router/match-api.ts +178 -218
  249. package/src/router/match-context.ts +0 -22
  250. package/src/router/match-handlers.ts +211 -165
  251. package/src/router/match-middleware/background-revalidation.ts +66 -22
  252. package/src/router/match-middleware/cache-lookup.ts +214 -263
  253. package/src/router/match-middleware/cache-store.ts +105 -50
  254. package/src/router/match-middleware/intercept-resolution.ts +8 -28
  255. package/src/router/match-middleware/segment-resolution.ts +52 -18
  256. package/src/router/match-pipelines.ts +1 -42
  257. package/src/router/match-result.ts +128 -44
  258. package/src/router/metrics.ts +5 -34
  259. package/src/router/middleware-types.ts +13 -142
  260. package/src/router/middleware.ts +301 -177
  261. package/src/router/navigation-snapshot.ts +133 -0
  262. package/src/router/params-util.ts +23 -0
  263. package/src/router/parse-pattern.ts +115 -0
  264. package/src/router/pattern-matching.ts +181 -150
  265. package/src/router/prefetch-cache-ttl.ts +51 -0
  266. package/src/router/prefetch-limits.ts +37 -0
  267. package/src/router/prerender-match.ts +203 -58
  268. package/src/router/preview-match.ts +35 -103
  269. package/src/router/request-classification.ts +291 -0
  270. package/src/router/revalidation.ts +123 -73
  271. package/src/router/route-snapshot.ts +256 -0
  272. package/src/router/router-context.ts +11 -29
  273. package/src/router/router-interfaces.ts +146 -35
  274. package/src/router/router-options.ts +202 -15
  275. package/src/router/router-registry.ts +2 -5
  276. package/src/router/segment-resolution/fresh.ts +301 -78
  277. package/src/router/segment-resolution/helpers.ts +115 -30
  278. package/src/router/segment-resolution/loader-cache.ts +156 -39
  279. package/src/router/segment-resolution/loader-mask.ts +60 -0
  280. package/src/router/segment-resolution/loader-snapshot.ts +259 -0
  281. package/src/router/segment-resolution/mask-nested.ts +83 -0
  282. package/src/router/segment-resolution/revalidation.ts +477 -385
  283. package/src/router/segment-resolution/static-store.ts +19 -5
  284. package/src/router/segment-resolution/streamed-handler-telemetry.ts +52 -0
  285. package/src/router/segment-resolution/view-transition-default.ts +56 -0
  286. package/src/router/segment-resolution.ts +5 -1
  287. package/src/router/segment-wrappers.ts +8 -5
  288. package/src/router/state-cookie-name.ts +33 -0
  289. package/src/router/substitute-pattern-params.ts +75 -0
  290. package/src/router/telemetry-otel.ts +160 -200
  291. package/src/router/telemetry.ts +105 -20
  292. package/src/router/timeout.ts +0 -20
  293. package/src/router/tracing.ts +215 -0
  294. package/src/router/trie-matching.ts +171 -59
  295. package/src/router/types.ts +10 -63
  296. package/src/router/url-params.ts +57 -0
  297. package/src/router.ts +210 -71
  298. package/src/rsc/full-payload.ts +70 -0
  299. package/src/rsc/handler-context.ts +3 -2
  300. package/src/rsc/handler.ts +682 -508
  301. package/src/rsc/helpers.ts +168 -46
  302. package/src/rsc/index.ts +2 -5
  303. package/src/rsc/json-route-result.ts +38 -0
  304. package/src/rsc/loader-fetch.ts +127 -31
  305. package/src/rsc/manifest-init.ts +33 -42
  306. package/src/rsc/nonce.ts +10 -1
  307. package/src/rsc/origin-guard.ts +39 -25
  308. package/src/rsc/progressive-enhancement.ts +138 -15
  309. package/src/rsc/redirect-guard.ts +100 -0
  310. package/src/rsc/response-cache-serve.ts +238 -0
  311. package/src/rsc/response-error.ts +79 -12
  312. package/src/rsc/response-route-handler.ts +99 -189
  313. package/src/rsc/rsc-rendering.ts +509 -73
  314. package/src/rsc/runtime-warnings.ts +23 -10
  315. package/src/rsc/server-action.ts +287 -113
  316. package/src/rsc/shell-capture.ts +1190 -0
  317. package/src/rsc/shell-serve.ts +181 -0
  318. package/src/rsc/ssr-setup.ts +18 -2
  319. package/src/rsc/transition-gate.ts +89 -0
  320. package/src/rsc/types.ts +62 -6
  321. package/src/runtime-env.ts +18 -0
  322. package/src/search-params.ts +35 -30
  323. package/src/segment-content-promise.ts +67 -0
  324. package/src/segment-loader-promise.ts +167 -0
  325. package/src/segment-system.tsx +449 -132
  326. package/src/serialize.ts +243 -0
  327. package/src/server/context.ts +367 -61
  328. package/src/server/cookie-parse.ts +32 -0
  329. package/src/server/cookie-store.ts +152 -5
  330. package/src/server/handle-store.ts +40 -38
  331. package/src/server/loader-registry.ts +38 -46
  332. package/src/server/request-context.ts +558 -173
  333. package/src/ssr/index.tsx +491 -174
  334. package/src/ssr/inject-rsc-eager.ts +167 -0
  335. package/src/ssr/ssr-root.tsx +228 -0
  336. package/src/static-handler.ts +27 -18
  337. package/src/testing/cache-status.ts +162 -0
  338. package/src/testing/collect-handle.ts +46 -0
  339. package/src/testing/dispatch.ts +813 -0
  340. package/src/testing/dom.entry.ts +22 -0
  341. package/src/testing/e2e/fixture.ts +188 -0
  342. package/src/testing/e2e/index.ts +128 -0
  343. package/src/testing/e2e/matchers.ts +35 -0
  344. package/src/testing/e2e/page-helpers.ts +272 -0
  345. package/src/testing/e2e/parity.ts +387 -0
  346. package/src/testing/e2e/server.ts +195 -0
  347. package/src/testing/flight-matchers.ts +97 -0
  348. package/src/testing/flight-normalize.ts +11 -0
  349. package/src/testing/flight-runtime.d.ts +57 -0
  350. package/src/testing/flight-tree.ts +682 -0
  351. package/src/testing/flight.entry.ts +52 -0
  352. package/src/testing/flight.ts +257 -0
  353. package/src/testing/generated-routes.ts +199 -0
  354. package/src/testing/index.ts +105 -0
  355. package/src/testing/internal/context.ts +371 -0
  356. package/src/testing/internal/flight-client-globals.ts +30 -0
  357. package/src/testing/internal/seed-vars.ts +54 -0
  358. package/src/testing/render-handler.ts +357 -0
  359. package/src/testing/render-route.tsx +584 -0
  360. package/src/testing/run-loader.ts +385 -0
  361. package/src/testing/run-middleware.ts +205 -0
  362. package/src/testing/run-transition-when.ts +164 -0
  363. package/src/testing/vitest-stubs/cloudflare-email.ts +9 -0
  364. package/src/testing/vitest-stubs/cloudflare-workers.ts +21 -0
  365. package/src/testing/vitest-stubs/plugin-rsc.ts +16 -0
  366. package/src/testing/vitest-stubs/version.ts +5 -0
  367. package/src/testing/vitest.ts +305 -0
  368. package/src/theme/ThemeProvider.tsx +56 -84
  369. package/src/theme/ThemeScript.tsx +7 -9
  370. package/src/theme/constants.ts +52 -13
  371. package/src/theme/index.ts +0 -7
  372. package/src/theme/theme-context.ts +1 -5
  373. package/src/theme/theme-script.ts +22 -21
  374. package/src/theme/use-theme.ts +0 -3
  375. package/src/types/boundaries.ts +0 -35
  376. package/src/types/cache-types.ts +17 -8
  377. package/src/types/error-types.ts +30 -90
  378. package/src/types/global-namespace.ts +54 -41
  379. package/src/types/handler-context.ts +234 -82
  380. package/src/types/index.ts +3 -10
  381. package/src/types/loader-types.ts +44 -15
  382. package/src/types/request-scope.ts +112 -0
  383. package/src/types/route-config.ts +20 -52
  384. package/src/types/route-entry.ts +19 -7
  385. package/src/types/segments.ts +137 -14
  386. package/src/urls/include-helper.ts +40 -75
  387. package/src/urls/include-provider.ts +71 -0
  388. package/src/urls/index.ts +2 -11
  389. package/src/urls/path-helper-types.ts +102 -23
  390. package/src/urls/path-helper.ts +62 -111
  391. package/src/urls/pattern-types.ts +84 -19
  392. package/src/urls/response-types.ts +25 -22
  393. package/src/urls/type-extraction.ts +98 -154
  394. package/src/urls/urls-function.ts +1 -19
  395. package/src/use-loader.tsx +346 -89
  396. package/src/vercel/index.ts +11 -0
  397. package/src/vercel/tracing.ts +88 -0
  398. package/src/vite/debug.ts +185 -0
  399. package/src/vite/discovery/bundle-postprocess.ts +36 -38
  400. package/src/vite/discovery/dev-prerender-cache.ts +117 -0
  401. package/src/vite/discovery/discover-routers.ts +130 -85
  402. package/src/vite/discovery/discovery-errors.ts +255 -0
  403. package/src/vite/discovery/gate-state.ts +171 -0
  404. package/src/vite/discovery/prerender-collection.ts +214 -132
  405. package/src/vite/discovery/route-types-writer.ts +40 -84
  406. package/src/vite/discovery/self-gen-tracking.ts +27 -1
  407. package/src/vite/discovery/state.ts +57 -6
  408. package/src/vite/discovery/virtual-module-codegen.ts +14 -34
  409. package/src/vite/index.ts +15 -0
  410. package/src/vite/inject-client-debug.ts +88 -0
  411. package/src/vite/plugin-types.ts +234 -62
  412. package/src/vite/plugins/cjs-to-esm.ts +16 -19
  413. package/src/vite/plugins/client-ref-dedup.ts +16 -11
  414. package/src/vite/plugins/client-ref-hashing.ts +28 -15
  415. package/src/vite/plugins/cloudflare-protocol-loader-hook.d.mts +23 -0
  416. package/src/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
  417. package/src/vite/plugins/cloudflare-protocol-stub.ts +194 -0
  418. package/src/vite/plugins/expose-action-id.ts +49 -98
  419. package/src/vite/plugins/expose-id-utils.ts +96 -51
  420. package/src/vite/plugins/expose-ids/export-analysis.ts +101 -34
  421. package/src/vite/plugins/expose-ids/handler-transform.ts +15 -64
  422. package/src/vite/plugins/expose-ids/loader-transform.ts +14 -24
  423. package/src/vite/plugins/expose-ids/router-transform.ts +118 -29
  424. package/src/vite/plugins/expose-internal-ids.ts +553 -317
  425. package/src/vite/plugins/performance-tracks.ts +89 -0
  426. package/src/vite/plugins/refresh-cmd.ts +89 -27
  427. package/src/vite/plugins/use-cache-transform.ts +73 -83
  428. package/src/vite/plugins/vercel-output.ts +384 -0
  429. package/src/vite/plugins/version-injector.ts +40 -29
  430. package/src/vite/plugins/version-plugin.ts +46 -37
  431. package/src/vite/plugins/virtual-entries.ts +138 -27
  432. package/src/vite/rango.ts +353 -303
  433. package/src/vite/router-discovery.ts +1090 -166
  434. package/src/vite/utils/ast-handler-extract.ts +26 -35
  435. package/src/vite/utils/banner.ts +4 -4
  436. package/src/vite/utils/bundle-analysis.ts +10 -15
  437. package/src/vite/utils/client-chunks.ts +184 -0
  438. package/src/vite/utils/directive-prologue.ts +40 -0
  439. package/src/vite/utils/forward-user-plugins.ts +171 -0
  440. package/src/vite/utils/manifest-utils.ts +4 -59
  441. package/src/vite/utils/package-resolution.ts +20 -52
  442. package/src/vite/utils/prerender-utils.ts +98 -38
  443. package/src/vite/utils/shared-utils.ts +144 -44
  444. package/src/browser/action-response-classifier.ts +0 -99
  445. package/src/browser/react/use-client-cache.ts +0 -58
  446. package/src/browser/shallow.ts +0 -40
  447. package/src/handles/index.ts +0 -7
  448. package/src/network-error-thrower.tsx +0 -23
  449. package/src/router/middleware-cookies.ts +0 -55
@@ -33,10 +33,116 @@ import type { ErrorBoundaryHandler, NotFoundBoundaryHandler } from "../types";
33
33
  import type { MiddlewareFn } from "./middleware.js";
34
34
  import {
35
35
  type TelemetrySink,
36
+ type CacheSegmentSignal,
36
37
  safeEmit,
37
38
  resolveSink,
38
39
  getRequestId,
40
+ buildCacheSignalSegments,
39
41
  } from "./telemetry.js";
42
+ import { _getRequestContext } from "../server/request-context.js";
43
+
44
+ /**
45
+ * Per-call telemetry lifecycle emitter for match()/matchPartial(). Each method
46
+ * reproduces the exact event object the two functions used to emit inline and is
47
+ * gated on the same per-call flag (`enabled` = the request's `emitTelemetry`), so
48
+ * a PPR shell-capture run (enabled=false) emits nothing while a foreground run
49
+ * emits byte-identical events. Extracted so the two transactions can't drift;
50
+ * pinned by thrown-response-telemetry.test.ts and
51
+ * shell-capture-telemetry-suppression.test.ts.
52
+ */
53
+ interface LifecycleEmitter {
54
+ start(): void;
55
+ end(segmentCount: number, cacheHit: boolean, status?: number): void;
56
+ cacheDecision(
57
+ routeKey: string,
58
+ state: {
59
+ cacheHit: boolean;
60
+ cacheSource?: "runtime" | "prerender";
61
+ shouldRevalidate?: boolean;
62
+ },
63
+ segments: CacheSegmentSignal[],
64
+ ): void;
65
+ error(error: Error, phase: string): void;
66
+ }
67
+
68
+ function createLifecycleEmitter(args: {
69
+ enabled: boolean;
70
+ sink: TelemetrySink;
71
+ requestId: string | undefined;
72
+ method: string;
73
+ pathname: string;
74
+ transaction: "match" | "matchPartial";
75
+ isPartial: boolean;
76
+ matchStart: number;
77
+ }): LifecycleEmitter {
78
+ return {
79
+ start(): void {
80
+ if (!args.enabled) return;
81
+ safeEmit(args.sink, {
82
+ type: "request.start",
83
+ timestamp: args.matchStart,
84
+ requestId: args.requestId,
85
+ method: args.method,
86
+ pathname: args.pathname,
87
+ transaction: args.transaction,
88
+ isPartial: args.isPartial,
89
+ });
90
+ },
91
+ end(segmentCount: number, cacheHit: boolean, status?: number): void {
92
+ if (!args.enabled) return;
93
+ safeEmit(args.sink, {
94
+ type: "request.end",
95
+ timestamp: performance.now(),
96
+ requestId: args.requestId,
97
+ method: args.method,
98
+ pathname: args.pathname,
99
+ transaction: args.transaction,
100
+ durationMs: performance.now() - args.matchStart,
101
+ segmentCount,
102
+ cacheHit,
103
+ // Only a thrown-Response short-circuit passes a status; a normal render
104
+ // completion omits it (the Response is built after match()).
105
+ ...(status !== undefined && { status }),
106
+ });
107
+ },
108
+ cacheDecision(
109
+ routeKey: string,
110
+ state: {
111
+ cacheHit: boolean;
112
+ cacheSource?: "runtime" | "prerender";
113
+ shouldRevalidate?: boolean;
114
+ },
115
+ segments: CacheSegmentSignal[],
116
+ ): void {
117
+ if (!args.enabled) return;
118
+ safeEmit(args.sink, {
119
+ type: "cache.decision",
120
+ timestamp: performance.now(),
121
+ requestId: args.requestId,
122
+ pathname: args.pathname,
123
+ routeKey,
124
+ hit: state.cacheHit,
125
+ shouldRevalidate: !!state.shouldRevalidate,
126
+ source: state.cacheSource,
127
+ segments,
128
+ });
129
+ },
130
+ error(error: Error, phase: string): void {
131
+ if (!args.enabled) return;
132
+ safeEmit(args.sink, {
133
+ type: "request.error",
134
+ timestamp: performance.now(),
135
+ requestId: args.requestId,
136
+ method: args.method,
137
+ pathname: args.pathname,
138
+ transaction: args.transaction,
139
+ error,
140
+ phase,
141
+ durationMs: performance.now() - args.matchStart,
142
+ });
143
+ },
144
+ };
145
+ }
40
146
 
41
147
  export interface MatchHandlerDeps<TEnv = any> {
42
148
  buildRouterContext: () => RouterContext<TEnv>;
@@ -51,6 +157,12 @@ export interface MatchHandlerDeps<TEnv = any> {
51
157
  isAction: boolean,
52
158
  ) => { intercept: InterceptEntry; entry: EntryData } | null;
53
159
  telemetry?: TelemetrySink;
160
+ /**
161
+ * DEVELOPMENT/TEST ONLY gate for the X-Rango-Cache debug header. When true,
162
+ * match/matchPartial stash a coarse route-level cache signal on the request
163
+ * context for the response-finalization path to emit. Default off.
164
+ */
165
+ cacheSignalEnabled?: boolean;
54
166
  }
55
167
 
56
168
  export interface MatchHandlers<TEnv = any> {
@@ -80,20 +192,6 @@ export interface MatchHandlers<TEnv = any> {
80
192
  negotiated?: boolean;
81
193
  manifestEntry?: EntryData;
82
194
  } | null>;
83
- createMatchContextForFull: (
84
- request: Request,
85
- env: TEnv,
86
- ) => Promise<MatchContext<TEnv> | { type: "redirect"; redirectUrl: string }>;
87
- createMatchContextForPartial: (
88
- request: Request,
89
- env: TEnv,
90
- actionContext?: {
91
- actionId?: string;
92
- actionUrl?: URL;
93
- actionResult?: any;
94
- formData?: FormData;
95
- },
96
- ) => Promise<MatchContext<TEnv> | null>;
97
195
  }
98
196
 
99
197
  /**
@@ -113,6 +211,20 @@ export function createMatchHandlers<TEnv = any>(
113
211
  } = deps;
114
212
  const hasTelemetry = !!deps.telemetry;
115
213
  const telemetry = resolveSink(deps.telemetry);
214
+ const cacheSignalEnabled = !!deps.cacheSignalEnabled;
215
+ const buildSignal = (
216
+ routeKey: string,
217
+ state: {
218
+ cacheHit: boolean;
219
+ cacheSource?: "runtime" | "prerender";
220
+ shouldRevalidate?: boolean;
221
+ },
222
+ ): CacheSegmentSignal[] => buildCacheSignalSegments(routeKey, state);
223
+ const recordSignalIfEnabled = (segments: CacheSegmentSignal[]): void => {
224
+ if (!cacheSignalEnabled) return;
225
+ const reqCtx = _getRequestContext();
226
+ if (reqCtx) reqCtx._cacheSignal = segments;
227
+ };
116
228
 
117
229
  async function createMatchContextForFull(
118
230
  request: Request,
@@ -145,57 +257,49 @@ export function createMatchHandlers<TEnv = any>(
145
257
  );
146
258
  }
147
259
 
148
- /**
149
- * Match request and return segments (document/SSR requests)
150
- *
151
- * Uses generator middleware pipeline for clean separation of concerns:
152
- * - cache-lookup: Check cache first
153
- * - segment-resolution: Resolve segments on cache miss
154
- * - cache-store: Store results in cache
155
- * - background-revalidation: SWR revalidation
156
- */
157
260
  async function match(request: Request, env: TEnv): Promise<MatchResult> {
158
- const requestId = hasTelemetry ? getRequestId(request) : undefined;
261
+ // Silence telemetry for the PPR background shell capture: it re-runs match()
262
+ // under a derived request context flagged _shellCaptureRun (shell-capture.ts
263
+ // attemptCapture), re-using the foreground Request — a second request.start/
264
+ // cache.decision/request.end stamped with the same WeakMap-keyed requestId
265
+ // would double-count dashboards. Derived here (inside the capture's active
266
+ // request-context ALS) so the read sees the derived context, not module state.
267
+ const emitTelemetry =
268
+ hasTelemetry && !_getRequestContext()?._shellCaptureRun;
269
+ const requestId = emitTelemetry ? getRequestId(request) : undefined;
159
270
  return runWithRouterLogContext({ request, transaction: "match" }, () => {
160
271
  const routerCtx = buildRouterContext();
272
+ // Also mute in-pipeline observeEvent emitters (revalidation.decision,
273
+ // cache-lookup's cache.decision) which read routerCtx.telemetry.
274
+ if (!emitTelemetry) routerCtx.telemetry = undefined;
161
275
  routerCtx.requestId = requestId;
162
276
  return runWithRouterContext(routerCtx, async () =>
163
277
  withRouterLogScope("match", async () => {
164
278
  const matchStart = performance.now();
165
- const pathname = new URL(request.url).pathname;
166
- if (hasTelemetry) {
167
- safeEmit(telemetry, {
168
- type: "request.start",
169
- timestamp: matchStart,
170
- requestId,
171
- method: request.method,
172
- pathname,
173
- transaction: "match",
174
- isPartial: false,
175
- });
176
- }
279
+ const pathname =
280
+ _getRequestContext()?.url?.pathname ??
281
+ new URL(request.url).pathname;
282
+ const emitter = createLifecycleEmitter({
283
+ enabled: emitTelemetry,
284
+ sink: telemetry,
285
+ requestId,
286
+ method: request.method,
287
+ pathname,
288
+ transaction: "match",
289
+ isPartial: false,
290
+ matchStart,
291
+ });
292
+ emitter.start();
177
293
 
178
294
  const result = await createMatchContextForFull(request, env);
179
295
 
180
- // Handle redirect case
181
296
  if ("type" in result && result.type === "redirect") {
182
- if (hasTelemetry) {
183
- safeEmit(telemetry, {
184
- type: "request.end",
185
- timestamp: performance.now(),
186
- requestId,
187
- method: request.method,
188
- pathname,
189
- transaction: "match",
190
- durationMs: performance.now() - matchStart,
191
- segmentCount: 0,
192
- cacheHit: false,
193
- });
194
- }
297
+ emitter.end(0, false);
195
298
  return {
196
299
  segments: [],
197
300
  matched: [],
198
301
  diff: [],
302
+ resolvedIds: [],
199
303
  params: {},
200
304
  redirect: result.redirectUrl,
201
305
  };
@@ -207,48 +311,29 @@ export function createMatchHandlers<TEnv = any>(
207
311
  const state = createPipelineState();
208
312
  const pipeline = createMatchPartialPipeline(ctx, state);
209
313
  const matchResult = await collectMatchResult(pipeline, ctx, state);
210
- if (hasTelemetry) {
211
- safeEmit(telemetry, {
212
- type: "cache.decision",
213
- timestamp: performance.now(),
214
- requestId,
215
- pathname,
216
- routeKey: ctx.routeKey,
217
- hit: state.cacheHit,
218
- shouldRevalidate: !!state.shouldRevalidate,
219
- source: state.cacheSource,
220
- });
221
- safeEmit(telemetry, {
222
- type: "request.end",
223
- timestamp: performance.now(),
224
- requestId,
225
- method: request.method,
226
- pathname,
227
- transaction: "match",
228
- durationMs: performance.now() - matchStart,
229
- segmentCount: matchResult.segments.length,
230
- cacheHit: state.cacheHit,
231
- });
314
+ if (hasTelemetry || cacheSignalEnabled) {
315
+ const signalSegments = buildSignal(ctx.routeKey, state);
316
+ recordSignalIfEnabled(signalSegments);
317
+ emitter.cacheDecision(ctx.routeKey, state, signalSegments);
232
318
  }
319
+ emitter.end(matchResult.segments.length, state.cacheHit);
233
320
  return matchResult;
234
321
  } catch (error) {
235
- if (hasTelemetry) {
236
- const errorObj =
237
- error instanceof Error ? error : new Error(String(error));
238
- safeEmit(telemetry, {
239
- type: "request.error",
240
- timestamp: performance.now(),
241
- requestId,
242
- method: request.method,
243
- pathname,
244
- transaction: "match",
245
- error: errorObj,
246
- phase: error instanceof Response ? "redirect" : "routing",
247
- durationMs: performance.now() - matchStart,
248
- });
322
+ if (error instanceof Response) {
323
+ // A thrown Response (middleware short-circuit — redirect / auth
324
+ // gate) is a COMPLETED request from the consumer's seat, not an
325
+ // error: emit request.end (the same shape the non-thrown redirect
326
+ // result above already emits), never request.error with a
327
+ // synthetic "[object Response]" error. Rethrow so the caller
328
+ // drives the redirect. Carry the Response's status so a sink can
329
+ // tell a 3xx short-circuit from a 2xx completion.
330
+ emitter.end(0, false, error.status);
331
+ throw error;
249
332
  }
250
- if (error instanceof Response) throw error;
251
- // Report unhandled errors during full match pipeline
333
+ emitter.error(
334
+ error instanceof Error ? error : new Error(String(error)),
335
+ "routing",
336
+ );
252
337
  callOnError(error, "routing", {
253
338
  request,
254
339
  url: ctx.url,
@@ -283,42 +368,39 @@ export function createMatchHandlers<TEnv = any>(
283
368
  );
284
369
  }
285
370
 
286
- /**
287
- * Match partial request with revalidation
288
- *
289
- * Uses generator middleware pipeline for clean separation of concerns:
290
- * - cache-lookup: Check cache first
291
- * - segment-resolution: Resolve segments on cache miss
292
- * - intercept-resolution: Handle intercept routes
293
- * - cache-store: Store results in cache
294
- * - background-revalidation: SWR revalidation
295
- */
296
371
  async function matchPartial(
297
372
  request: Request,
298
373
  context: TEnv,
299
374
  actionContext?: ActionContext,
300
375
  ): Promise<MatchResult | null> {
301
- const partialRequestId = hasTelemetry ? getRequestId(request) : undefined;
376
+ // See match() above: the PPR shell capture re-runs matchPartial() under a
377
+ // _shellCaptureRun context and must stay invisible to the sink.
378
+ const emitTelemetry =
379
+ hasTelemetry && !_getRequestContext()?._shellCaptureRun;
380
+ const partialRequestId = emitTelemetry ? getRequestId(request) : undefined;
302
381
  return runWithRouterLogContext(
303
382
  { request, transaction: "matchPartial" },
304
383
  () => {
305
384
  const routerCtx = buildRouterContext();
385
+ if (!emitTelemetry) routerCtx.telemetry = undefined;
306
386
  routerCtx.requestId = partialRequestId;
307
387
  return runWithRouterContext(routerCtx, async () =>
308
388
  withRouterLogScope("matchPartial", async () => {
309
389
  const matchStart = performance.now();
310
- const pathname = new URL(request.url).pathname;
311
- if (hasTelemetry) {
312
- safeEmit(telemetry, {
313
- type: "request.start",
314
- timestamp: matchStart,
315
- requestId: partialRequestId,
316
- method: request.method,
317
- pathname,
318
- transaction: "matchPartial",
319
- isPartial: true,
320
- });
321
- }
390
+ const pathname =
391
+ _getRequestContext()?.url?.pathname ??
392
+ new URL(request.url).pathname;
393
+ const emitter = createLifecycleEmitter({
394
+ enabled: emitTelemetry,
395
+ sink: telemetry,
396
+ requestId: partialRequestId,
397
+ method: request.method,
398
+ pathname,
399
+ transaction: "matchPartial",
400
+ isPartial: true,
401
+ matchStart,
402
+ });
403
+ emitter.start();
322
404
 
323
405
  const ctx = await createMatchContextForPartial(
324
406
  request,
@@ -326,19 +408,7 @@ export function createMatchHandlers<TEnv = any>(
326
408
  actionContext,
327
409
  );
328
410
  if (!ctx) {
329
- if (hasTelemetry) {
330
- safeEmit(telemetry, {
331
- type: "request.end",
332
- timestamp: performance.now(),
333
- requestId: partialRequestId,
334
- method: request.method,
335
- pathname,
336
- transaction: "matchPartial",
337
- durationMs: performance.now() - matchStart,
338
- segmentCount: 0,
339
- cacheHit: false,
340
- });
341
- }
411
+ emitter.end(0, false);
342
412
  return null;
343
413
  }
344
414
 
@@ -362,50 +432,28 @@ export function createMatchHandlers<TEnv = any>(
362
432
  state,
363
433
  );
364
434
  flushRevalidationTrace();
365
- if (hasTelemetry) {
366
- safeEmit(telemetry, {
367
- type: "cache.decision",
368
- timestamp: performance.now(),
369
- requestId: partialRequestId,
370
- pathname,
371
- routeKey: ctx.routeKey,
372
- hit: state.cacheHit,
373
- shouldRevalidate: !!state.shouldRevalidate,
374
- source: state.cacheSource,
375
- });
376
- safeEmit(telemetry, {
377
- type: "request.end",
378
- timestamp: performance.now(),
379
- requestId: partialRequestId,
380
- method: request.method,
381
- pathname,
382
- transaction: "matchPartial",
383
- durationMs: performance.now() - matchStart,
384
- segmentCount: matchResult.segments.length,
385
- cacheHit: state.cacheHit,
386
- });
435
+ if (hasTelemetry || cacheSignalEnabled) {
436
+ const signalSegments = buildSignal(ctx.routeKey, state);
437
+ recordSignalIfEnabled(signalSegments);
438
+ emitter.cacheDecision(ctx.routeKey, state, signalSegments);
387
439
  }
440
+ emitter.end(matchResult.segments.length, state.cacheHit);
388
441
  return matchResult;
389
442
  } catch (error) {
390
443
  flushRevalidationTrace();
391
- if (hasTelemetry) {
392
- const errorObj =
393
- error instanceof Error ? error : new Error(String(error));
394
- const phase = actionContext ? "action" : "revalidation";
395
- safeEmit(telemetry, {
396
- type: "request.error",
397
- timestamp: performance.now(),
398
- requestId: partialRequestId,
399
- method: request.method,
400
- pathname,
401
- transaction: "matchPartial",
402
- error: errorObj,
403
- phase: error instanceof Response ? "redirect" : phase,
404
- durationMs: performance.now() - matchStart,
405
- });
444
+ if (error instanceof Response) {
445
+ // A thrown Response (middleware short-circuit — redirect / auth
446
+ // gate) is a COMPLETED request, not an error: emit request.end
447
+ // (parity with match()), never request.error. Rethrow so the
448
+ // caller drives the redirect. Carry the Response's status so a
449
+ // sink can tell a 3xx short-circuit from a 2xx completion.
450
+ emitter.end(0, false, error.status);
451
+ throw error;
406
452
  }
407
- if (error instanceof Response) throw error;
408
- // Report unhandled errors during partial match pipeline
453
+ emitter.error(
454
+ error instanceof Error ? error : new Error(String(error)),
455
+ actionContext ? "action" : "revalidation",
456
+ );
409
457
  callOnError(error, actionContext ? "action" : "revalidation", {
410
458
  request,
411
459
  url: ctx.url,
@@ -434,7 +482,5 @@ export function createMatchHandlers<TEnv = any>(
434
482
  matchPartial: matchPartial,
435
483
  matchError: matchError,
436
484
  previewMatch: previewMatch,
437
- createMatchContextForFull: createMatchContextForFull,
438
- createMatchContextForPartial: createMatchContextForPartial,
439
485
  };
440
486
  }
@@ -103,7 +103,12 @@ import type { ResolvedSegment } from "../../types.js";
103
103
  import type { MatchContext, MatchPipelineState } from "../match-context.js";
104
104
  import { getRouterContext } from "../router-context.js";
105
105
  import type { GeneratorMiddleware } from "./cache-lookup.js";
106
- import { debugLog, debugWarn } from "../logging.js";
106
+ import { debugLog, debugWarn, getOrCreateRequestId } from "../logging.js";
107
+ import { INTERNAL_RANGO_DEBUG } from "../../internal-debug.js";
108
+ import {
109
+ runWithRequestContext,
110
+ type RequestContext,
111
+ } from "../../server/request-context.js";
107
112
 
108
113
  /**
109
114
  * Creates background revalidation middleware
@@ -143,8 +148,19 @@ export function withBackgroundRevalidation<TEnv>(
143
148
 
144
149
  const requestCtx = getRequestContext();
145
150
  const cacheScope = ctx.cacheScope;
151
+ const reqId = INTERNAL_RANGO_DEBUG
152
+ ? getOrCreateRequestId(ctx.request)
153
+ : undefined;
146
154
 
147
155
  requestCtx?.waitUntil(async () => {
156
+ // Prevent background metrics from polluting foreground timeline.
157
+ // The foreground uses its own metricsStore reference directly (via
158
+ // appendMetric), so nulling Store.metrics only affects track() calls
159
+ // inside this background Store.run() scope.
160
+ const savedMetrics = ctx.Store.metrics;
161
+ ctx.Store.metrics = undefined;
162
+
163
+ const start = performance.now();
148
164
  debugLog("backgroundRevalidation", "revalidating stale route", {
149
165
  pathname: ctx.pathname,
150
166
  fullMatch: ctx.isFullMatch,
@@ -156,8 +172,6 @@ export function withBackgroundRevalidation<TEnv>(
156
172
  requestCtx._handleStore = createHandleStore();
157
173
 
158
174
  try {
159
- // Create fresh handler context and loader promises to avoid
160
- // reusing memoized results from the foreground pass
161
175
  const freshHandlerContext = createHandlerContext(
162
176
  ctx.matched.params,
163
177
  ctx.request,
@@ -173,29 +187,46 @@ export function withBackgroundRevalidation<TEnv>(
173
187
  const freshLoaderPromises = new Map<string, Promise<any>>();
174
188
  setupLoaderAccess(freshHandlerContext, freshLoaderPromises);
175
189
 
176
- // Resolve all segments fresh (without revalidation logic)
177
- // to ensure complete components for caching
178
- const freshSegments = await ctx.Store.run(() =>
179
- resolveAllSegments(
180
- ctx.entries,
181
- ctx.routeKey,
182
- ctx.matched.params,
183
- freshHandlerContext,
184
- freshLoaderPromises,
185
- ),
190
+ // Re-establish the request-context ALS around the re-render. ctx.Store
191
+ // is a different ALS (DSL build context); on workerd a waitUntil task
192
+ // runs detached from the request's I/O context, so a handler/component
193
+ // that reads the ambient getRequestContext() during this background
194
+ // re-render would otherwise throw "called outside of a request context".
195
+ const freshSegments = await runWithRequestContext(
196
+ requestCtx as RequestContext<TEnv>,
197
+ () =>
198
+ ctx.Store.run(() =>
199
+ resolveAllSegments(
200
+ ctx.entries,
201
+ ctx.routeKey,
202
+ ctx.matched.params,
203
+ freshHandlerContext,
204
+ freshLoaderPromises,
205
+ { skipLoaders: true },
206
+ ),
207
+ ),
186
208
  );
187
209
 
188
- // Also resolve intercept segments fresh if applicable
189
210
  let freshInterceptSegments: ResolvedSegment[] = [];
190
211
  if (ctx.interceptResult) {
191
- freshInterceptSegments = await ctx.Store.run(() =>
192
- resolveInterceptEntry(
193
- ctx.interceptResult!.intercept,
194
- ctx.interceptResult!.entry,
195
- ctx.matched.params,
196
- freshHandlerContext,
197
- true,
198
- ),
212
+ freshInterceptSegments = await runWithRequestContext(
213
+ requestCtx as RequestContext<TEnv>,
214
+ () =>
215
+ ctx.Store.run(() =>
216
+ resolveInterceptEntry(
217
+ ctx.interceptResult!.intercept,
218
+ ctx.interceptResult!.entry,
219
+ ctx.matched.params,
220
+ freshHandlerContext,
221
+ true,
222
+ undefined,
223
+ // Skip intercept middleware: this is a post-response background
224
+ // re-render to refresh a stale cached route. The foreground
225
+ // already ran the middleware; re-running it would double its
226
+ // side effects and a short-circuit Response would abort the write.
227
+ { skipMiddleware: true },
228
+ ),
229
+ ),
199
230
  );
200
231
  }
201
232
 
@@ -207,16 +238,29 @@ export function withBackgroundRevalidation<TEnv>(
207
238
  completeSegments,
208
239
  ctx.isIntercept,
209
240
  );
241
+ if (INTERNAL_RANGO_DEBUG) {
242
+ const dur = performance.now() - start;
243
+ console.log(
244
+ `[RSC Background][req:${reqId}] SWR revalidation ${ctx.pathname} (${dur.toFixed(2)}ms) segments=${completeSegments.length}`,
245
+ );
246
+ }
210
247
  debugLog("backgroundRevalidation", "revalidation complete", {
211
248
  pathname: ctx.pathname,
212
249
  });
213
250
  } catch (error) {
251
+ if (INTERNAL_RANGO_DEBUG) {
252
+ const dur = performance.now() - start;
253
+ console.log(
254
+ `[RSC Background][req:${reqId}] SWR revalidation ${ctx.pathname} FAILED (${dur.toFixed(2)}ms) error=${String(error)}`,
255
+ );
256
+ }
214
257
  debugWarn("backgroundRevalidation", "revalidation failed", {
215
258
  pathname: ctx.pathname,
216
259
  error: String(error),
217
260
  });
218
261
  } finally {
219
262
  requestCtx._handleStore = originalHandleStore;
263
+ ctx.Store.metrics = savedMetrics;
220
264
  }
221
265
  });
222
266
  };