@rangojs/router 0.0.0-experimental.14 → 0.0.0-experimental.140

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 (460) hide show
  1. package/AGENTS.md +17 -0
  2. package/README.md +432 -7
  3. package/dist/bin/rango.js +2073 -213
  4. package/dist/testing/vitest.js +82 -0
  5. package/dist/vite/index.js +7258 -2714
  6. package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
  7. package/package.json +140 -67
  8. package/skills/api-client/SKILL.md +211 -0
  9. package/skills/breadcrumbs/SKILL.md +329 -0
  10. package/skills/bundle-analysis/SKILL.md +159 -0
  11. package/skills/cache-guide/SKILL.md +487 -0
  12. package/skills/caching/SKILL.md +357 -25
  13. package/skills/comparison/SKILL.md +50 -0
  14. package/skills/comparison/agents/openai.yaml +4 -0
  15. package/skills/comparison/references/framework-comparison.md +837 -0
  16. package/skills/composability/SKILL.md +246 -0
  17. package/skills/css/SKILL.md +76 -0
  18. package/skills/debug-manifest/SKILL.md +16 -10
  19. package/skills/document-cache/SKILL.md +87 -62
  20. package/skills/fonts/SKILL.md +6 -4
  21. package/skills/handler-use/SKILL.md +364 -0
  22. package/skills/hooks/SKILL.md +557 -79
  23. package/skills/host-router/SKILL.md +320 -0
  24. package/skills/i18n/SKILL.md +276 -0
  25. package/skills/intercept/SKILL.md +207 -15
  26. package/skills/layout/SKILL.md +146 -6
  27. package/skills/links/SKILL.md +304 -25
  28. package/skills/loader/SKILL.md +616 -54
  29. package/skills/middleware/SKILL.md +217 -37
  30. package/skills/migrate-nextjs/SKILL.md +611 -0
  31. package/skills/migrate-react-router/SKILL.md +927 -0
  32. package/skills/mime-routes/SKILL.md +42 -11
  33. package/skills/observability/SKILL.md +194 -0
  34. package/skills/parallel/SKILL.md +284 -3
  35. package/skills/ppr/SKILL.md +426 -0
  36. package/skills/prerender/SKILL.md +437 -52
  37. package/skills/rango/SKILL.md +369 -22
  38. package/skills/react-compiler/SKILL.md +168 -0
  39. package/skills/response-routes/SKILL.md +263 -121
  40. package/skills/route/SKILL.md +350 -21
  41. package/skills/router-setup/SKILL.md +246 -33
  42. package/skills/scripts/SKILL.md +179 -0
  43. package/skills/server-actions/SKILL.md +775 -0
  44. package/skills/shell-manifest/SKILL.md +185 -0
  45. package/skills/streams-and-websockets/SKILL.md +283 -0
  46. package/skills/tailwind/SKILL.md +27 -3
  47. package/skills/testing/SKILL.md +126 -222
  48. package/skills/testing/bindings.md +103 -0
  49. package/skills/testing/cache-prerender.md +127 -0
  50. package/skills/testing/client-components.md +124 -0
  51. package/skills/testing/e2e-parity.md +125 -0
  52. package/skills/testing/flight.md +91 -0
  53. package/skills/testing/handles.md +131 -0
  54. package/skills/testing/loader.md +128 -0
  55. package/skills/testing/middleware.md +99 -0
  56. package/skills/testing/render-handler.md +122 -0
  57. package/skills/testing/response-routes.md +95 -0
  58. package/skills/testing/reverse-and-types.md +85 -0
  59. package/skills/testing/server-actions.md +107 -0
  60. package/skills/testing/server-tree.md +128 -0
  61. package/skills/testing/setup.md +123 -0
  62. package/skills/theme/SKILL.md +9 -8
  63. package/skills/typesafety/SKILL.md +532 -103
  64. package/skills/use-cache/SKILL.md +367 -0
  65. package/skills/vercel/SKILL.md +128 -0
  66. package/skills/view-transitions/SKILL.md +337 -0
  67. package/src/__augment-tests__/augment.ts +81 -0
  68. package/src/__augment-tests__/augmented.check.ts +116 -0
  69. package/src/__internal.ts +77 -44
  70. package/src/bin/rango.ts +312 -15
  71. package/src/browser/action-coordinator.ts +114 -0
  72. package/src/browser/action-fence.ts +47 -0
  73. package/src/browser/app-shell.ts +39 -0
  74. package/src/browser/app-version.ts +14 -0
  75. package/src/browser/connection-warmup.ts +134 -0
  76. package/src/browser/cookie-name.ts +140 -0
  77. package/src/browser/event-controller.ts +293 -202
  78. package/src/browser/history-state.ts +101 -0
  79. package/src/browser/index.ts +3 -3
  80. package/src/browser/intercept-utils.ts +52 -0
  81. package/src/browser/invalidate-client-cache.ts +52 -0
  82. package/src/browser/link-interceptor.ts +24 -4
  83. package/src/browser/logging.ts +11 -0
  84. package/src/browser/merge-segment-loaders.ts +20 -12
  85. package/src/browser/navigation-bridge.ts +385 -576
  86. package/src/browser/navigation-client.ts +245 -75
  87. package/src/browser/navigation-store-handle.ts +38 -0
  88. package/src/browser/navigation-store.ts +184 -118
  89. package/src/browser/navigation-transaction.ts +247 -0
  90. package/src/browser/network-error-handler.ts +88 -0
  91. package/src/browser/partial-update.ts +412 -364
  92. package/src/browser/prefetch/cache.ts +359 -0
  93. package/src/browser/prefetch/fetch.ts +452 -0
  94. package/src/browser/prefetch/observer.ts +65 -0
  95. package/src/browser/prefetch/policy.ts +48 -0
  96. package/src/browser/prefetch/queue.ts +209 -0
  97. package/src/browser/prefetch/resource-ready.ts +77 -0
  98. package/src/browser/rango-state.ts +194 -0
  99. package/src/browser/react/Link.tsx +275 -68
  100. package/src/browser/react/NavigationProvider.tsx +265 -109
  101. package/src/browser/react/ScrollRestoration.tsx +10 -6
  102. package/src/browser/react/context.ts +11 -0
  103. package/src/browser/react/filter-segment-order.ts +70 -0
  104. package/src/browser/react/index.ts +0 -48
  105. package/src/browser/react/location-state-shared.ts +272 -60
  106. package/src/browser/react/location-state.ts +90 -20
  107. package/src/browser/react/mount-context.ts +6 -1
  108. package/src/browser/react/nonce-context.ts +23 -0
  109. package/src/browser/react/shallow-equal.ts +27 -0
  110. package/src/browser/react/use-action.ts +35 -66
  111. package/src/browser/react/use-handle.ts +39 -126
  112. package/src/browser/react/use-href.tsx +8 -1
  113. package/src/browser/react/use-link-status.ts +39 -13
  114. package/src/browser/react/use-navigation.ts +53 -69
  115. package/src/browser/react/use-params.ts +75 -0
  116. package/src/browser/react/use-pathname.ts +47 -0
  117. package/src/browser/react/use-reverse.ts +106 -0
  118. package/src/browser/react/use-router.ts +98 -0
  119. package/src/browser/react/use-search-params.ts +51 -0
  120. package/src/browser/react/use-segments.ts +72 -99
  121. package/src/browser/response-adapter.ts +164 -0
  122. package/src/browser/rsc-router.tsx +300 -72
  123. package/src/browser/scroll-restoration.ts +138 -50
  124. package/src/browser/segment-reconciler.ts +243 -0
  125. package/src/browser/segment-structure-assert.ts +17 -1
  126. package/src/browser/server-action-bridge.ts +668 -613
  127. package/src/browser/types.ts +223 -51
  128. package/src/browser/validate-redirect-origin.ts +56 -0
  129. package/src/build/collect-fallback-refs.ts +107 -0
  130. package/src/build/generate-manifest.ts +252 -161
  131. package/src/build/generate-route-types.ts +41 -1038
  132. package/src/build/index.ts +12 -7
  133. package/src/build/prefix-tree-utils.ts +123 -0
  134. package/src/build/route-trie.ts +225 -42
  135. package/src/build/route-types/ast-helpers.ts +25 -0
  136. package/src/build/route-types/ast-route-extraction.ts +105 -0
  137. package/src/build/route-types/codegen.ts +113 -0
  138. package/src/build/route-types/include-resolution.ts +812 -0
  139. package/src/build/route-types/param-extraction.ts +51 -0
  140. package/src/build/route-types/per-module-writer.ts +144 -0
  141. package/src/build/route-types/router-processing.ts +695 -0
  142. package/src/build/route-types/scan-filter.ts +85 -0
  143. package/src/build/route-types/source-scan.ts +216 -0
  144. package/src/build/runtime-discovery.ts +223 -0
  145. package/src/cache/background-task.ts +34 -0
  146. package/src/cache/cache-error.ts +104 -0
  147. package/src/cache/cache-key-utils.ts +60 -0
  148. package/src/cache/cache-policy.ts +199 -0
  149. package/src/cache/cache-runtime.ts +525 -0
  150. package/src/cache/cache-scope.ts +298 -332
  151. package/src/cache/cache-tag.ts +103 -0
  152. package/src/cache/cf/cf-base64.ts +33 -0
  153. package/src/cache/cf/cf-cache-constants.ts +127 -0
  154. package/src/cache/cf/cf-cache-store.ts +2500 -158
  155. package/src/cache/cf/cf-cache-types.ts +349 -0
  156. package/src/cache/cf/cf-kv-utils.ts +46 -0
  157. package/src/cache/cf/cf-tag-marker-memo.ts +105 -0
  158. package/src/cache/cf/index.ts +17 -17
  159. package/src/cache/document-cache.ts +199 -92
  160. package/src/cache/handle-capture.ts +81 -0
  161. package/src/cache/handle-snapshot.ts +111 -0
  162. package/src/cache/index.ts +29 -35
  163. package/src/cache/memory-segment-store.ts +363 -30
  164. package/src/cache/profile-registry.ts +88 -0
  165. package/src/cache/read-through-swr.ts +178 -0
  166. package/src/cache/segment-codec.ts +248 -0
  167. package/src/cache/shell-cache.ts +386 -0
  168. package/src/cache/tag-invalidation.ts +230 -0
  169. package/src/cache/taint.ts +153 -0
  170. package/src/cache/types.ts +156 -211
  171. package/src/cache/vercel/index.ts +11 -0
  172. package/src/cache/vercel/vercel-cache-store.ts +1102 -0
  173. package/src/client.rsc.tsx +43 -21
  174. package/src/client.tsx +131 -347
  175. package/src/cloudflare/index.ts +11 -0
  176. package/src/cloudflare/tracing.ts +109 -0
  177. package/src/component-utils.ts +23 -4
  178. package/src/components/DefaultDocument.tsx +13 -3
  179. package/src/context-var.ts +168 -0
  180. package/src/debug.ts +19 -9
  181. package/src/decode-loader-results.ts +52 -0
  182. package/src/defer.ts +185 -0
  183. package/src/deps/ssr.ts +0 -1
  184. package/src/encode-kv.ts +49 -0
  185. package/src/errors.ts +106 -10
  186. package/src/escape-script.ts +52 -0
  187. package/src/handle.ts +110 -35
  188. package/src/handles/MetaTags.tsx +83 -59
  189. package/src/handles/Scripts.tsx +183 -0
  190. package/src/handles/breadcrumbs.ts +93 -0
  191. package/src/handles/deferred-resolution.ts +127 -0
  192. package/src/handles/is-thenable.ts +18 -0
  193. package/src/handles/meta.ts +44 -53
  194. package/src/handles/script.ts +244 -0
  195. package/src/host/cookie-handler.ts +20 -65
  196. package/src/host/errors.ts +21 -30
  197. package/src/host/index.ts +13 -9
  198. package/src/host/pattern-matcher.ts +50 -79
  199. package/src/host/router.ts +151 -121
  200. package/src/host/testing.ts +45 -32
  201. package/src/host/types.ts +52 -11
  202. package/src/host/utils.ts +2 -2
  203. package/src/href-client.ts +192 -57
  204. package/src/index.rsc.ts +177 -35
  205. package/src/index.ts +255 -71
  206. package/src/internal-debug.ts +9 -2
  207. package/src/loader-store.ts +500 -0
  208. package/src/loader.rsc.ts +31 -99
  209. package/src/loader.ts +30 -12
  210. package/src/missing-id-error.ts +68 -0
  211. package/src/outlet-context.ts +1 -1
  212. package/src/outlet-provider.tsx +41 -0
  213. package/src/prerender/param-hash.ts +16 -14
  214. package/src/prerender/store.ts +121 -21
  215. package/src/prerender.ts +460 -26
  216. package/src/redirect-origin.ts +100 -0
  217. package/src/regex-escape.ts +8 -0
  218. package/src/render-error-thrower.tsx +20 -0
  219. package/src/response-utils.ts +62 -0
  220. package/src/reverse.ts +198 -128
  221. package/src/root-error-boundary.tsx +42 -48
  222. package/src/route-content-wrapper.tsx +22 -77
  223. package/src/route-definition/dsl-helpers.ts +1116 -0
  224. package/src/route-definition/helper-factories.ts +88 -0
  225. package/src/route-definition/helpers-types.ts +505 -0
  226. package/src/route-definition/index.ts +54 -0
  227. package/src/route-definition/redirect.ts +134 -0
  228. package/src/route-definition/resolve-handler-use.ts +160 -0
  229. package/src/route-definition/use-item-types.ts +29 -0
  230. package/src/route-definition.ts +1 -1481
  231. package/src/route-map-builder.ts +82 -144
  232. package/src/route-name.ts +53 -0
  233. package/src/route-types.ts +71 -45
  234. package/src/router/basename.ts +14 -0
  235. package/src/router/content-negotiation.ts +263 -0
  236. package/src/router/debug-manifest.ts +72 -0
  237. package/src/router/error-handling.ts +54 -27
  238. package/src/router/find-match.ts +245 -0
  239. package/src/router/handler-context.ts +377 -125
  240. package/src/router/instrument.ts +350 -0
  241. package/src/router/intercept-resolution.ts +59 -28
  242. package/src/router/lazy-includes.ts +254 -0
  243. package/src/router/loader-resolution.ts +421 -157
  244. package/src/router/logging.ts +106 -6
  245. package/src/router/manifest.ts +131 -57
  246. package/src/router/match-api.ts +167 -246
  247. package/src/router/match-context.ts +4 -24
  248. package/src/router/match-handlers.ts +440 -0
  249. package/src/router/match-middleware/background-revalidation.ts +117 -93
  250. package/src/router/match-middleware/cache-lookup.ts +297 -150
  251. package/src/router/match-middleware/cache-store.ts +123 -51
  252. package/src/router/match-middleware/intercept-resolution.ts +44 -43
  253. package/src/router/match-middleware/segment-resolution.ts +64 -22
  254. package/src/router/match-pipelines.ts +11 -87
  255. package/src/router/match-result.ts +121 -50
  256. package/src/router/metrics.ts +219 -28
  257. package/src/router/middleware-types.ts +93 -0
  258. package/src/router/middleware.ts +505 -441
  259. package/src/router/navigation-snapshot.ts +133 -0
  260. package/src/router/params-util.ts +23 -0
  261. package/src/router/parse-pattern.ts +115 -0
  262. package/src/router/pattern-matching.ts +311 -142
  263. package/src/router/prefetch-cache-ttl.ts +51 -0
  264. package/src/router/prefetch-limits.ts +37 -0
  265. package/src/router/prerender-match.ts +547 -0
  266. package/src/router/preview-match.ts +102 -0
  267. package/src/router/request-classification.ts +278 -0
  268. package/src/router/revalidation.ts +203 -62
  269. package/src/router/route-snapshot.ts +246 -0
  270. package/src/router/router-context.ts +45 -48
  271. package/src/router/router-interfaces.ts +554 -0
  272. package/src/router/router-options.ts +779 -0
  273. package/src/router/router-registry.ts +21 -0
  274. package/src/router/segment-resolution/fresh.ts +772 -0
  275. package/src/router/segment-resolution/helpers.ts +348 -0
  276. package/src/router/segment-resolution/loader-cache.ts +250 -0
  277. package/src/router/segment-resolution/loader-mask.ts +44 -0
  278. package/src/router/segment-resolution/revalidation.ts +1331 -0
  279. package/src/router/segment-resolution/static-store.ts +81 -0
  280. package/src/router/segment-resolution/streamed-handler-telemetry.ts +52 -0
  281. package/src/router/segment-resolution/view-transition-default.ts +56 -0
  282. package/src/router/segment-resolution.ts +25 -1354
  283. package/src/router/segment-wrappers.ts +292 -0
  284. package/src/router/state-cookie-name.ts +33 -0
  285. package/src/router/substitute-pattern-params.ts +75 -0
  286. package/src/router/telemetry-otel.ts +261 -0
  287. package/src/router/telemetry.ts +377 -0
  288. package/src/router/timeout.ts +128 -0
  289. package/src/router/tracing.ts +206 -0
  290. package/src/router/trie-matching.ts +240 -61
  291. package/src/router/types.ts +23 -70
  292. package/src/router/url-params.ts +57 -0
  293. package/src/router.ts +781 -2378
  294. package/src/rsc/full-payload.ts +70 -0
  295. package/src/rsc/handler-context.ts +46 -0
  296. package/src/rsc/handler.ts +905 -1142
  297. package/src/rsc/helpers.ts +275 -19
  298. package/src/rsc/index.ts +2 -25
  299. package/src/rsc/json-route-result.ts +38 -0
  300. package/src/rsc/loader-fetch.ts +305 -0
  301. package/src/rsc/manifest-init.ts +77 -0
  302. package/src/rsc/nonce.ts +14 -0
  303. package/src/rsc/origin-guard.ts +155 -0
  304. package/src/rsc/progressive-enhancement.ts +502 -0
  305. package/src/rsc/redirect-guard.ts +99 -0
  306. package/src/rsc/response-cache-serve.ts +238 -0
  307. package/src/rsc/response-error.ts +104 -0
  308. package/src/rsc/response-route-handler.ts +257 -0
  309. package/src/rsc/rsc-rendering.ts +337 -0
  310. package/src/rsc/runtime-warnings.ts +55 -0
  311. package/src/rsc/server-action.ts +522 -0
  312. package/src/rsc/shell-capture.ts +439 -0
  313. package/src/rsc/ssr-setup.ts +144 -0
  314. package/src/rsc/transition-gate.ts +89 -0
  315. package/src/rsc/types.ts +95 -12
  316. package/src/runtime-env.ts +18 -0
  317. package/src/search-params.ts +99 -82
  318. package/src/segment-content-promise.ts +67 -0
  319. package/src/segment-loader-promise.ts +149 -0
  320. package/src/segment-system.tsx +349 -134
  321. package/src/serialize.ts +243 -0
  322. package/src/server/context.ts +452 -85
  323. package/src/server/cookie-parse.ts +32 -0
  324. package/src/server/cookie-store.ts +310 -0
  325. package/src/server/fetchable-loader-store.ts +11 -6
  326. package/src/server/handle-store.ts +123 -42
  327. package/src/server/live.ts +130 -0
  328. package/src/server/loader-registry.ts +51 -100
  329. package/src/server/request-context.ts +842 -157
  330. package/src/server.ts +15 -8
  331. package/src/ssr/index.tsx +412 -136
  332. package/src/ssr/ssr-root.tsx +228 -0
  333. package/src/static-handler.ts +45 -18
  334. package/src/testing/cache-status.ts +162 -0
  335. package/src/testing/collect-handle.ts +46 -0
  336. package/src/testing/dispatch.ts +701 -0
  337. package/src/testing/dom.entry.ts +22 -0
  338. package/src/testing/e2e/fixture.ts +188 -0
  339. package/src/testing/e2e/index.ts +128 -0
  340. package/src/testing/e2e/matchers.ts +35 -0
  341. package/src/testing/e2e/page-helpers.ts +272 -0
  342. package/src/testing/e2e/parity.ts +387 -0
  343. package/src/testing/e2e/server.ts +195 -0
  344. package/src/testing/flight-matchers.ts +97 -0
  345. package/src/testing/flight-normalize.ts +11 -0
  346. package/src/testing/flight-runtime.d.ts +57 -0
  347. package/src/testing/flight-tree.ts +682 -0
  348. package/src/testing/flight.entry.ts +52 -0
  349. package/src/testing/flight.ts +257 -0
  350. package/src/testing/generated-routes.ts +199 -0
  351. package/src/testing/index.ts +105 -0
  352. package/src/testing/internal/context.ts +371 -0
  353. package/src/testing/internal/flight-client-globals.ts +30 -0
  354. package/src/testing/internal/seed-vars.ts +54 -0
  355. package/src/testing/render-handler.ts +357 -0
  356. package/src/testing/render-route.tsx +584 -0
  357. package/src/testing/run-loader.ts +385 -0
  358. package/src/testing/run-middleware.ts +205 -0
  359. package/src/testing/run-transition-when.ts +164 -0
  360. package/src/testing/vitest-stubs/cloudflare-email.ts +9 -0
  361. package/src/testing/vitest-stubs/cloudflare-workers.ts +21 -0
  362. package/src/testing/vitest-stubs/plugin-rsc.ts +16 -0
  363. package/src/testing/vitest-stubs/version.ts +5 -0
  364. package/src/testing/vitest.ts +305 -0
  365. package/src/theme/ThemeProvider.tsx +40 -72
  366. package/src/theme/ThemeScript.tsx +12 -14
  367. package/src/theme/constants.ts +57 -15
  368. package/src/theme/index.ts +3 -20
  369. package/src/theme/theme-context.ts +5 -35
  370. package/src/theme/theme-script.ts +43 -39
  371. package/src/theme/use-theme.ts +0 -3
  372. package/src/types/boundaries.ts +123 -0
  373. package/src/types/cache-types.ts +207 -0
  374. package/src/types/error-types.ts +132 -0
  375. package/src/types/global-namespace.ts +113 -0
  376. package/src/types/handler-context.ts +839 -0
  377. package/src/types/index.ts +81 -0
  378. package/src/types/loader-types.ts +212 -0
  379. package/src/types/request-scope.ts +112 -0
  380. package/src/types/route-config.ts +138 -0
  381. package/src/types/route-entry.ts +114 -0
  382. package/src/types/segments.ts +271 -0
  383. package/src/types.ts +1 -1795
  384. package/src/urls/include-helper.ts +162 -0
  385. package/src/urls/include-provider.ts +71 -0
  386. package/src/urls/index.ts +43 -0
  387. package/src/urls/path-helper-types.ts +413 -0
  388. package/src/urls/path-helper.ts +275 -0
  389. package/src/urls/pattern-types.ts +124 -0
  390. package/src/urls/response-types.ts +109 -0
  391. package/src/urls/type-extraction.ts +316 -0
  392. package/src/urls/urls-function.ts +80 -0
  393. package/src/urls.ts +1 -1341
  394. package/src/use-loader.tsx +406 -141
  395. package/src/vercel/index.ts +11 -0
  396. package/src/vercel/tracing.ts +88 -0
  397. package/src/vite/debug.ts +185 -0
  398. package/src/vite/discovery/bundle-postprocess.ts +182 -0
  399. package/src/vite/discovery/discover-routers.ts +389 -0
  400. package/src/vite/discovery/discovery-errors.ts +255 -0
  401. package/src/vite/discovery/gate-state.ts +171 -0
  402. package/src/vite/discovery/prerender-collection.ts +467 -0
  403. package/src/vite/discovery/route-types-writer.ts +214 -0
  404. package/src/vite/discovery/self-gen-tracking.ts +73 -0
  405. package/src/vite/discovery/state.ts +161 -0
  406. package/src/vite/discovery/virtual-module-codegen.ts +183 -0
  407. package/src/vite/index.ts +23 -2255
  408. package/src/vite/inject-client-debug.ts +36 -0
  409. package/src/vite/plugin-types.ts +303 -0
  410. package/src/vite/plugins/cjs-to-esm.ts +90 -0
  411. package/src/vite/plugins/client-ref-dedup.ts +120 -0
  412. package/src/vite/plugins/client-ref-hashing.ts +118 -0
  413. package/src/vite/plugins/cloudflare-protocol-loader-hook.d.mts +23 -0
  414. package/src/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
  415. package/src/vite/plugins/cloudflare-protocol-stub.ts +194 -0
  416. package/src/vite/{expose-action-id.ts → plugins/expose-action-id.ts} +88 -110
  417. package/src/vite/{expose-id-utils.ts → plugins/expose-id-utils.ts} +89 -79
  418. package/src/vite/plugins/expose-ids/export-analysis.ts +363 -0
  419. package/src/vite/plugins/expose-ids/handler-transform.ts +130 -0
  420. package/src/vite/plugins/expose-ids/loader-transform.ts +64 -0
  421. package/src/vite/plugins/expose-ids/router-transform.ts +199 -0
  422. package/src/vite/plugins/expose-ids/types.ts +45 -0
  423. package/src/vite/plugins/expose-internal-ids.ts +805 -0
  424. package/src/vite/plugins/performance-tracks.ts +89 -0
  425. package/src/vite/plugins/refresh-cmd.ts +127 -0
  426. package/src/vite/plugins/use-cache-transform.ts +313 -0
  427. package/src/vite/plugins/vercel-output.ts +384 -0
  428. package/src/vite/plugins/version-injector.ts +94 -0
  429. package/src/vite/plugins/version-plugin.ts +263 -0
  430. package/src/vite/plugins/virtual-entries.ts +234 -0
  431. package/src/vite/plugins/virtual-stub-plugin.ts +29 -0
  432. package/src/vite/rango.ts +560 -0
  433. package/src/vite/router-discovery.ts +1638 -0
  434. package/src/vite/{ast-handler-extract.ts → utils/ast-handler-extract.ts} +200 -37
  435. package/src/vite/utils/banner.ts +36 -0
  436. package/src/vite/utils/bundle-analysis.ts +132 -0
  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 +15 -0
  441. package/src/vite/utils/package-resolution.ts +89 -0
  442. package/src/vite/utils/prerender-utils.ts +249 -0
  443. package/src/vite/utils/shared-utils.ts +269 -0
  444. package/CLAUDE.md +0 -43
  445. package/dist/vite/index.named-routes.gen.ts +0 -103
  446. package/src/browser/lru-cache.ts +0 -69
  447. package/src/browser/react/use-client-cache.ts +0 -56
  448. package/src/browser/request-controller.ts +0 -164
  449. package/src/browser/shallow.ts +0 -35
  450. package/src/cache/memory-store.ts +0 -253
  451. package/src/handles/index.ts +0 -6
  452. package/src/href-context.ts +0 -33
  453. package/src/network-error-thrower.tsx +0 -21
  454. package/src/router.gen.ts +0 -6
  455. package/src/static-handler.gen.ts +0 -5
  456. package/src/urls.gen.ts +0 -8
  457. package/src/vite/expose-internal-ids.ts +0 -1167
  458. package/src/vite/package-resolution.ts +0 -125
  459. package/src/vite/virtual-entries.ts +0 -114
  460. /package/src/vite/{version.d.ts → plugins/version.d.ts} +0 -0
