@rangojs/router 0.0.0-experimental.1b930379 → 0.0.0-experimental.1c0bdfad

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 (400) hide show
  1. package/AGENTS.md +12 -0
  2. package/README.md +245 -49
  3. package/dist/bin/rango.js +441 -134
  4. package/dist/testing/vitest.js +82 -0
  5. package/dist/vite/index.js +3453 -1240
  6. package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
  7. package/package.json +76 -21
  8. package/skills/api-client/SKILL.md +211 -0
  9. package/skills/breadcrumbs/SKILL.md +64 -2
  10. package/skills/bundle-analysis/SKILL.md +159 -0
  11. package/skills/cache-guide/SKILL.md +247 -23
  12. package/skills/caching/SKILL.md +318 -15
  13. package/skills/composability/SKILL.md +27 -2
  14. package/skills/css/SKILL.md +76 -0
  15. package/skills/debug-manifest/SKILL.md +4 -2
  16. package/skills/document-cache/SKILL.md +78 -55
  17. package/skills/handler-use/SKILL.md +364 -0
  18. package/skills/hooks/SKILL.md +250 -30
  19. package/skills/host-router/SKILL.md +83 -23
  20. package/skills/i18n/SKILL.md +276 -0
  21. package/skills/intercept/SKILL.md +50 -6
  22. package/skills/layout/SKILL.md +35 -9
  23. package/skills/links/SKILL.md +249 -17
  24. package/skills/loader/SKILL.md +279 -53
  25. package/skills/middleware/SKILL.md +52 -13
  26. package/skills/migrate-nextjs/SKILL.md +584 -0
  27. package/skills/migrate-react-router/SKILL.md +771 -0
  28. package/skills/mime-routes/SKILL.md +28 -1
  29. package/skills/observability/SKILL.md +172 -0
  30. package/skills/parallel/SKILL.md +203 -7
  31. package/skills/prerender/SKILL.md +153 -109
  32. package/skills/rango/SKILL.md +251 -22
  33. package/skills/react-compiler/SKILL.md +168 -0
  34. package/skills/response-routes/SKILL.md +123 -48
  35. package/skills/route/SKILL.md +101 -5
  36. package/skills/router-setup/SKILL.md +116 -8
  37. package/skills/scripts/SKILL.md +179 -0
  38. package/skills/server-actions/SKILL.md +775 -0
  39. package/skills/streams-and-websockets/SKILL.md +283 -0
  40. package/skills/tailwind/SKILL.md +27 -3
  41. package/skills/testing/SKILL.md +129 -0
  42. package/skills/testing/bindings.md +103 -0
  43. package/skills/testing/cache-prerender.md +127 -0
  44. package/skills/testing/client-components.md +124 -0
  45. package/skills/testing/e2e-parity.md +125 -0
  46. package/skills/testing/flight.md +91 -0
  47. package/skills/testing/handles.md +129 -0
  48. package/skills/testing/loader.md +128 -0
  49. package/skills/testing/middleware.md +99 -0
  50. package/skills/testing/render-handler.md +121 -0
  51. package/skills/testing/response-routes.md +95 -0
  52. package/skills/testing/reverse-and-types.md +84 -0
  53. package/skills/testing/server-actions.md +107 -0
  54. package/skills/testing/server-tree.md +128 -0
  55. package/skills/testing/setup.md +123 -0
  56. package/skills/typesafety/SKILL.md +332 -29
  57. package/skills/use-cache/SKILL.md +46 -14
  58. package/skills/view-transitions/SKILL.md +294 -0
  59. package/src/__augment-tests__/augment.ts +81 -0
  60. package/src/__augment-tests__/augmented.check.ts +116 -0
  61. package/src/__internal.ts +1 -66
  62. package/src/browser/action-coordinator.ts +53 -36
  63. package/src/browser/action-fence.ts +47 -0
  64. package/src/browser/app-shell.ts +39 -0
  65. package/src/browser/app-version.ts +14 -0
  66. package/src/browser/connection-warmup.ts +134 -0
  67. package/src/browser/cookie-name.ts +140 -0
  68. package/src/browser/event-controller.ts +197 -150
  69. package/src/browser/history-state.ts +21 -0
  70. package/src/browser/index.ts +3 -3
  71. package/src/browser/invalidate-client-cache.ts +52 -0
  72. package/src/browser/navigation-bridge.ts +111 -31
  73. package/src/browser/navigation-client.ts +201 -67
  74. package/src/browser/navigation-store-handle.ts +38 -0
  75. package/src/browser/navigation-store.ts +76 -67
  76. package/src/browser/navigation-transaction.ts +18 -66
  77. package/src/browser/network-error-handler.ts +34 -7
  78. package/src/browser/partial-update.ts +187 -112
  79. package/src/browser/prefetch/cache.ts +230 -35
  80. package/src/browser/prefetch/fetch.ts +338 -39
  81. package/src/browser/prefetch/queue.ts +126 -20
  82. package/src/browser/prefetch/resource-ready.ts +77 -0
  83. package/src/browser/rango-state.ts +158 -76
  84. package/src/browser/react/Link.tsx +111 -16
  85. package/src/browser/react/NavigationProvider.tsx +135 -120
  86. package/src/browser/react/ScrollRestoration.tsx +10 -6
  87. package/src/browser/react/context.ts +7 -2
  88. package/src/browser/react/filter-segment-order.ts +66 -7
  89. package/src/browser/react/index.ts +0 -48
  90. package/src/browser/react/location-state-shared.ts +178 -8
  91. package/src/browser/react/location-state.ts +39 -14
  92. package/src/browser/react/use-action.ts +6 -15
  93. package/src/browser/react/use-handle.ts +23 -69
  94. package/src/browser/react/use-href.tsx +8 -1
  95. package/src/browser/react/use-link-status.ts +33 -8
  96. package/src/browser/react/use-navigation.ts +32 -7
  97. package/src/browser/react/use-params.ts +20 -10
  98. package/src/browser/react/use-reverse.ts +106 -0
  99. package/src/browser/react/use-router.ts +46 -11
  100. package/src/browser/react/use-search-params.ts +0 -5
  101. package/src/browser/react/use-segments.ts +11 -21
  102. package/src/browser/response-adapter.ts +80 -5
  103. package/src/browser/rsc-router.tsx +218 -76
  104. package/src/browser/scroll-restoration.ts +54 -42
  105. package/src/browser/segment-reconciler.ts +36 -9
  106. package/src/browser/segment-structure-assert.ts +2 -2
  107. package/src/browser/server-action-bridge.ts +222 -61
  108. package/src/browser/types.ts +91 -11
  109. package/src/browser/validate-redirect-origin.ts +43 -16
  110. package/src/build/collect-fallback-refs.ts +107 -0
  111. package/src/build/generate-manifest.ts +65 -40
  112. package/src/build/generate-route-types.ts +5 -1
  113. package/src/build/index.ts +8 -2
  114. package/src/build/prefix-tree-utils.ts +123 -0
  115. package/src/build/route-trie.ts +165 -36
  116. package/src/build/route-types/ast-route-extraction.ts +15 -8
  117. package/src/build/route-types/codegen.ts +16 -5
  118. package/src/build/route-types/include-resolution.ts +125 -24
  119. package/src/build/route-types/param-extraction.ts +6 -3
  120. package/src/build/route-types/per-module-writer.ts +22 -6
  121. package/src/build/route-types/router-processing.ts +272 -96
  122. package/src/build/route-types/scan-filter.ts +9 -2
  123. package/src/build/route-types/source-scan.ts +216 -0
  124. package/src/build/runtime-discovery.ts +9 -20
  125. package/src/cache/cache-error.ts +104 -0
  126. package/src/cache/cache-key-utils.ts +29 -13
  127. package/src/cache/cache-policy.ts +108 -34
  128. package/src/cache/cache-runtime.ts +214 -48
  129. package/src/cache/cache-scope.ts +236 -89
  130. package/src/cache/cache-tag.ts +103 -0
  131. package/src/cache/cf/cf-base64.ts +33 -0
  132. package/src/cache/cf/cf-cache-constants.ts +127 -0
  133. package/src/cache/cf/cf-cache-store.ts +2224 -171
  134. package/src/cache/cf/cf-cache-types.ts +349 -0
  135. package/src/cache/cf/cf-kv-utils.ts +46 -0
  136. package/src/cache/cf/cf-tag-marker-memo.ts +105 -0
  137. package/src/cache/cf/index.ts +11 -17
  138. package/src/cache/document-cache.ts +89 -27
  139. package/src/cache/handle-snapshot.ts +70 -0
  140. package/src/cache/index.ts +11 -20
  141. package/src/cache/memory-segment-store.ts +136 -37
  142. package/src/cache/profile-registry.ts +31 -31
  143. package/src/cache/read-through-swr.ts +41 -11
  144. package/src/cache/segment-codec.ts +9 -17
  145. package/src/cache/tag-invalidation.ts +230 -0
  146. package/src/cache/taint.ts +55 -0
  147. package/src/cache/types.ts +37 -100
  148. package/src/client.rsc.tsx +44 -21
  149. package/src/client.tsx +119 -290
  150. package/src/cloudflare/index.ts +11 -0
  151. package/src/cloudflare/tracing.ts +109 -0
  152. package/src/component-utils.ts +19 -0
  153. package/src/components/DefaultDocument.tsx +8 -2
  154. package/src/context-var.ts +84 -2
  155. package/src/debug.ts +2 -2
  156. package/src/decode-loader-results.ts +52 -0
  157. package/src/defer.ts +196 -0
  158. package/src/deps/ssr.ts +0 -1
  159. package/src/encode-kv.ts +49 -0
  160. package/src/errors.ts +30 -4
  161. package/src/escape-script.ts +52 -0
  162. package/src/handle.ts +70 -22
  163. package/src/handles/MetaTags.tsx +56 -19
  164. package/src/handles/Scripts.tsx +183 -0
  165. package/src/handles/breadcrumbs.ts +37 -8
  166. package/src/handles/is-thenable.ts +19 -0
  167. package/src/handles/meta.ts +51 -40
  168. package/src/handles/script.ts +244 -0
  169. package/src/host/cookie-handler.ts +9 -60
  170. package/src/host/errors.ts +0 -24
  171. package/src/host/index.ts +8 -2
  172. package/src/host/pattern-matcher.ts +23 -52
  173. package/src/host/router.ts +107 -99
  174. package/src/host/testing.ts +40 -27
  175. package/src/host/types.ts +37 -4
  176. package/src/host/utils.ts +1 -1
  177. package/src/href-client.ts +137 -22
  178. package/src/index.rsc.ts +93 -12
  179. package/src/index.ts +133 -15
  180. package/src/internal-debug.ts +11 -10
  181. package/src/loader-store.ts +500 -0
  182. package/src/loader.rsc.ts +20 -13
  183. package/src/loader.ts +12 -11
  184. package/src/missing-id-error.ts +68 -0
  185. package/src/outlet-context.ts +1 -1
  186. package/src/outlet-provider.tsx +1 -5
  187. package/src/prerender/param-hash.ts +16 -16
  188. package/src/prerender/store.ts +37 -41
  189. package/src/prerender.ts +198 -82
  190. package/src/redirect-origin.ts +100 -0
  191. package/src/regex-escape.ts +8 -0
  192. package/src/render-error-thrower.tsx +20 -0
  193. package/src/response-utils.ts +62 -0
  194. package/src/reverse.ts +65 -15
  195. package/src/root-error-boundary.tsx +1 -19
  196. package/src/route-content-wrapper.tsx +7 -72
  197. package/src/route-definition/dsl-helpers.ts +469 -276
  198. package/src/route-definition/helper-factories.ts +29 -139
  199. package/src/route-definition/helpers-types.ts +113 -37
  200. package/src/route-definition/index.ts +3 -0
  201. package/src/route-definition/redirect.ts +53 -12
  202. package/src/route-definition/resolve-handler-use.ts +161 -0
  203. package/src/route-definition/use-item-types.ts +32 -0
  204. package/src/route-map-builder.ts +7 -17
  205. package/src/route-types.ts +37 -41
  206. package/src/router/basename.ts +14 -0
  207. package/src/router/content-negotiation.ts +164 -17
  208. package/src/router/error-handling.ts +45 -18
  209. package/src/router/find-match.ts +45 -22
  210. package/src/router/handler-context.ts +83 -39
  211. package/src/router/instrument.ts +350 -0
  212. package/src/router/intercept-resolution.ts +50 -24
  213. package/src/router/lazy-includes.ts +19 -53
  214. package/src/router/loader-resolution.ts +274 -56
  215. package/src/router/logging.ts +5 -8
  216. package/src/router/manifest.ts +49 -45
  217. package/src/router/match-api.ts +120 -204
  218. package/src/router/match-context.ts +0 -22
  219. package/src/router/match-handlers.ts +58 -58
  220. package/src/router/match-middleware/background-revalidation.ts +33 -6
  221. package/src/router/match-middleware/cache-lookup.ts +214 -263
  222. package/src/router/match-middleware/cache-store.ts +73 -33
  223. package/src/router/match-middleware/intercept-resolution.ts +8 -28
  224. package/src/router/match-middleware/segment-resolution.ts +52 -18
  225. package/src/router/match-pipelines.ts +1 -42
  226. package/src/router/match-result.ts +104 -40
  227. package/src/router/metrics.ts +5 -34
  228. package/src/router/middleware-types.ts +13 -142
  229. package/src/router/middleware.ts +270 -172
  230. package/src/router/navigation-snapshot.ts +131 -0
  231. package/src/router/params-util.ts +23 -0
  232. package/src/router/pattern-matching.ts +132 -90
  233. package/src/router/prefetch-cache-ttl.ts +51 -0
  234. package/src/router/prerender-match.ts +195 -56
  235. package/src/router/preview-match.ts +32 -102
  236. package/src/router/request-classification.ts +276 -0
  237. package/src/router/revalidation.ts +123 -73
  238. package/src/router/route-snapshot.ts +244 -0
  239. package/src/router/router-context.ts +8 -28
  240. package/src/router/router-interfaces.ts +115 -35
  241. package/src/router/router-options.ts +172 -15
  242. package/src/router/router-registry.ts +2 -5
  243. package/src/router/segment-resolution/fresh.ts +264 -77
  244. package/src/router/segment-resolution/helpers.ts +115 -30
  245. package/src/router/segment-resolution/loader-cache.ts +63 -37
  246. package/src/router/segment-resolution/revalidation.ts +474 -385
  247. package/src/router/segment-resolution/static-store.ts +19 -5
  248. package/src/router/segment-resolution/streamed-handler-telemetry.ts +52 -0
  249. package/src/router/segment-resolution/view-transition-default.ts +36 -0
  250. package/src/router/segment-resolution.ts +5 -1
  251. package/src/router/segment-wrappers.ts +8 -5
  252. package/src/router/state-cookie-name.ts +33 -0
  253. package/src/router/substitute-pattern-params.ts +56 -0
  254. package/src/router/telemetry-otel.ts +161 -199
  255. package/src/router/telemetry.ts +96 -19
  256. package/src/router/timeout.ts +0 -20
  257. package/src/router/tracing.ts +206 -0
  258. package/src/router/trie-matching.ts +163 -59
  259. package/src/router/types.ts +10 -63
  260. package/src/router/url-params.ts +44 -0
  261. package/src/router.ts +163 -55
  262. package/src/rsc/handler-context.ts +3 -2
  263. package/src/rsc/handler.ts +658 -511
  264. package/src/rsc/helpers.ts +168 -46
  265. package/src/rsc/index.ts +2 -5
  266. package/src/rsc/json-route-result.ts +38 -0
  267. package/src/rsc/loader-fetch.ts +127 -31
  268. package/src/rsc/manifest-init.ts +33 -42
  269. package/src/rsc/origin-guard.ts +39 -25
  270. package/src/rsc/progressive-enhancement.ts +77 -11
  271. package/src/rsc/redirect-guard.ts +99 -0
  272. package/src/rsc/response-cache-serve.ts +238 -0
  273. package/src/rsc/response-error.ts +79 -12
  274. package/src/rsc/response-route-handler.ts +99 -189
  275. package/src/rsc/rsc-rendering.ts +105 -72
  276. package/src/rsc/runtime-warnings.ts +23 -10
  277. package/src/rsc/server-action.ts +263 -112
  278. package/src/rsc/ssr-setup.ts +18 -2
  279. package/src/rsc/types.ts +32 -6
  280. package/src/runtime-env.ts +18 -0
  281. package/src/search-params.ts +35 -30
  282. package/src/segment-content-promise.ts +67 -0
  283. package/src/segment-loader-promise.ts +149 -0
  284. package/src/segment-system.tsx +281 -129
  285. package/src/serialize.ts +243 -0
  286. package/src/server/context.ts +309 -61
  287. package/src/server/cookie-parse.ts +32 -0
  288. package/src/server/cookie-store.ts +80 -5
  289. package/src/server/handle-store.ts +40 -38
  290. package/src/server/loader-registry.ts +26 -46
  291. package/src/server/request-context.ts +398 -172
  292. package/src/ssr/index.tsx +25 -16
  293. package/src/static-handler.ts +27 -18
  294. package/src/testing/cache-status.ts +162 -0
  295. package/src/testing/collect-handle.ts +40 -0
  296. package/src/testing/dispatch.ts +701 -0
  297. package/src/testing/dom.entry.ts +22 -0
  298. package/src/testing/e2e/fixture.ts +188 -0
  299. package/src/testing/e2e/index.ts +128 -0
  300. package/src/testing/e2e/matchers.ts +35 -0
  301. package/src/testing/e2e/page-helpers.ts +272 -0
  302. package/src/testing/e2e/parity.ts +387 -0
  303. package/src/testing/e2e/server.ts +195 -0
  304. package/src/testing/flight-matchers.ts +97 -0
  305. package/src/testing/flight-normalize.ts +11 -0
  306. package/src/testing/flight-runtime.d.ts +57 -0
  307. package/src/testing/flight-tree.ts +682 -0
  308. package/src/testing/flight.entry.ts +52 -0
  309. package/src/testing/flight.ts +257 -0
  310. package/src/testing/generated-routes.ts +183 -0
  311. package/src/testing/index.ts +99 -0
  312. package/src/testing/internal/context.ts +371 -0
  313. package/src/testing/internal/flight-client-globals.ts +30 -0
  314. package/src/testing/internal/seed-vars.ts +54 -0
  315. package/src/testing/render-handler.ts +343 -0
  316. package/src/testing/render-route.tsx +581 -0
  317. package/src/testing/run-loader.ts +385 -0
  318. package/src/testing/run-middleware.ts +205 -0
  319. package/src/testing/vitest-stubs/cloudflare-email.ts +9 -0
  320. package/src/testing/vitest-stubs/cloudflare-workers.ts +21 -0
  321. package/src/testing/vitest-stubs/plugin-rsc.ts +16 -0
  322. package/src/testing/vitest-stubs/version.ts +5 -0
  323. package/src/testing/vitest.ts +305 -0
  324. package/src/theme/ThemeProvider.tsx +20 -58
  325. package/src/theme/ThemeScript.tsx +7 -9
  326. package/src/theme/constants.ts +52 -13
  327. package/src/theme/index.ts +0 -7
  328. package/src/theme/theme-context.ts +1 -5
  329. package/src/theme/theme-script.ts +22 -21
  330. package/src/theme/use-theme.ts +0 -3
  331. package/src/types/boundaries.ts +0 -35
  332. package/src/types/cache-types.ts +17 -8
  333. package/src/types/error-types.ts +30 -90
  334. package/src/types/global-namespace.ts +54 -41
  335. package/src/types/handler-context.ts +233 -81
  336. package/src/types/index.ts +1 -10
  337. package/src/types/loader-types.ts +44 -15
  338. package/src/types/request-scope.ts +112 -0
  339. package/src/types/route-config.ts +6 -50
  340. package/src/types/route-entry.ts +19 -7
  341. package/src/types/segments.ts +37 -14
  342. package/src/urls/include-helper.ts +33 -70
  343. package/src/urls/index.ts +1 -11
  344. package/src/urls/path-helper-types.ts +58 -11
  345. package/src/urls/path-helper.ts +57 -111
  346. package/src/urls/pattern-types.ts +48 -19
  347. package/src/urls/response-types.ts +25 -22
  348. package/src/urls/type-extraction.ts +58 -139
  349. package/src/urls/urls-function.ts +1 -18
  350. package/src/use-loader.tsx +346 -89
  351. package/src/vite/debug.ts +185 -0
  352. package/src/vite/discovery/bundle-postprocess.ts +36 -38
  353. package/src/vite/discovery/discover-routers.ts +130 -85
  354. package/src/vite/discovery/discovery-errors.ts +194 -0
  355. package/src/vite/discovery/gate-state.ts +171 -0
  356. package/src/vite/discovery/prerender-collection.ts +214 -132
  357. package/src/vite/discovery/route-types-writer.ts +40 -84
  358. package/src/vite/discovery/self-gen-tracking.ts +27 -1
  359. package/src/vite/discovery/state.ts +57 -6
  360. package/src/vite/discovery/virtual-module-codegen.ts +14 -34
  361. package/src/vite/index.ts +6 -0
  362. package/src/vite/inject-client-debug.ts +36 -0
  363. package/src/vite/plugin-types.ts +155 -65
  364. package/src/vite/plugins/cjs-to-esm.ts +16 -19
  365. package/src/vite/plugins/client-ref-dedup.ts +16 -11
  366. package/src/vite/plugins/client-ref-hashing.ts +28 -15
  367. package/src/vite/plugins/cloudflare-protocol-loader-hook.d.mts +23 -0
  368. package/src/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
  369. package/src/vite/plugins/cloudflare-protocol-stub.ts +194 -0
  370. package/src/vite/plugins/expose-action-id.ts +49 -98
  371. package/src/vite/plugins/expose-id-utils.ts +96 -51
  372. package/src/vite/plugins/expose-ids/export-analysis.ts +101 -34
  373. package/src/vite/plugins/expose-ids/handler-transform.ts +15 -64
  374. package/src/vite/plugins/expose-ids/loader-transform.ts +14 -24
  375. package/src/vite/plugins/expose-ids/router-transform.ts +118 -29
  376. package/src/vite/plugins/expose-internal-ids.ts +553 -317
  377. package/src/vite/plugins/performance-tracks.ts +89 -0
  378. package/src/vite/plugins/refresh-cmd.ts +89 -27
  379. package/src/vite/plugins/use-cache-transform.ts +73 -83
  380. package/src/vite/plugins/version-injector.ts +21 -25
  381. package/src/vite/plugins/version-plugin.ts +46 -37
  382. package/src/vite/plugins/virtual-entries.ts +13 -18
  383. package/src/vite/rango.ts +238 -295
  384. package/src/vite/router-discovery.ts +940 -149
  385. package/src/vite/utils/ast-handler-extract.ts +26 -35
  386. package/src/vite/utils/banner.ts +4 -4
  387. package/src/vite/utils/bundle-analysis.ts +10 -15
  388. package/src/vite/utils/client-chunks.ts +184 -0
  389. package/src/vite/utils/directive-prologue.ts +40 -0
  390. package/src/vite/utils/forward-user-plugins.ts +171 -0
  391. package/src/vite/utils/manifest-utils.ts +4 -59
  392. package/src/vite/utils/package-resolution.ts +20 -52
  393. package/src/vite/utils/prerender-utils.ts +81 -34
  394. package/src/vite/utils/shared-utils.ts +92 -42
  395. package/src/browser/action-response-classifier.ts +0 -99
  396. package/src/browser/react/use-client-cache.ts +0 -58
  397. package/src/browser/shallow.ts +0 -40
  398. package/src/handles/index.ts +0 -7
  399. package/src/network-error-thrower.tsx +0 -23
  400. package/src/router/middleware-cookies.ts +0 -55