@@ -8,11 +8,11 @@ import {
8
8
  generateHistoryKey,
9
9
  } from "./navigation-store.js";
10
10
  import { createEventController } from "./event-controller.js";
11
+ import { validateRedirectOrigin } from "./validate-redirect-origin.js";
11
12
  import { createNavigationClient } from "./navigation-client.js";
12
13
  import { createServerActionBridge } from "./server-action-bridge.js";
13
14
  import { createNavigationBridge } from "./navigation-bridge.js";
14
- import { NavigationProvider, initHandleDataSync, initSegmentsSync } from "./react/index.js";
15
- import { initThemeConfigSync } from "../theme/theme-context.js";
15
+ import { NavigationProvider } from "./react/index.js";
16
16
  import type {
17
17
  RscPayload,
18
18
  RscBrowserDependencies,
@@ -22,6 +22,17 @@ import type {
22
22
  } from "./types.js";
23
23
  import type { EventController } from "./event-controller.js";
24
24
  import type { ResolvedThemeConfig, Theme } from "../theme/types.js";
25
+ import { initRangoState } from "./rango-state.js";
26
+ import { registerNavigationStore } from "./navigation-store-handle.js";
27
+ import { initPrefetchCache } from "./prefetch/cache.js";
28
+ import { setPrefetchConcurrency } from "./prefetch/queue.js";
29
+ import { setPrefetchDecoder } from "./prefetch/fetch.js";
30
+ import { setAppVersion } from "./app-version.js";
31
+ import {
32
+ isInterceptSegment,
33
+ splitInterceptSegments,
34
+ } from "./intercept-utils.js";
35
+ import { createAppShellRef } from "./app-shell.js";
25
36
 
26
37
  // Vite HMR types are provided by vite/client
27
38
 
@@ -89,7 +100,6 @@ export interface InitBrowserAppOptions {
89
100
  * Only used when themeConfig is provided.
90
101
  */
91
102
  initialTheme?: Theme;
92
-
93
103
  }
94
104
 
95
105
  /**
@@ -107,13 +117,26 @@ export interface BrowserAppContext {
107
117
  initialTheme?: Theme;
108
118
  /** Whether connection warmup is enabled */
109
119
  warmupEnabled?: boolean;
120
+ /** Whether the hydrated tree should be wrapped in React.StrictMode */
121
+ strictMode?: boolean;
122
+ /** App version for prefetch version mismatch detection */
123
+ version?: string;
124
+ /**
125
+ * App-shell ref, read through on each render so renderSegments and the
126
+ * NavigationProvider see rootLayout/basename/version without closing over a
127
+ * stale snapshot. Set once from the initial payload and not swapped within a
128
+ * session: a cross-app navigation is a full document load (X-RSC-Reload), so
129
+ * the target app establishes its own shell on load. Theme, warmup, and
130
+ * prefetch TTL are document-lifetime too (see AppShell).
131
+ */
132
+ appShellRef?: import("./app-shell.js").AppShellRef;
110
133
  }
111
134
 
112
135
  // Module-level state for the initialized app
113
136
  let browserAppContext: BrowserAppContext | null = null;
114
137
 
115
138
  /**
116
- * Initialize the browser app. Must be called before rendering RSCRouter.
139
+ * Initialize the browser app. Must be called before rendering Rango.
117
140
  *
118
141
  * This function:
119
142
  * - Loads the initial RSC payload from the stream
@@ -122,18 +145,26 @@ let browserAppContext: BrowserAppContext | null = null;
122
145
  * - Configures HMR support
123
146
  */
124
147
  export async function initBrowserApp(
125
- options: InitBrowserAppOptions
148
+ options: InitBrowserAppOptions,
126
149
  ): Promise<BrowserAppContext> {
127
- const { rscStream, deps, storeOptions, linkInterception = true, themeConfig, initialTheme } = options;
150
+ const {
151
+ rscStream,
152
+ deps,
153
+ storeOptions,
154
+ linkInterception = true,
155
+ themeConfig,
156
+ initialTheme,
157
+ } = options;
128
158
 
129
- // Load initial payload from SSR-injected __FLIGHT_DATA__
130
159
  const initialPayload =
131
160
  await deps.createFromReadableStream<RscPayload>(rscStream);
132
161
 
133
162
  // Extract themeConfig and initialTheme from payload if not explicitly provided
134
163
  // This allows virtual entries to work without importing the router
135
- const effectiveThemeConfig = themeConfig ?? initialPayload.metadata?.themeConfig ?? null;
136
- const effectiveInitialTheme = initialTheme ?? initialPayload.metadata?.initialTheme;
164
+ const effectiveThemeConfig =
165
+ themeConfig ?? initialPayload.metadata?.themeConfig ?? null;
166
+ const effectiveInitialTheme =
167
+ initialTheme ?? initialPayload.metadata?.initialTheme;
137
168
 
138
169
  // Get initial segments and compute history key from current URL
139
170
  const initialSegments = (initialPayload.metadata?.segments ??
@@ -149,20 +180,29 @@ export async function initBrowserApp(
149
180
  ...(storeOptions?.cacheSize && { cacheSize: storeOptions.cacheSize }),
150
181
  });
151
182
 
183
+ // Register the active store on the module-level handle and wire the
184
+ // jar-divergence observer before any getRangoState() read can detect a
185
+ // cross-tab/server rotation. There is no global store singleton, so this
186
+ // handle is the live reference.
187
+ registerNavigationStore(store);
188
+
189
+ // Seed router identity from the initial SSR payload so the first
190
+ // cross-app SPA navigation can detect the app switch.
191
+ if (initialPayload.metadata?.routerId) {
192
+ store.setRouterId?.(initialPayload.metadata.routerId);
193
+ }
194
+
152
195
  // Create event controller for reactive state management
153
196
  const eventController = createEventController({
154
197
  initialLocation: new URL(window.location.href),
155
198
  });
156
199
 
157
- // Initialize segments state BEFORE hydration to avoid mismatch
158
- initSegmentsSync(initialPayload.metadata?.matched, initialPayload.metadata?.pathname);
159
-
160
- // Initialize theme config for MetaTags (must match SSR state)
161
- initThemeConfigSync(effectiveThemeConfig);
162
-
163
200
  // Initialize event controller with segment order (even without handles)
164
201
  eventController.setHandleData({}, initialPayload.metadata?.matched);
165
202
 
203
+ // Initialize route params
204
+ eventController.setParams(initialPayload.metadata?.params ?? {});
205
+
166
206
  // Initialize handle data from initial payload BEFORE hydration
167
207
  // This ensures useHandle returns correct data during hydration to avoid mismatch
168
208
  // The handles property is an async generator that yields on each push
@@ -172,28 +212,73 @@ export async function initBrowserApp(
172
212
  for await (const handleData of handlesGenerator) {
173
213
  lastHandleData = handleData;
174
214
  }
175
- // Initialize both event controller AND module-level SSR state for hydration compatibility
176
- eventController.setHandleData(lastHandleData, initialPayload.metadata?.matched);
177
- initHandleDataSync(lastHandleData, initialPayload.metadata?.matched);
215
+ // Initialize event controller with initial handle state before hydration.
216
+ eventController.setHandleData(
217
+ lastHandleData,
218
+ initialPayload.metadata?.matched,
219
+ );
178
220
 
179
221
  // Update the initial cache entry with the processed handleData
180
222
  // The cache entry was created by createNavigationStore but without handleData
181
223
  store.updateCacheHandleData(initialHistoryKey, lastHandleData);
182
224
  }
183
225
 
184
-
185
226
  // Create composable utilities
186
227
  const client = createNavigationClient(deps);
187
228
 
188
- // Extract rootLayout and version from metadata for browser-side re-renders
189
- const rootLayout = initialPayload.metadata?.rootLayout;
229
+ // Capture the per-router app-shell. rootLayout, basename, and version live
230
+ // here and are read through the ref at call time rather than closed over.
231
+ // It is set once from the initial payload and not swapped within a session:
232
+ // a cross-app navigation is a full document load (X-RSC-Reload), so the
233
+ // target app establishes its own shell on load.
190
234
  const version = initialPayload.metadata?.version;
235
+ const appShellRef = createAppShellRef({
236
+ routerId: initialPayload.metadata?.routerId,
237
+ rootLayout: initialPayload.metadata?.rootLayout,
238
+ basename: initialPayload.metadata?.basename,
239
+ version,
240
+ });
191
241
 
192
- // Create a bound renderSegments that includes rootLayout
242
+ // Initialize the rango state cookie for cache invalidation. The build version
243
+ // busts cached prefetches on deploy; the server-resolved cookie name
244
+ // namespaces the cookie so sibling apps on the same origin don't collide
245
+ // (falls back to the bare default prefix if metadata lacks the name).
246
+ initRangoState(version ?? "0", initialPayload.metadata?.stateCookieName);
247
+ setAppVersion(version);
248
+
249
+ // Initialize the in-memory prefetch cache (TTL + max size) and the prefetch
250
+ // queue concurrency from server config. A TTL of 0 disables the cache;
251
+ // undefined values fall back to the module defaults.
252
+ const prefetchCacheTTL = initialPayload.metadata?.prefetchCacheTTL;
253
+ const prefetchCacheSize = initialPayload.metadata?.prefetchCacheSize;
254
+ if (prefetchCacheTTL !== undefined || prefetchCacheSize !== undefined) {
255
+ initPrefetchCache(prefetchCacheTTL, prefetchCacheSize);
256
+ }
257
+ const prefetchConcurrency = initialPayload.metadata?.prefetchConcurrency;
258
+ if (prefetchConcurrency !== undefined) {
259
+ setPrefetchConcurrency(prefetchConcurrency);
260
+ }
261
+
262
+ // Wire the RSC decoder so prefetches decode eagerly and warm the route's
263
+ // client chunks (same createFromFetch the navigation client uses).
264
+ setPrefetchDecoder((response) => deps.createFromFetch<RscPayload>(response));
265
+
266
+ // Create a bound renderSegments that reads rootLayout through the shell ref.
267
+ // The shell is set once at init and not swapped within a session (a cross-app
268
+ // navigation is a full document load), so this always renders this app's
269
+ // Document; reading through the ref just avoids closing over a stale value.
193
270
  const renderSegments = (
194
271
  segments: ResolvedSegment[],
195
- options?: RenderSegmentsOptions
196
- ) => baseRenderSegments(segments, { ...options, rootLayout });
272
+ options?: RenderSegmentsOptions,
273
+ ) =>
274
+ baseRenderSegments(segments, {
275
+ ...options,
276
+ rootLayout: appShellRef.get().rootLayout,
277
+ });
278
+
279
+ // Lazy reference for navigation bridge — the action bridge is created first
280
+ // but may need to trigger SPA navigation for action redirects.
281
+ let navigateFn: ((url: string, options?: any) => Promise<void>) | null = null;
197
282
 