@@ -7,34 +7,94 @@
7
7
  */
8
8
 
9
9
  import {
10
- requireRequestContext,
10
+ getRequestContext,
11
11
  setRequestContextParams,
12
- getLocationState,
13
12
  } from "../server/request-context.js";
14
- import { resolveLocationStateEntries } from "../browser/react/location-state-shared.js";
15
13
  import { appendMetric } from "../router/metrics.js";
16
- import { getSSRSetup } from "./ssr-setup.js";
14
+ import { observePhase, PHASES } from "../router/instrument.js";
15
+ import { getSSRSetup, isRscRequest } from "./ssr-setup.js";
17
16
  import type { RscPayload } from "./types.js";
17
+ import type { MatchResult } from "../types.js";
18
18
  import {
19
19
  createResponseWithMergedHeaders,
20
20
  createSimpleRedirectResponse,
21
+ attachLocationStateIfPresent,
21
22
  } from "./helpers.js";
22
23
  import type { HandlerContext } from "./handler-context.js";
23
24
 
24
- export async function handleRscRendering<TEnv>(
25
+ export function handleRscRendering<TEnv>(
25
26
  ctx: HandlerContext<TEnv>,
26
27
  request: Request,
27
28
  env: TEnv,
28
29
  url: URL,
29
30
  isPartial: boolean,
30
- handleStore: ReturnType<typeof requireRequestContext>["_handleStore"],
31
+ handleStore: ReturnType<typeof getRequestContext>["_handleStore"],
31
32
  nonce: string | undefined,
32
33
  ): Promise<Response> {
33
- const reqCtx = requireRequestContext();
34
+ // Instrument the whole render phase once through the unified API: it records
35
+ // the "render:total" perf metric AND opens the "rango.render" span from the
36
+ // same boundary (match -> serialize -> SSR), so the two surfaces agree.
37
+ // Loaders kicked off during matching nest under the span; the SSR HTML pass
38
+ // below opens "rango.ssr" the same way.
39
+ return observePhase(PHASES.render, () =>
40
+ handleRscRenderingInner(
41
+ ctx,
42
+ request,
43
+ env,
44
+ url,
45
+ isPartial,
46
+ handleStore,
47
+ nonce,
48
+ ),
49
+ );
50
+ }
51
+
52
+ async function handleRscRenderingInner<TEnv>(
53
+ ctx: HandlerContext<TEnv>,
54
+ request: Request,
55
+ env: TEnv,
56
+ url: URL,
57
+ isPartial: boolean,
58
+ handleStore: ReturnType<typeof getRequestContext>["_handleStore"],
59
+ nonce: string | undefined,
60
+ ): Promise<Response> {
61
+ const reqCtx = getRequestContext();
34
62
 
35
63
  let payload: RscPayload;
36
64
  let hasInterceptSlots = false;
37
65
 
66
+ // Shared by the partial-fallback and full-render paths. The partial-success
67
+ // payload below is intentionally different (omits rootLayout/theme, adds slots).
68
+ const buildFullPayload = (m: MatchResult): RscPayload => ({
69
+ metadata: {
70
+ pathname: url.pathname,
71
+ routerId: ctx.router.id,
72
+ basename: ctx.router.basename,
73
+ segments: m.segments,
74
+ matched: m.matched,
75
+ diff: m.diff,
76
+ resolvedIds: m.resolvedIds,
77
+ params: m.params,
78
+ isPartial: false,
79
+ rootLayout: ctx.router.rootLayout,
80
+ handles: handleStore.stream(),
81
+ version: ctx.version,
82
+ prefetchCacheTTL: ctx.router.prefetchCacheTTL,
83
+ stateCookieName: ctx.router.resolvedStateCookieName,
84
+ themeConfig: ctx.router.themeConfig,
85
+ // Carry warmupEnabled on the initial full-render payload so the client
86
+ // respects warmup:false from first load. The 404 and PE payloads already
87
+ // include it; without it here warmup could never be disabled on the
88
+ // normal full-load path (partial payloads omit it by design).
89
+ warmupEnabled: ctx.router.warmupEnabled,
90
+ // Carry strictMode on the initial full-render payload so the browser
91
+ // entry knows whether to wrap hydration in React.StrictMode. Partial
92
+ // (navigation) payloads omit it by design; StrictMode is decided once.
93
+ strictMode: ctx.router.strictMode,
94
+ initialTheme: reqCtx.theme,
95
+ },
96
+ });
97
+
38
98
  if (isPartial) {
39
99
  // Partial render (navigation)
40
100
  const result = await ctx.router.matchPartial(request, { env });
@@ -51,22 +111,7 @@ export async function handleRscRendering<TEnv>(
51
111
  return createSimpleRedirectResponse(match.redirect);
52
112
  }
53
113
 
54
- payload = {
55
- metadata: {
56
- pathname: url.pathname,
57
- segments: match.segments,
58
- matched: match.matched,
59
- diff: match.diff,
60
- params: match.params,
61
- isPartial: false,
62
- rootLayout: ctx.router.rootLayout,
63
- handles: handleStore.stream(),
64
- version: ctx.version,
65
- prefetchCacheTTL: ctx.router.prefetchCacheTTL,
66
- themeConfig: ctx.router.themeConfig,
67
- initialTheme: reqCtx.theme,
68
- },
69
- };
114
+ payload = buildFullPayload(match);
70
115
  } else {
71
116
  setRequestContextParams(result.params, result.routeName);
72
117
 
@@ -75,15 +120,23 @@ export async function handleRscRendering<TEnv>(
75
120
  payload = {
76
121
  metadata: {
77
122
  pathname: url.pathname,
123
+ // routerId is serialized on every payload (including within-session
124
+ // ones) so the frontend can read the current app/router identity. It
125
+ // always equals the current app's id: a cross-app navigation is
126
+ // intercepted server-side (X-RSC-Reload) and never delivers a
127
+ // different-router payload to the client.
128
+ routerId: ctx.router.id,
78
129
  segments: result.segments,
79
130
  matched: result.matched,
80
131
  diff: result.diff,
132
+ resolvedIds: result.resolvedIds,
81
133
  params: result.params,
82
134
  isPartial: true,
83
135
  slots: result.slots,
84
136
  handles: handleStore.stream(),
85
137
  version: ctx.version,
86
138
  prefetchCacheTTL: ctx.router.prefetchCacheTTL,
139
+ stateCookieName: ctx.router.resolvedStateCookieName,
87
140
  },
88
141
  };
89
142
  }
@@ -130,25 +183,7 @@ export async function handleRscRendering<TEnv>(
130
183
  { headers: { "Content-Type": "application/json" } },
131
184
  );
132
185
  } else {
133
- payload = {
134
- // Initial SSR can reconstruct the tree from segments + rootLayout,
135
- // so we omit root to avoid sending the same structure twice.
136
-
137
- metadata: {
138
- pathname: url.pathname,
139
- segments: match.segments,
140
- matched: match.matched,
141
- diff: match.diff,
142
- params: match.params,
143
- isPartial: false,
144
- rootLayout: ctx.router.rootLayout,
145
- handles: handleStore.stream(),
146
- version: ctx.version,
147
- prefetchCacheTTL: ctx.router.prefetchCacheTTL,
148
- themeConfig: ctx.router.themeConfig,
149
- initialTheme: reqCtx.theme,
150
- },
151
- };
186
+ payload = buildFullPayload(match);
152
187
  }
153
188
  }
154
189
 
@@ -156,19 +191,18 @@ export async function handleRscRendering<TEnv>(
156
191
  // SSR (full page) requests ignore location state since there's no history.state
157
192
  // to write to on a fresh page load.
158
193
  if (isPartial && payload.metadata) {
159
- const locationState = getLocationState();
160
- if (locationState) {
161
- payload.metadata.locationState =
162
- resolveLocationStateEntries(locationState);
163
- }
194
+ attachLocationStateIfPresent(payload);
164
195
  }
165
196
 
166
197
  const metricsStore = reqCtx._metricsStore;
167
- const renderStart = performance.now();
168
198
 
169
199
  // Serialize to RSC stream
170
200
  const rscSerializeStart = performance.now();
171
- const rscStream = ctx.renderToReadableStream<RscPayload>(payload);
201
+ const rscStream = ctx.renderToReadableStream<RscPayload>(payload, {
202
+ onError: (error: unknown) => {
203
+ ctx.callOnError(error, "rendering", { request, url, env });
204
+ },
205
+ });
172
206
  const rscSerializeDur = performance.now() - rscSerializeStart;
173
207
  // This measures synchronous stream creation, not end-to-end stream consumption.
174
208
  appendMetric(
@@ -178,23 +212,24 @@ export async function handleRscRendering<TEnv>(
178
212
  rscSerializeDur,
179
213
  );
180
214
 
181
- // Determine if this is an RSC request or HTML request.
182
- // Partial requests (_rsc_partial) are always RSC -- they come from client-side
183
- // navigation or prefetch fetch(). We cannot rely on Accept alone since some
184
- // browsers may send Accept: text/html for non-HTML requests.
185
- const isRscRequest =
186
- isPartial ||
187
- (!request.headers.get("accept")?.includes("text/html") &&
188
- !url.searchParams.has("__html")) ||
189
- url.searchParams.has("__rsc");
190
-
191
- if (isRscRequest) {
192
- const renderDur = performance.now() - renderStart;
193
- appendMetric(metricsStore, "render:total", renderStart, renderDur);
215
+ if (isRscRequest(request, url, isPartial)) {
216
+ // render:total is recorded by the observePhase wrapper around this function.
194
217
  const rscHeaders: Record<string, string> = {
195
218
  "content-type": "text/x-component;charset=utf-8",
196
219
  vary: "accept, X-Rango-State, X-RSC-Router-Client-Path",
220
+ // Router identity, so the client can verify pre-decode (before importing
221
+ // chunks) that this content payload belongs to its app and refuse a
222
+ // foreign one (cache/proxy/bug). Control-only reload/redirect responses
223
+ // are deliberately NOT stamped. See browser/response-adapter.ts.
224
+ "X-RSC-Router-Id": ctx.router.id,
197
225
  };
226
+ // Tell the client's prefetch cache to scope this response to its source
227
+ // URL (instead of the default source-agnostic wildcard). Intercept
228
+ // responses depend on the source page matching an intercept rule, so
229
+ // they must not be reused for navigations from other sources.
230
+ if (hasInterceptSlots) {
231
+ rscHeaders["x-rsc-prefetch-scope"] = "source";
232
+ }
198
233
  // Enable browser HTTP caching for prefetch responses only.
199
234
  // Requires X-Rango-Prefetch header (sent by Link prefetch fetch),
200
235
  // non-intercept context (intercept responses depend on source page),
@@ -220,16 +255,14 @@ export async function handleRscRendering<TEnv>(
220
255
  metricsStore,
221
256
  );
222
257
 
223
- const ssrRenderStart = performance.now();
224
- const htmlStream = await ssrModule.renderHTML(rscStream, {
225
- nonce,
226
- streamMode,
227
- });
228
- const ssrRenderDur = performance.now() - ssrRenderStart;
229
- appendMetric(metricsStore, "ssr-render-html", ssrRenderStart, ssrRenderDur);
230
-
231
- const renderDur = performance.now() - renderStart;
232
- appendMetric(metricsStore, "render:total", renderStart, renderDur);
258
+ // ssr-render-html metric + rango.ssr span from one boundary. render:total is
259
+ // recorded by the observePhase wrapper around this function.
260
+ const htmlStream = await observePhase(PHASES.ssr, () =>
261
+ ssrModule.renderHTML(rscStream, {
262
+ nonce,
263
+ streamMode,
264
+ }),
265
+ );
233
266
 
234
267
  return createResponseWithMergedHeaders(htmlStream, {
235
268
  headers: { "content-type": "text/html;charset=utf-8" },
@@ -8,6 +8,7 @@ import {
8
8
  createResponseWithMergedHeaders,
9
9
  carryOverRedirectHeaders,
10
10
  } from "./helpers.js";
11
+ import { isRedirectResponse } from "../response-utils.js";
11
12
 
12
13
  // W3 -----------------------------------------------------------------------
13
14
 
@@ -18,16 +19,14 @@ import {
18
19
  */
19
20
  export function extractRedirectResponse(value: unknown): Response | null {
20
21
  if (!(value instanceof Response)) return null;
21
- const location = value.headers.get("Location");
22
- if (value.status >= 300 && value.status < 400 && location) {
23
- const redirect = createResponseWithMergedHeaders(null, {
24
- status: value.status,
25
- headers: { Location: location },
26
- });
27
- carryOverRedirectHeaders(value, redirect);
28
- return redirect;
29
- }
30
- return null;
22
+ if (!isRedirectResponse(value)) return null;
23
+ const location = value.headers.get("Location")!;
24
+ const redirect = createResponseWithMergedHeaders(null, {
25
+ status: value.status,
26
+ headers: { Location: location },
27
+ });
28
+ carryOverRedirectHeaders(value, redirect);
29
+ return redirect;
31
30
  }
32
31
 
33
32
  /**
@@ -40,3 +39,17 @@ export function warnNonRedirectPeResponse(): void {
40
39
  `ignored — the page will re-render at the current URL instead.`,
41
40
  );
42
41
  }
42
+
43
+ /**
44
+ * Warn when a non-redirect Response is returned (not thrown) from an action
45
+ * on the JS (fetch) path. A raw Response cannot be serialized into Flight, so
46
+ * it is discarded — mirroring the PE path. Use `throw redirect('/path')` for
47
+ * redirects.
48
+ */
49
+ export function warnNonRedirectActionResponse(actionId: string): void {
50
+ console.warn(
51
+ `[@rangojs/router] Server action "${actionId}" returned a Response ` +
52
+ `that is not a redirect. Non-redirect Responses cannot be serialized ` +
53
+ `and are ignored. Use \`throw redirect('/path')\` for redirects.`,
54
+ );
55
+ }