198
283
  // Setup server action bridge
199
284
  const actionBridge = createServerActionBridge({
@@ -203,7 +288,19 @@ export async function initBrowserApp(
203
288
  deps,
204
289
  onUpdate: (update) => store.emitUpdate(update),
205
290
  renderSegments,
206
- version,
291
+ onNavigate: (url, options) => {
292
+ if (!navigateFn) {
293
+ // Navigation bridge not wired yet: hard-navigate, but re-validate
294
+ // same-origin defensively so this init-window fallback cannot become an
295
+ // open redirect (the normal path validates inside the navigation bridge).
296
+ const safe = validateRedirectOrigin(url, window.location.origin);
297
+ if (safe) {
298
+ window.location.href = safe;
299
+ }
300
+ return Promise.resolve();
301
+ }
302
+ return navigateFn(url, options);
303
+ },
207
304
  });
208
305
  actionBridge.register();
209
306
 
@@ -214,9 +311,12 @@ export async function initBrowserApp(
214
311
  client,
215
312
  onUpdate: (update) => store.emitUpdate(update),
216
313
  renderSegments,
217
- version,
314
+ version: version,
218
315
  });
219
316
 
317
+ // Connect action redirect → navigation bridge (now that both are initialized)
318
+ navigateFn = (url, options) => navigationBridge.navigate(url, options);
319
+
220
320
  // Optionally enable global link interception
221
321
  if (linkInterception) {
222
322
  navigationBridge.registerLinkInterception();
@@ -225,52 +325,157 @@ export async function initBrowserApp(
225
325
  // Build initial tree with rootLayout
226
326
  const initialTree = renderSegments(initialPayload.metadata!.segments);
227
327
 
228
- // Setup HMR
328
+ // Setup HMR with debounce — burst saves (format-on-save, rapid edits)
329
+ // fire many rsc:update events in quick succession. Without debouncing,
330
+ // each event triggers a fetchPartial() which on slow routes can pile up
331
+ // and overwhelm the worker (cross-request promise issues, 500s).
229
332
  if (import.meta.hot) {
230
- import.meta.hot.on("rsc:update", async () => {
231
- console.log("[RSCRouter] HMR: Server update, refetching RSC");
232
-
233
- const handle = eventController.startNavigation(window.location.href, {
234
- replace: true,
235
- });
236
- const streamingToken = handle.startStreaming();
237
-
238
- try {
239
- const { payload, streamComplete } = await client.fetchPartial({
240
- targetUrl: window.location.href,
241
- segmentIds: [],
242
- previousUrl: store.getSegmentState().currentUrl,
243
- hmr: true,
244
- });
333
+ let hmrTimer: ReturnType<typeof setTimeout> | null = null;
334
+ let hmrAbort: AbortController | null = null;
245
335
 
246
- if (payload.metadata?.isPartial) {
247
- const segments = payload.metadata.segments || [];
248
- const matched = payload.metadata.matched || [];
336
+ import.meta.hot.on("rsc:update", () => {
337
+ // Cancel any pending debounce timer
338
+ if (hmrTimer !== null) {
339
+ clearTimeout(hmrTimer);
340
+ }
249
341
 
250
- store.setSegmentIds(matched);
251
- store.setCurrentUrl(window.location.href);
342
+ // Abort any in-flight HMR fetch so it doesn't race with the next one
343
+ if (hmrAbort) {
344
+ hmrAbort.abort();
345
+ hmrAbort = null;
346
+ }
347
+
348
+ // Debounce: wait 200ms of quiet before fetching
349
+ hmrTimer = setTimeout(async () => {
350
+ hmrTimer = null;
351
+
352
+ // Don't interrupt an active user navigation — startNavigation()
353
+ // would abort it and refetch the old URL (window.location.href
354
+ // hasn't updated yet). The user's navigation will pick up the
355
+ // new server code when it completes. isNavigating covers the
356
+ // full lifecycle (fetching + streaming, before commit) without
357
+ // blocking on server actions.
358
+ if (eventController.getState().isNavigating) {
359
+ console.log("[Rango] HMR: Skipping — navigation in progress");
360
+ return;
361
+ }
252
362
 
253
- const historyKey = generateHistoryKey(window.location.href);
254
- store.setHistoryKey(historyKey);
255
- const currentHandleData = eventController.getHandleState().data;
256
- store.cacheSegmentsForHistory(historyKey, segments, currentHandleData);
363
+ console.log("[Rango] HMR: Server update, refetching RSC");
257
364
 
258
- store.emitUpdate({
259
- root: renderSegments(segments),
260
- metadata: payload.metadata,
365
+ const abort = new AbortController();
366
+ hmrAbort = abort;
367
+
368
+ const handle = eventController.startNavigation(window.location.href, {
369
+ replace: true,
370
+ });
371
+ const streamingToken = handle.startStreaming();
372
+
373
+ const interceptSourceUrl = store.getInterceptSourceUrl();
374
+
375
+ try {
376
+ const { payload, streamComplete } = await client.fetchPartial({
377
+ targetUrl: window.location.href,
378
+ segmentIds: [],
379
+ previousUrl: store.getSegmentState().currentUrl,
380
+ interceptSourceUrl: interceptSourceUrl || undefined,
381
+ routerId: store.getRouterId?.(),
382
+ hmr: true,
383
+ signal: abort.signal,
261
384
  });
262
- }
263
385
 
264
- await streamComplete;
265
- } finally {
266
- streamingToken.end();
267
- }
268
- handle.complete(new URL(window.location.href));
269
- console.log("[RSCRouter] HMR: RSC stream complete");
386
+ if (abort.signal.aborted) return;
387
+
388
+ // If the server returned a non-RSC response (404, 500 without
389
+ // error boundary), the payload won't have valid metadata.
390
+ // Reload to recover rather than leaving the page stale.
391
+ if (!payload.metadata) {
392
+ throw new Error("HMR refetch returned invalid payload");
393
+ }
394
+
395
+ // Update version BEFORE rebuilding state so that
396
+ // clearHistoryCache() runs first, then the fresh segment
397
+ // cache entry we create below survives.
398
+ //
399
+ // Compare against the bridge's live version, not the init-time
400
+ // `version` const: after the first HMR bump the const is stale, so a
401
+ // later update with an unchanged version would otherwise re-clear the
402
+ // cache and re-broadcast across tabs/apps. The live read fires only
403
+ // on a genuine version change.
404
+ const newVersion = payload.metadata.version;
405
+ const currentVersion = navigationBridge.getVersion();
406
+ if (newVersion && newVersion !== currentVersion) {
407
+ console.log(
408
+ "[Rango] HMR: version changed",
409
+ currentVersion,
410
+ "→",
411
+ newVersion,
412
+ "clearing caches",
413
+ );
414
+ navigationBridge.updateVersion(newVersion);
415
+ }
416
+
417
+ // Apply only partial segment updates. A non-partial payload during
418
+ // HMR is transient: the worker route table is still rebuilding after
419
+ // the edit, so the URL momentarily resolves to not-found/catch-all.
420
+ // Skip it -- the debounced follow-up refetch returns the settled
421
+ // route's partial payload and renders it below. We never reload here:
422
+ // a paramless document GET would run the SSR path and surface the
423
+ // not-found page during that same transient.
424
+ if (payload.metadata?.isPartial) {
425
+ const segments = payload.metadata.segments || [];
426
+ const matched = payload.metadata.matched || [];
427
+
428
+ // Derive intercept state from the returned payload, not the
429
+ // pre-fetch store snapshot. If the HMR edit removed intercept
430
+ // behavior, the response won't contain intercept segments.
431
+ const responseIsIntercept = segments.some(isInterceptSegment);
432
+
433
+ // Sync store intercept state with what the server returned
434
+ if (!responseIsIntercept && interceptSourceUrl) {
435
+ store.setInterceptSourceUrl(null);
436
+ }
437
+
438
+ store.setSegmentIds(matched);
439
+ store.setCurrentUrl(window.location.href);
440
+
441
+ const historyKey = generateHistoryKey(window.location.href, {
442
+ intercept: responseIsIntercept,
443
+ });
444
+ store.setHistoryKey(historyKey);
445
+ const currentHandleData = eventController.getHandleState().data;
446
+ store.cacheSegmentsForHistory(
447
+ historyKey,
448
+ segments,
449
+ currentHandleData,
450
+ );
451
+
452
+ const { main, intercept } = splitInterceptSegments(segments);
453
+ store.emitUpdate({
454
+ root: renderSegments(main, {
455
+ interceptSegments: intercept.length > 0 ? intercept : undefined,
456
+ }),
457
+ metadata: payload.metadata,
458
+ });
459
+ }
460
+
461
+ await streamComplete;
462
+ handle.complete(new URL(window.location.href));
463
+ console.log("[Rango] HMR: RSC stream complete");
464
+ } catch (err) {
465
+ if (abort.signal.aborted) return;
466
+ console.warn("[Rango] HMR: Refetch failed, reloading page", err);
467
+ window.location.reload();
468
+ return;
469
+ } finally {
470
+ if (hmrAbort === abort) hmrAbort = null;
471
+ streamingToken.end();
472
+ handle[Symbol.dispose]();
473
+ }
474
+ }, 200);
270
475
  });
271
476
  }
272
477
 
273
- // Store context for RSCRouter component
478
+ // Store context for Rango component
274
479
  const context: BrowserAppContext = {
275
480
  store,
276
481
  eventController,
@@ -280,6 +485,9 @@ export async function initBrowserApp(
280
485
  themeConfig: effectiveThemeConfig,
281
486
  initialTheme: effectiveInitialTheme,
282
487
  warmupEnabled: initialPayload.metadata?.warmupEnabled ?? true,
488
+ strictMode: initialPayload.metadata?.strictMode ?? true,
489
+ version,
490
+ appShellRef,
283
491
  };
284
492
  browserAppContext = context;
285
493
 
@@ -292,7 +500,7 @@ export async function initBrowserApp(
292
500
  export function getBrowserAppContext(): BrowserAppContext {
293
501
  if (!browserAppContext) {
294
502
  throw new Error(
295
- "RSCRouter: initBrowserApp() must be called before rendering RSCRouter"
503
+ "Rango: initBrowserApp() must be called before rendering Rango",
296
504
  );
297
505
  }
298
506
  return browserAppContext;
@@ -306,18 +514,18 @@ export function resetBrowserAppContext(): void {
306
514
  }
307
515
 
308
516
  /**
309
- * Props for the RSCRouter component
517
+ * Props for the Rango component
310
518
  */
311
- export interface RSCRouterProps {}
519
+ export interface RangoProps {}
312
520
 
313
521
  /**
314
- * RSCRouter component - renders the RSC router with all internal wiring.
522
+ * Rango component - renders the RSC router with all internal wiring.
315
523
  *
316
524
  * Must be called after initBrowserApp() has completed.
317
525
  *
318
526
  * @example
319
527
  * ```tsx
320
- * import { initBrowserApp, RSCRouter } from "rsc-router/browser";
528
+ * import { initBrowserApp, Rango } from "rsc-router/browser";
321
529
  * import { rscStream } from "rsc-html-stream/client";
322
530
  * import * as rscBrowser from "@vitejs/plugin-rsc/browser";
323
531
  *
@@ -327,26 +535,46 @@ export interface RSCRouterProps {}
327
535
  * hydrateRoot(
328
536
  * document,
329
537
  * <React.StrictMode>
330
- * <RSCRouter />
538
+ * <Rango />
331
539
  * </React.StrictMode>
332
540
  * );
333
541
  * }
334
542
  * main();
335
543
  * ```
336
544
  */
337
- export function RSCRouter(_props: RSCRouterProps): React.ReactElement {
338
- const { store, eventController, bridge, initialPayload, initialTree, themeConfig, initialTheme, warmupEnabled } =
339
- getBrowserAppContext();
545
+ export function Rango(_props: RangoProps): React.ReactElement {
546
+ const {
547
+ store,
548
+ eventController,
549
+ bridge,
550
+ initialPayload,
551
+ initialTree,
552
+ themeConfig,
553
+ initialTheme,
554
+ warmupEnabled,
555
+ version,
556
+ appShellRef,
557
+ } = getBrowserAppContext();
558
+
559
+ // Signal that the React tree has hydrated. useEffect only fires after
560
+ // hydration completes, so this attribute is a stable readiness marker
561
+ // that does not depend on React internals like __reactFiber.
562
+ React.useEffect(() => {
563
+ document.documentElement.dataset.hydrated = "";
564
+ }, []);
340
565
 
341
566
  return (
342
567
  <NavigationProvider
343
568
  store={store}
344
569
  eventController={eventController}
345
- initialPayload={{ ...initialPayload, root: initialTree }}
570
+ initialPayload={{ root: initialTree, metadata: initialPayload.metadata! }}
346
571
  bridge={bridge}
347
572
  themeConfig={themeConfig}
348
573
  initialTheme={initialTheme}
349
574
  warmupEnabled={warmupEnabled}
575
+ version={version}
576
+ basename={initialPayload.metadata?.basename}
577
+ appShellRef={appShellRef}
350
578
  />
351
579
  );
352
580
  }