@rangojs/router 0.0.0-experimental.79 → 0.0.0-experimental.7c7e4327

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 (440) hide show
  1. package/AGENTS.md +8 -4
  2. package/README.md +301 -797
  3. package/dist/bin/rango.js +603 -145
  4. package/dist/testing/vitest.js +82 -0
  5. package/dist/vite/index.js +3750 -1160
  6. package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
  7. package/package.json +96 -24
  8. package/skills/api-client/SKILL.md +211 -0
  9. package/skills/breadcrumbs/SKILL.md +85 -6
  10. package/skills/bundle-analysis/SKILL.md +159 -0
  11. package/skills/cache-guide/SKILL.md +228 -33
  12. package/skills/caching/SKILL.md +336 -19
  13. package/skills/catalog.json +271 -0
  14. package/skills/comparison/SKILL.md +50 -0
  15. package/skills/comparison/agents/openai.yaml +4 -0
  16. package/skills/comparison/references/framework-comparison.md +837 -0
  17. package/skills/composability/SKILL.md +110 -4
  18. package/skills/css/SKILL.md +76 -0
  19. package/skills/debug-manifest/SKILL.md +5 -3
  20. package/skills/defer-hydration/SKILL.md +235 -0
  21. package/skills/document-cache/SKILL.md +87 -56
  22. package/skills/fonts/SKILL.md +1 -1
  23. package/skills/handler-use/SKILL.md +12 -10
  24. package/skills/hooks/SKILL.md +73 -691
  25. package/skills/hooks/data.md +273 -0
  26. package/skills/hooks/handle-and-actions.md +103 -0
  27. package/skills/hooks/navigation.md +110 -0
  28. package/skills/hooks/outlets.md +41 -0
  29. package/skills/hooks/state.md +228 -0
  30. package/skills/hooks/urls.md +135 -0
  31. package/skills/host-router/SKILL.md +129 -27
  32. package/skills/i18n/SKILL.md +276 -0
  33. package/skills/intercept/SKILL.md +75 -19
  34. package/skills/layout/SKILL.md +40 -19
  35. package/skills/links/SKILL.md +247 -17
  36. package/skills/loader/SKILL.md +248 -10
  37. package/skills/middleware/SKILL.md +25 -13
  38. package/skills/migrate-nextjs/SKILL.md +205 -20
  39. package/skills/migrate-react-router/SKILL.md +59 -670
  40. package/skills/migrate-react-router/cloudflare-workers.md +129 -0
  41. package/skills/migrate-react-router/component-migration.md +196 -0
  42. package/skills/migrate-react-router/data-and-actions.md +225 -0
  43. package/skills/migrate-react-router/route-mapping.md +271 -0
  44. package/skills/mime-routes/SKILL.md +29 -2
  45. package/skills/observability/SKILL.md +202 -0
  46. package/skills/parallel/SKILL.md +40 -10
  47. package/skills/ppr/SKILL.md +616 -0
  48. package/skills/prerender/SKILL.md +72 -60
  49. package/skills/rango/SKILL.md +318 -26
  50. package/skills/react-compiler/SKILL.md +168 -0
  51. package/skills/response-routes/SKILL.md +138 -49
  52. package/skills/route/SKILL.md +117 -9
  53. package/skills/router-setup/SKILL.md +44 -9
  54. package/skills/scripts/SKILL.md +179 -0
  55. package/skills/server-actions/SKILL.md +776 -0
  56. package/skills/shell-manifest/SKILL.md +185 -0
  57. package/skills/streams-and-websockets/SKILL.md +283 -0
  58. package/skills/tailwind/SKILL.md +28 -4
  59. package/skills/testing/SKILL.md +130 -0
  60. package/skills/testing/bindings.md +103 -0
  61. package/skills/testing/cache-prerender.md +127 -0
  62. package/skills/testing/client-components.md +124 -0
  63. package/skills/testing/e2e-parity.md +125 -0
  64. package/skills/testing/flight.md +91 -0
  65. package/skills/testing/handles.md +131 -0
  66. package/skills/testing/loader.md +128 -0
  67. package/skills/testing/middleware.md +99 -0
  68. package/skills/testing/render-handler.md +122 -0
  69. package/skills/testing/response-routes.md +95 -0
  70. package/skills/testing/reverse-and-types.md +85 -0
  71. package/skills/testing/server-actions.md +107 -0
  72. package/skills/testing/server-tree.md +128 -0
  73. package/skills/testing/setup.md +123 -0
  74. package/skills/theme/SKILL.md +1 -1
  75. package/skills/typesafety/SKILL.md +45 -626
  76. package/skills/typesafety/env-and-bindings.md +254 -0
  77. package/skills/typesafety/generated-files-and-cli.md +335 -0
  78. package/skills/typesafety/params-and-search.md +153 -0
  79. package/skills/typesafety/route-types.md +209 -0
  80. package/skills/use-cache/SKILL.md +74 -15
  81. package/skills/vercel/SKILL.md +128 -0
  82. package/skills/view-transitions/SKILL.md +337 -0
  83. package/src/__augment-tests__/augment.ts +81 -0
  84. package/src/__augment-tests__/augmented.check.ts +116 -0
  85. package/src/__internal.ts +0 -65
  86. package/src/browser/action-coordinator.ts +53 -36
  87. package/src/browser/action-fence.ts +47 -0
  88. package/src/browser/app-shell.ts +39 -0
  89. package/src/browser/connection-warmup.ts +134 -0
  90. package/src/browser/cookie-name.ts +140 -0
  91. package/src/browser/event-controller.ts +252 -158
  92. package/src/browser/history-state.ts +21 -0
  93. package/src/browser/index.ts +3 -3
  94. package/src/browser/invalidate-client-cache.ts +52 -0
  95. package/src/browser/logging.ts +28 -0
  96. package/src/browser/merge-segment-loaders.ts +6 -4
  97. package/src/browser/navigation-bridge.ts +94 -25
  98. package/src/browser/navigation-client.ts +144 -79
  99. package/src/browser/navigation-store-handle.ts +38 -0
  100. package/src/browser/navigation-store.ts +161 -73
  101. package/src/browser/navigation-transaction.ts +9 -59
  102. package/src/browser/network-error-handler.ts +34 -7
  103. package/src/browser/partial-update.ts +183 -144
  104. package/src/browser/prefetch/cache.ts +242 -77
  105. package/src/browser/prefetch/fetch.ts +325 -69
  106. package/src/browser/prefetch/queue.ts +61 -12
  107. package/src/browser/rango-state.ts +158 -76
  108. package/src/browser/react/Link.tsx +58 -20
  109. package/src/browser/react/NavigationProvider.tsx +202 -120
  110. package/src/browser/react/ScrollRestoration.tsx +10 -6
  111. package/src/browser/react/filter-segment-order.ts +66 -7
  112. package/src/browser/react/index.ts +0 -48
  113. package/src/browser/react/location-state-shared.ts +178 -8
  114. package/src/browser/react/location-state.ts +39 -14
  115. package/src/browser/react/use-action.ts +6 -15
  116. package/src/browser/react/use-handle.ts +17 -14
  117. package/src/browser/react/use-href.tsx +8 -1
  118. package/src/browser/react/use-link-status.ts +33 -8
  119. package/src/browser/react/use-navigation.ts +32 -7
  120. package/src/browser/react/use-params.ts +20 -10
  121. package/src/browser/react/use-reverse.ts +106 -0
  122. package/src/browser/react/use-router.ts +25 -3
  123. package/src/browser/react/use-search-params.ts +0 -5
  124. package/src/browser/react/use-segments.ts +11 -21
  125. package/src/browser/response-adapter.ts +99 -8
  126. package/src/browser/rsc-router.tsx +145 -28
  127. package/src/browser/scroll-restoration.ts +37 -22
  128. package/src/browser/segment-reconciler.ts +31 -21
  129. package/src/browser/segment-structure-assert.ts +2 -2
  130. package/src/browser/server-action-bridge.ts +236 -65
  131. package/src/browser/types.ts +102 -9
  132. package/src/browser/validate-redirect-origin.ts +43 -16
  133. package/src/build/collect-fallback-refs.ts +107 -0
  134. package/src/build/generate-manifest.ts +203 -154
  135. package/src/build/generate-route-types.ts +3 -1
  136. package/src/build/index.ts +11 -3
  137. package/src/build/prefix-tree-utils.ts +123 -0
  138. package/src/build/route-trie.ts +152 -21
  139. package/src/build/route-types/ast-route-extraction.ts +15 -8
  140. package/src/build/route-types/codegen.ts +16 -5
  141. package/src/build/route-types/include-resolution.ts +456 -62
  142. package/src/build/route-types/param-extraction.ts +6 -3
  143. package/src/build/route-types/per-module-writer.ts +22 -6
  144. package/src/build/route-types/router-processing.ts +128 -51
  145. package/src/build/route-types/scan-filter.ts +1 -1
  146. package/src/build/route-types/source-scan.ts +216 -0
  147. package/src/build/runtime-discovery.ts +13 -21
  148. package/src/cache/cache-error.ts +104 -0
  149. package/src/cache/cache-key-utils.ts +58 -13
  150. package/src/cache/cache-policy.ts +108 -34
  151. package/src/cache/cache-runtime.ts +421 -58
  152. package/src/cache/cache-scope.ts +187 -96
  153. package/src/cache/cache-tag.ts +149 -0
  154. package/src/cache/cf/cf-base64.ts +33 -0
  155. package/src/cache/cf/cf-cache-constants.ts +127 -0
  156. package/src/cache/cf/cf-cache-store.ts +2202 -372
  157. package/src/cache/cf/cf-cache-types.ts +349 -0
  158. package/src/cache/cf/cf-kv-utils.ts +46 -0
  159. package/src/cache/cf/cf-tag-marker-memo.ts +105 -0
  160. package/src/cache/cf/index.ts +6 -16
  161. package/src/cache/document-cache.ts +126 -41
  162. package/src/cache/handle-snapshot.ts +70 -0
  163. package/src/cache/index.ts +23 -20
  164. package/src/cache/memory-segment-store.ts +243 -37
  165. package/src/cache/profile-registry.ts +46 -31
  166. package/src/cache/read-through-swr.ts +56 -12
  167. package/src/cache/segment-codec.ts +13 -21
  168. package/src/cache/shell-snapshot.ts +417 -0
  169. package/src/cache/tag-invalidation.ts +230 -0
  170. package/src/cache/types.ts +180 -99
  171. package/src/cache/vercel/index.ts +11 -0
  172. package/src/cache/vercel/vercel-cache-store.ts +1127 -0
  173. package/src/client.rsc.tsx +41 -21
  174. package/src/client.tsx +33 -61
  175. package/src/cloudflare/index.ts +11 -0
  176. package/src/cloudflare/tracing.ts +108 -0
  177. package/src/component-utils.ts +19 -0
  178. package/src/components/DefaultDocument.tsx +8 -2
  179. package/src/context-var.ts +18 -6
  180. package/src/decode-loader-results.ts +52 -0
  181. package/src/defer.ts +185 -0
  182. package/src/deps/ssr.ts +0 -1
  183. package/src/encode-kv.ts +49 -0
  184. package/src/errors.ts +30 -4
  185. package/src/escape-script.ts +52 -0
  186. package/src/handle.ts +67 -37
  187. package/src/handles/MetaTags.tsx +24 -53
  188. package/src/handles/Scripts.tsx +183 -0
  189. package/src/handles/breadcrumbs.ts +35 -8
  190. package/src/handles/deferred-resolution.ts +127 -0
  191. package/src/handles/is-thenable.ts +18 -0
  192. package/src/handles/meta.ts +14 -40
  193. package/src/handles/script.ts +244 -0
  194. package/src/host/cookie-handler.ts +9 -60
  195. package/src/host/errors.ts +13 -22
  196. package/src/host/index.ts +9 -2
  197. package/src/host/pattern-matcher.ts +23 -52
  198. package/src/host/router.ts +107 -99
  199. package/src/host/testing.ts +40 -27
  200. package/src/host/types.ts +37 -4
  201. package/src/host/utils.ts +1 -1
  202. package/src/href-client.ts +137 -22
  203. package/src/index.rsc.ts +97 -12
  204. package/src/index.ts +98 -14
  205. package/src/internal-debug.ts +11 -10
  206. package/src/loader-store.ts +500 -0
  207. package/src/loader.rsc.ts +20 -13
  208. package/src/loader.ts +12 -11
  209. package/src/missing-id-error.ts +68 -0
  210. package/src/outlet-context.ts +1 -1
  211. package/src/outlet-provider.tsx +1 -5
  212. package/src/prerender/param-hash.ts +16 -16
  213. package/src/prerender/store.ts +32 -37
  214. package/src/prerender.ts +78 -10
  215. package/src/redirect-origin.ts +114 -0
  216. package/src/regex-escape.ts +8 -0
  217. package/src/render-error-thrower.tsx +20 -0
  218. package/src/response-utils.ts +62 -0
  219. package/src/reverse.ts +65 -39
  220. package/src/root-error-boundary.tsx +1 -19
  221. package/src/route-content-wrapper.tsx +19 -77
  222. package/src/route-definition/dsl-helpers.ts +304 -309
  223. package/src/route-definition/helper-factories.ts +28 -140
  224. package/src/route-definition/helpers-types.ts +87 -59
  225. package/src/route-definition/index.ts +1 -2
  226. package/src/route-definition/redirect.ts +44 -11
  227. package/src/route-definition/resolve-handler-use.ts +12 -1
  228. package/src/route-definition/use-item-types.ts +29 -0
  229. package/src/route-map-builder.ts +41 -20
  230. package/src/route-types.ts +19 -46
  231. package/src/router/basename.ts +14 -0
  232. package/src/router/content-negotiation.ts +73 -25
  233. package/src/router/error-handling.ts +45 -18
  234. package/src/router/find-match.ts +129 -30
  235. package/src/router/handler-context.ts +27 -42
  236. package/src/router/instrument.ts +355 -0
  237. package/src/router/intercept-resolution.ts +39 -20
  238. package/src/router/lazy-includes.ts +82 -59
  239. package/src/router/loader-resolution.ts +167 -72
  240. package/src/router/logging.ts +0 -6
  241. package/src/router/manifest.ts +74 -40
  242. package/src/router/match-api.ts +80 -55
  243. package/src/router/match-context.ts +0 -22
  244. package/src/router/match-handlers.ts +211 -165
  245. package/src/router/match-middleware/background-revalidation.ts +40 -24
  246. package/src/router/match-middleware/cache-lookup.ts +159 -285
  247. package/src/router/match-middleware/cache-store.ts +64 -52
  248. package/src/router/match-middleware/intercept-resolution.ts +0 -22
  249. package/src/router/match-middleware/segment-resolution.ts +0 -22
  250. package/src/router/match-pipelines.ts +1 -42
  251. package/src/router/match-result.ts +69 -79
  252. package/src/router/metrics.ts +0 -34
  253. package/src/router/middleware-types.ts +7 -134
  254. package/src/router/middleware.ts +298 -172
  255. package/src/router/navigation-snapshot.ts +7 -56
  256. package/src/router/params-util.ts +23 -0
  257. package/src/router/parse-pattern.ts +115 -0
  258. package/src/router/pattern-matching.ts +181 -150
  259. package/src/router/prefetch-cache-ttl.ts +51 -0
  260. package/src/router/prefetch-limits.ts +37 -0
  261. package/src/router/prerender-match.ts +112 -67
  262. package/src/router/preview-match.ts +6 -2
  263. package/src/router/request-classification.ts +50 -69
  264. package/src/router/revalidation.ts +123 -73
  265. package/src/router/route-snapshot.ts +14 -3
  266. package/src/router/router-context.ts +6 -29
  267. package/src/router/router-interfaces.ts +115 -36
  268. package/src/router/router-options.ts +166 -5
  269. package/src/router/router-registry.ts +2 -5
  270. package/src/router/segment-resolution/fresh.ts +131 -86
  271. package/src/router/segment-resolution/helpers.ts +86 -6
  272. package/src/router/segment-resolution/loader-cache.ts +139 -39
  273. package/src/router/segment-resolution/loader-mask.ts +67 -0
  274. package/src/router/segment-resolution/loader-snapshot.ts +251 -0
  275. package/src/router/segment-resolution/revalidation.ts +272 -320
  276. package/src/router/segment-resolution/static-store.ts +19 -5
  277. package/src/router/segment-resolution/streamed-handler-telemetry.ts +52 -0
  278. package/src/router/segment-resolution/view-transition-default.ts +56 -0
  279. package/src/router/segment-resolution.ts +5 -1
  280. package/src/router/segment-wrappers.ts +6 -5
  281. package/src/router/state-cookie-name.ts +33 -0
  282. package/src/router/substitute-pattern-params.ts +75 -0
  283. package/src/router/telemetry-otel.ts +160 -200
  284. package/src/router/telemetry.ts +105 -20
  285. package/src/router/timeout.ts +0 -20
  286. package/src/router/tracing.ts +215 -0
  287. package/src/router/trie-matching.ts +171 -59
  288. package/src/router/types.ts +9 -63
  289. package/src/router/url-params.ts +57 -0
  290. package/src/router.ts +157 -71
  291. package/src/rsc/full-payload.ts +70 -0
  292. package/src/rsc/handler-context.ts +3 -2
  293. package/src/rsc/handler.ts +291 -217
  294. package/src/rsc/helpers.ts +168 -46
  295. package/src/rsc/index.ts +2 -5
  296. package/src/rsc/json-route-result.ts +38 -0
  297. package/src/rsc/loader-fetch.ts +114 -38
  298. package/src/rsc/manifest-init.ts +29 -42
  299. package/src/rsc/nonce.ts +10 -1
  300. package/src/rsc/origin-guard.ts +39 -25
  301. package/src/rsc/progressive-enhancement.ts +124 -13
  302. package/src/rsc/redirect-guard.ts +100 -0
  303. package/src/rsc/response-cache-serve.ts +238 -0
  304. package/src/rsc/response-error.ts +79 -12
  305. package/src/rsc/response-route-handler.ts +99 -189
  306. package/src/rsc/rsc-rendering.ts +421 -76
  307. package/src/rsc/runtime-warnings.ts +23 -10
  308. package/src/rsc/server-action.ts +282 -116
  309. package/src/rsc/shell-capture.ts +1158 -0
  310. package/src/rsc/shell-serve.ts +150 -0
  311. package/src/rsc/ssr-setup.ts +16 -0
  312. package/src/rsc/transition-gate.ts +89 -0
  313. package/src/rsc/types.ts +53 -5
  314. package/src/runtime-env.ts +18 -0
  315. package/src/search-params.ts +35 -30
  316. package/src/segment-loader-promise.ts +49 -4
  317. package/src/segment-system.tsx +350 -149
  318. package/src/serialize.ts +243 -0
  319. package/src/server/context.ts +208 -51
  320. package/src/server/cookie-parse.ts +32 -0
  321. package/src/server/cookie-store.ts +152 -5
  322. package/src/server/handle-store.ts +21 -38
  323. package/src/server/loader-registry.ts +33 -42
  324. package/src/server/request-context.ts +395 -176
  325. package/src/ssr/index.tsx +458 -178
  326. package/src/ssr/ssr-root.tsx +228 -0
  327. package/src/static-handler.ts +10 -13
  328. package/src/testing/cache-status.ts +162 -0
  329. package/src/testing/collect-handle.ts +46 -0
  330. package/src/testing/dispatch.ts +813 -0
  331. package/src/testing/dom.entry.ts +22 -0
  332. package/src/testing/e2e/fixture.ts +188 -0
  333. package/src/testing/e2e/index.ts +128 -0
  334. package/src/testing/e2e/matchers.ts +35 -0
  335. package/src/testing/e2e/page-helpers.ts +272 -0
  336. package/src/testing/e2e/parity.ts +387 -0
  337. package/src/testing/e2e/server.ts +195 -0
  338. package/src/testing/flight-matchers.ts +97 -0
  339. package/src/testing/flight-normalize.ts +11 -0
  340. package/src/testing/flight-runtime.d.ts +57 -0
  341. package/src/testing/flight-tree.ts +682 -0
  342. package/src/testing/flight.entry.ts +52 -0
  343. package/src/testing/flight.ts +257 -0
  344. package/src/testing/generated-routes.ts +199 -0
  345. package/src/testing/index.ts +105 -0
  346. package/src/testing/internal/context.ts +371 -0
  347. package/src/testing/internal/flight-client-globals.ts +30 -0
  348. package/src/testing/internal/seed-vars.ts +54 -0
  349. package/src/testing/render-handler.ts +357 -0
  350. package/src/testing/render-route.tsx +584 -0
  351. package/src/testing/run-loader.ts +385 -0
  352. package/src/testing/run-middleware.ts +205 -0
  353. package/src/testing/run-transition-when.ts +164 -0
  354. package/src/testing/vitest-stubs/cloudflare-email.ts +9 -0
  355. package/src/testing/vitest-stubs/cloudflare-workers.ts +21 -0
  356. package/src/testing/vitest-stubs/plugin-rsc.ts +16 -0
  357. package/src/testing/vitest-stubs/version.ts +5 -0
  358. package/src/testing/vitest.ts +305 -0
  359. package/src/theme/ThemeProvider.tsx +56 -84
  360. package/src/theme/ThemeScript.tsx +7 -9
  361. package/src/theme/constants.ts +52 -13
  362. package/src/theme/index.ts +0 -7
  363. package/src/theme/theme-context.ts +1 -5
  364. package/src/theme/theme-script.ts +22 -21
  365. package/src/theme/use-theme.ts +0 -3
  366. package/src/types/boundaries.ts +0 -35
  367. package/src/types/cache-types.ts +13 -4
  368. package/src/types/error-types.ts +30 -90
  369. package/src/types/global-namespace.ts +54 -41
  370. package/src/types/handler-context.ts +110 -62
  371. package/src/types/index.ts +3 -10
  372. package/src/types/loader-types.ts +11 -9
  373. package/src/types/request-scope.ts +112 -0
  374. package/src/types/route-config.ts +20 -52
  375. package/src/types/route-entry.ts +0 -6
  376. package/src/types/segments.ts +135 -14
  377. package/src/urls/include-helper.ts +19 -64
  378. package/src/urls/include-provider.ts +71 -0
  379. package/src/urls/index.ts +2 -11
  380. package/src/urls/path-helper-types.ts +63 -17
  381. package/src/urls/path-helper.ts +22 -106
  382. package/src/urls/pattern-types.ts +72 -19
  383. package/src/urls/response-types.ts +22 -29
  384. package/src/urls/type-extraction.ts +98 -154
  385. package/src/urls/urls-function.ts +1 -19
  386. package/src/use-loader.tsx +292 -107
  387. package/src/vercel/index.ts +11 -0
  388. package/src/vercel/tracing.ts +88 -0
  389. package/src/vite/debug.ts +185 -0
  390. package/src/vite/discovery/bundle-postprocess.ts +8 -7
  391. package/src/vite/discovery/dev-prerender-cache.ts +117 -0
  392. package/src/vite/discovery/discover-routers.ts +127 -86
  393. package/src/vite/discovery/discovery-errors.ts +255 -0
  394. package/src/vite/discovery/gate-state.ts +171 -0
  395. package/src/vite/discovery/prerender-collection.ts +96 -68
  396. package/src/vite/discovery/route-types-writer.ts +40 -84
  397. package/src/vite/discovery/self-gen-tracking.ts +27 -1
  398. package/src/vite/discovery/state.ts +45 -1
  399. package/src/vite/discovery/virtual-module-codegen.ts +14 -34
  400. package/src/vite/index.ts +4 -0
  401. package/src/vite/inject-client-debug.ts +88 -0
  402. package/src/vite/plugin-types.ts +210 -10
  403. package/src/vite/plugins/cjs-to-esm.ts +16 -19
  404. package/src/vite/plugins/client-ref-dedup.ts +16 -11
  405. package/src/vite/plugins/client-ref-hashing.ts +28 -15
  406. package/src/vite/plugins/cloudflare-protocol-loader-hook.d.mts +23 -0
  407. package/src/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
  408. package/src/vite/plugins/cloudflare-protocol-stub.ts +194 -0
  409. package/src/vite/plugins/expose-action-id.ts +48 -95
  410. package/src/vite/plugins/expose-id-utils.ts +88 -55
  411. package/src/vite/plugins/expose-ids/export-analysis.ts +101 -34
  412. package/src/vite/plugins/expose-ids/handler-transform.ts +11 -90
  413. package/src/vite/plugins/expose-ids/loader-transform.ts +14 -24
  414. package/src/vite/plugins/expose-ids/router-transform.ts +118 -29
  415. package/src/vite/plugins/expose-internal-ids.ts +505 -486
  416. package/src/vite/plugins/performance-tracks.ts +26 -25
  417. package/src/vite/plugins/refresh-cmd.ts +1 -1
  418. package/src/vite/plugins/use-cache-transform.ts +73 -83
  419. package/src/vite/plugins/vercel-output.ts +384 -0
  420. package/src/vite/plugins/version-injector.ts +40 -29
  421. package/src/vite/plugins/version-plugin.ts +37 -40
  422. package/src/vite/plugins/virtual-entries.ts +138 -27
  423. package/src/vite/rango.ts +236 -138
  424. package/src/vite/router-discovery.ts +927 -136
  425. package/src/vite/utils/ast-handler-extract.ts +26 -35
  426. package/src/vite/utils/banner.ts +1 -1
  427. package/src/vite/utils/bundle-analysis.ts +10 -15
  428. package/src/vite/utils/client-chunks.ts +184 -0
  429. package/src/vite/utils/directive-prologue.ts +40 -0
  430. package/src/vite/utils/forward-user-plugins.ts +171 -0
  431. package/src/vite/utils/manifest-utils.ts +4 -59
  432. package/src/vite/utils/package-resolution.ts +20 -52
  433. package/src/vite/utils/prerender-utils.ts +71 -43
  434. package/src/vite/utils/shared-utils.ts +142 -43
  435. package/src/browser/action-response-classifier.ts +0 -99
  436. package/src/browser/react/use-client-cache.ts +0 -58
  437. package/src/browser/shallow.ts +0 -40
  438. package/src/handles/index.ts +0 -7
  439. package/src/network-error-thrower.tsx +0 -23
  440. package/src/router/middleware-cookies.ts +0 -55
@@ -28,6 +28,14 @@ import { NonceContext } from "./nonce-context.js";
28
28
  import type { ResolvedThemeConfig, Theme } from "../../theme/types.js";
29
29
  import { cancelAllPrefetches } from "../prefetch/queue.js";
30
30
  import { handleNavigationEnd } from "../scroll-restoration.js";
31
+ import { createAppShellRef, type AppShellRef } from "../app-shell.js";
32
+ import { startConnectionWarmup } from "../connection-warmup.js";
33
+ import { debugLog } from "../logging.js";
34
+ import { cloneHandleData } from "../navigation-store.js";
35
+ import {
36
+ deferredHandleNames,
37
+ resolveDeferredHandleValues,
38
+ } from "../../handles/deferred-resolution.js";
31
39
 
32
40
  /**
33
41
  * Process handles from an async generator, updating the event controller
@@ -46,10 +54,35 @@ async function processHandles(
46
54
  store: NavigationStore;
47
55
  matched?: string[];
48
56
  isPartial?: boolean;
57
+ /** Server's `resolvedIds`: every segment re-resolved this request,
58
+ * including null-component ones excluded from `diff`/`segments`.
59
+ * Drives cleanup of stale handle buckets when a re-resolved segment
60
+ * pushed nothing. */
61
+ resolvedIds?: string[];
49
62
  historyKey: string;
50
63
  },
51
64
  ): Promise<void> {
52
- const { eventController, store, matched, isPartial, historyKey } = opts;
65
+ const {
66
+ eventController,
67
+ store,
68
+ matched,
69
+ isPartial,
70
+ resolvedIds,
71
+ historyKey,
72
+ } = opts;
73
+
74
+ // This nav's instance token, captured before any await — processHandles runs
75
+ // right after its own commit, so this is that commit's token. generateHistoryKey
76
+ // is URL-only, so an A->B->A revisit reuses the key; the token lets a late
77
+ // resolution tell its own visit apart from a newer same-URL visit, so a stale
78
+ // nav can never clobber a fresher one's live state or cache (P1).
79
+ const myInstance = store.getNavInstance();
80
+
81
+ // True while this nav still owns the live page: same history key AND the most
82
+ // recent commit is still ours (no newer nav has committed since).
83
+ const stillLive = (): boolean =>
84
+ historyKey === store.getHistoryKey() &&
85
+ myInstance === store.getNavInstance();
53
86
 
54
87
  let yieldCount = 0;
55
88
  for await (const handleData of handlesGenerator) {
@@ -57,14 +90,98 @@ async function processHandles(
57
90
  // This prevents handle data from cancelled navigations polluting
58
91
  // the current route's breadcrumbs (e.g., quick popstate after clicking a link).
59
92
  if (historyKey !== store.getHistoryKey()) {
60
- console.log(
93
+ debugLog(
61
94
  "[NavigationProvider] Stopping handle processing - user navigated away",
62
95
  );
63
96
  return;
64
97
  }
65
98
 
66
99
  yieldCount++;
67
- eventController.setHandleData(handleData, matched, isPartial);
100
+
101
+ // Resolve-by-default: hold the previous resolved value until this yield's
102
+ // deferred (Promise) handle values settle, then apply the fully-resolved
103
+ // snapshot. The hold needs NO extra state — we simply do not touch the store
104
+ // until the values resolve, so useHandle keeps reading (and showing) the
105
+ // previous data. A yield with no deferred value applies synchronously.
106
+ const hasDeferred = deferredHandleNames(handleData).size > 0;
107
+
108
+ if (!hasDeferred) {
109
+ eventController.setHandleData(
110
+ handleData,
111
+ matched,
112
+ isPartial,
113
+ resolvedIds,
114
+ );
115
+ // Keep the cache fresh. The token guard stops a stale same-URL nav writing
116
+ // a newer entry; the owned-write folds probe + write into one scan.
117
+ store.updateCacheHandleDataIfOwned(
118
+ historyKey,
119
+ eventController.getHandleState().data,
120
+ myInstance,
121
+ false,
122
+ );
123
+ continue;
124
+ }
125
+
126
+ // The PREVIOUS (held) snapshot — captured before the await so the cache and
127
+ // the navigate-away merge below reflect what useHandle is still showing.
128
+ const previousSnapshot = cloneHandleData(
129
+ eventController.getHandleState().data,
130
+ );
131
+
132
+ // The route HAS changed even though the handle data is held, so update
133
+ // `routeSegmentIds` (what useSegments reads) now. This leaves `data` /
134
+ // `segmentOrder` (what useHandle collects over) untouched, so useHandle keeps
135
+ // holding its previous value while useSegments reflects the new route.
136
+ eventController.setRouteSegmentIds(matched ?? []);
137
+
138
+ // Deferred-pending: the new values are not applied yet (the previous value is
139
+ // held), so the cache entry must NOT be served as fresh on a popstate return.
140
+ // Mark it STALE + handlesPending (token-guarded), storing the PREVIOUS (held)
141
+ // snapshot. P1 fix: a deferred value is a SERVER-side promise streamed via
142
+ // Flight, so a navigate-away ABORTS the stream and the resolve below never
143
+ // settles. stale makes a popstate return revalidate; handlesPending makes that
144
+ // revalidation a FULL re-render (no client segment IDs) so the server
145
+ // re-streams the handles — a diff-only revalidation would omit the unchanged
146
+ // segments' handles and the deferred value would never land (see the
147
+ // segmentIds branch in navigation-bridge.ts).
148
+ store.updateCacheHandleDataIfOwned(
149
+ historyKey,
150
+ previousSnapshot,
151
+ myInstance,
152
+ true,
153
+ true,
154
+ );
155
+
156
+ // Resolve every deferred value (allSettled; rejected + nullish dropped, sync
157
+ // values pass through). Each stream yield is a full cumulative snapshot.
158
+ const resolved = await resolveDeferredHandleValues(handleData);
159
+
160
+ if (!stillLive()) {
161
+ // Navigated away (or a same-URL nav superseded us) while resolving. We do
162
+ // NOT write `resolved` into the entry. It is THIS yield's snapshot only (on a
163
+ // partial nav, just the re-resolved segments' buckets), and a correct write
164
+ // needs setHandleData's nested per-segment merge + matched/resolvedIds
165
+ // cleanup: HandleData is handleName -> segmentId -> entries[], so a
166
+ // handle-name-level spread would drop a shared layout bucket (e.g. a
167
+ // Breadcrumbs layout crumb under L0 when the route pushed under R0) and would
168
+ // mark stale previous-route buckets fresh. We cannot run that merge here
169
+ // without touching the now-different live page. Instead leave the entry as it
170
+ // was marked before the await — stale + handlesPending — so a popstate return
171
+ // revalidates with a full re-render and re-streams the handles. A newer nav
172
+ // owning the entry has already overwritten it; nothing to do either way.
173
+ continue;
174
+ }
175
+
176
+ // Still live: apply the fully-resolved snapshot and refresh the cache fresh.
177
+ eventController.setHandleData(resolved, matched, isPartial, resolvedIds);
178
+ store.updateCacheHandleDataIfOwned(
179
+ historyKey,
180
+ eventController.getHandleState().data,
181
+ myInstance,
182
+ false,
183
+ false,
184
+ );
68
185
  }
69
186
 
70
187
  // Check again before final updates
@@ -72,19 +189,19 @@ async function processHandles(
72
189
  return;
73
190
  }
74
191
 
75
- // For partial updates where the generator yielded nothing (cached handlers),
76
- // we still need to update the segment order to clean up stale handle data.
77
- // This happens when navigating away from a route - the handlers for the new
78
- // route might not push any breadcrumbs, but we still need to remove the old ones.
192
+ // For partial updates where the generator yielded nothing (every
193
+ // re-resolved handler pushed nothing), still call setHandleData so the
194
+ // cleanup pass can clear out stale buckets for those segments.
79
195
  if (yieldCount === 0 && matched) {
80
- eventController.setHandleData({}, matched, true);
196
+ eventController.setHandleData({}, matched, true, resolvedIds);
81
197
  }
82
198
 
83
199
  // After handles processing completes, update the cache's handleData.
84
200
  // This fixes a race condition where commit() caches stale handleData before
85
201
  // the async handles processing completes.
86
- // Only update if we're still on the same page (historyKey matches).
87
- if (historyKey === store.getHistoryKey()) {
202
+ // Only update if we're still on the same page AND this is still the live nav
203
+ // (the token guard stops a stale same-URL nav writing a newer nav's state).
204
+ if (stillLive()) {
88
205
  const finalHandleData = eventController.getHandleState().data;
89
206
  store.updateCacheHandleData(historyKey, finalHandleData);
90
207
  }
@@ -133,15 +250,33 @@ export interface NavigationProviderProps {
133
250
  warmupEnabled?: boolean;
134
251
 
135
252
  /**
136
- * App version from server payload (stable, immutable).
137
- * Forwarded to context for cache key building.
253
+ * App version from server payload.
254
+ * Used only as a fallback when `appShellRef` is not supplied.
138
255
  */
139
256
  version?: string;
140
257
 
141
258
  /**
142
259
  * URL prefix for all routes (from createRouter({ basename })).
260
+ * Used only as a fallback when `appShellRef` is not supplied.
143
261
  */
144
262
  basename?: string;
263
+
264
+ /**
265
+ * App-shell ref. When provided, the context's `basename` and `version` are
266
+ * read through it (live getters) so they don't close over a stale snapshot or
267
+ * invalidate the memoized context value. The shell is set once at init and is
268
+ * not swapped within a session — a cross-app navigation is a full document
269
+ * load (X-RSC-Reload), so the target app establishes its own shell on load.
270
+ */
271
+ appShellRef?: AppShellRef;
272
+
273
+ /**
274
+ * CSP nonce to expose via NonceContext. Production leaves this undefined — the
275
+ * browser has no nonce (it is a server-side HTML concern), and SSR provides the
276
+ * nonce through its own NonceContext.Provider. Test harnesses (renderRoute) set
277
+ * it to seed a nonce so components calling useNonce() can be exercised.
278
+ */
279
+ nonce?: string;
145
280
  }
146
281
 
147
282
  /**
@@ -175,6 +310,8 @@ export function NavigationProvider({
175
310
  warmupEnabled,
176
311
  version,
177
312
  basename,
313
+ appShellRef,
314
+ nonce,
178
315
  }: NavigationProviderProps): ReactNode {
179
316
  // Track current payload for rendering (this triggers re-renders)
180
317
  const [payload, setPayload] = useState(initialPayload);
@@ -196,104 +333,43 @@ export function NavigationProvider({
196
333
  await bridge.refresh();
197
334
  }, []);
198
335
 
199
- // Context value is stable (store, eventController, navigate, refresh never change)
200
- const contextValue = useMemo<NavigationStoreContextValue>(
201
- () => ({
336
+ // basename/version are always read through a shell ref so the context value
337
+ // has a single shape. Both are set once: a supplied appShellRef is seeded
338
+ // from the init payload (a cross-app navigation reloads, so it is not swapped
339
+ // in-session), and the standalone fallback wraps the mount-time props.
340
+ const fallbackShellRef = useRef<AppShellRef | null>(null);
341
+ if (!fallbackShellRef.current) {
342
+ fallbackShellRef.current = createAppShellRef({ basename, version });
343
+ }
344
+ const shellRef = appShellRef ?? fallbackShellRef.current;
345
+
346
+ const contextValue = useMemo<NavigationStoreContextValue>(() => {
347
+ const value = {
202
348
  store,
203
349
  eventController,
204
350
  navigate,
205
351
  refresh,
206
- version,
207
- basename,
208
- }),
209
- [],
210
- );
352
+ } as NavigationStoreContextValue;
353
+ Object.defineProperty(value, "basename", {
354
+ configurable: true,
355
+ enumerable: true,
356
+ get: () => shellRef.get().basename,
357
+ });
358
+ Object.defineProperty(value, "version", {
359
+ configurable: true,
360
+ enumerable: true,
361
+ get: () => shellRef.get().version,
362
+ });
363
+ return value;
364
+ }, []);
211
365
 
212
- // Connection warmup: keep TLS alive after idle periods.
213
- // After 60s of no user interaction, marks connection as "cold".
214
- // On next interaction or visibility change, sends a HEAD request to warm TLS
215
- // before the user actually clicks a link.
366
+ // Connection warmup: keep TLS alive after idle periods. After 60s of no
367
+ // interaction the connection is marked cold; the next pointer/touch
368
+ // interaction or visibility change warms TLS via a HEAD request before the
369
+ // user clicks a link. State machine lives in connection-warmup.ts.
216
370
  useEffect(() => {
217
371
  if (!warmupEnabled) return;
218
-
219
- const IDLE_TIMEOUT = 60_000;
220
- const DEBOUNCE_DELAY = 150;
221
-
222
- let idleTimer: ReturnType<typeof setTimeout> | undefined;
223
- let debounceTimer: ReturnType<typeof setTimeout> | undefined;
224
- let isCold = false;
225
- let warmupListenersAttached = false;
226
-
227
- function sendWarmup() {
228
- isCold = false;
229
- fetch("/?_rsc_warmup", { method: "HEAD" }).catch(() => {});
230
- }
231
-
232
- function triggerWarmup() {
233
- if (!isCold) return;
234
- clearTimeout(debounceTimer);
235
- debounceTimer = setTimeout(() => {
236
- sendWarmup();
237
- detachWarmupListeners();
238
- resetIdleTimer();
239
- }, DEBOUNCE_DELAY);
240
- }
241
-
242
- function onVisibilityChange() {
243
- if (document.visibilityState === "visible" && isCold) {
244
- triggerWarmup();
245
- }
246
- }
247
-
248
- function attachWarmupListeners() {
249
- if (warmupListenersAttached) return;
250
- warmupListenersAttached = true;
251
- document.addEventListener("visibilitychange", onVisibilityChange);
252
- document.addEventListener("mousemove", triggerWarmup, { once: true });
253
- document.addEventListener("touchstart", triggerWarmup, { once: true });
254
- }
255
-
256
- function detachWarmupListeners() {
257
- warmupListenersAttached = false;
258
- document.removeEventListener("visibilitychange", onVisibilityChange);
259
- document.removeEventListener("mousemove", triggerWarmup);
260
- document.removeEventListener("touchstart", triggerWarmup);
261
- }
262
-
263
- function markCold() {
264
- isCold = true;
265
- attachWarmupListeners();
266
- }
267
-
268
- function resetIdleTimer() {
269
- clearTimeout(idleTimer);
270
- isCold = false;
271
- idleTimer = setTimeout(markCold, IDLE_TIMEOUT);
272
- }
273
-
274
- // Activity events that reset the idle timer
275
- const activityEvents = [
276
- "mousemove",
277
- "keydown",
278
- "touchstart",
279
- "scroll",
280
- ] as const;
281
- const activityOptions: AddEventListenerOptions = { passive: true };
282
-
283
- for (const event of activityEvents) {
284
- document.addEventListener(event, resetIdleTimer, activityOptions);
285
- }
286
-
287
- resetIdleTimer();
288
-
289
- return () => {
290
- clearTimeout(idleTimer);
291
- clearTimeout(debounceTimer);
292
- detachWarmupListeners();
293
- for (const event of activityEvents) {
294
- document.removeEventListener(event, resetIdleTimer);
295
- }
296
- };
372
+ return startConnectionWarmup();
297
373
  }, [warmupEnabled]);
298
374
 
299
375
  // Cancel non-matching prefetches when navigation starts.
@@ -345,8 +421,12 @@ export function NavigationProvider({
345
421
  metadata: update.metadata,
346
422
  });
347
423
 
348
- // Update route params
349
- eventController.setParams(update.metadata.params ?? {});
424
+ // Update route params. Only reset when the server actually sends a params
425
+ // map — an absent `params` field means "no change" (e.g., legacy action
426
+ // responses that omitted params). Explicit `{}` still clears correctly.
427
+ if (update.metadata.params !== undefined) {
428
+ eventController.setParams(update.metadata.params);
429
+ }
350
430
 
351
431
  // Update handle data progressively as it streams in
352
432
  if (update.metadata.handles) {
@@ -359,24 +439,20 @@ export function NavigationProvider({
359
439
  store,
360
440
  matched: update.metadata.matched,
361
441
  isPartial: update.metadata.isPartial,
442
+ resolvedIds: update.metadata.resolvedIds,
362
443
  historyKey,
363
444
  }).catch((err) =>
364
445
  console.error("[NavigationProvider] Error consuming handles:", err),
365
446
  );
366
- } else if (update.metadata.cachedHandleData) {
367
- // For back/forward navigation from cache, restore the cached handleData
368
- // This restores breadcrumbs to the exact state they were when the page was cached
369
- eventController.setHandleData(
370
- update.metadata.cachedHandleData,
371
- update.metadata.matched,
372
- false, // full replace - restore entire cached state
373
- );
374
447
  } else if (update.metadata.matched) {
375
- // For cached navigations without handleData, update segmentOrder to clean up stale data
448
+ // cachedHandleData present -> full restore (back/forward); absent ->
449
+ // partial cleanup of segments no longer matched.
450
+ const cached = update.metadata.cachedHandleData;
376
451
  eventController.setHandleData(
377
- {}, // Empty data - all existing data not in matched will be cleaned up
452
+ cached ?? {},
378
453
  update.metadata.matched,
379
- true, // partial update - will clean up segments not in matched
454
+ cached === undefined,
455
+ cached === undefined ? update.metadata.resolvedIds : undefined,
380
456
  );
381
457
  }
382
458
  });
@@ -389,7 +465,8 @@ export function NavigationProvider({
389
465
  payload.root instanceof Promise ? use(payload.root) : payload.root;
390
466
 
391
467
  // Wrap content in RootErrorBoundary to catch:
392
- // 1. Errors from NetworkErrorThrower (rendered during network failures)
468
+ // 1. Errors from RenderErrorThrower (network failures and unprocessable
469
+ // navigation responses, routed here by the navigation bridge)
393
470
  // 2. Client component errors that occur before/outside the segment tree's error boundary
394
471
  // 3. Errors during promise resolution or navigation state updates
395
472
  // This acts as a safety net - the segment tree has its own RootErrorBoundary that
@@ -398,7 +475,11 @@ export function NavigationProvider({
398
475
  // Build the content tree
399
476
  let content = <RootErrorBoundary>{root}</RootErrorBoundary>;
400
477
 
401
- // Wrap with ThemeProvider when theme is enabled
478
+ // Wrap with ThemeProvider when theme is enabled. The ThemeProvider is
479
+ // document-lifetime: its config comes from the initial load and persists for
480
+ // the session. It sits above the segment tree and is not remounted in-session;
481
+ // a cross-app navigation is a full document load (X-RSC-Reload), so the target
482
+ // app's theme config takes effect on its own load.
402
483
  if (themeConfig) {
403
484
  content = (
404
485
  <ThemeProvider config={themeConfig} initialTheme={initialTheme}>
@@ -409,9 +490,10 @@ export function NavigationProvider({
409
490
 
410
491
  // Match SSR tree shape: NonceContext.Provider is always present so
411
492
  // hydration sees the same component tree. Value is undefined on the
412
- // client — CSP nonces are a server-side HTML concern.
493
+ // client — CSP nonces are a server-side HTML concern — unless a test
494
+ // harness seeded one via the `nonce` prop.
413
495
  content = (
414
- <NonceContext.Provider value={undefined}>{content}</NonceContext.Provider>
496
+ <NonceContext.Provider value={nonce}>{content}</NonceContext.Provider>
415
497
  );
416
498
 
417
499
  return (
@@ -14,17 +14,21 @@ export interface ScrollRestorationProps {
14
14
  * Return location.pathname to restore scroll based on path
15
15
  * (useful for keeping scroll position on the same page).
16
16
  *
17
+ * Provide a stable reference: a module-level function or one wrapped in
18
+ * useCallback. The init effect re-runs when getKey's identity changes, and
19
+ * teardown clears in-memory scroll positions — a fresh inline arrow on every
20
+ * parent render would discard unpersisted positions mid-session.
21
+ *
17
22
  * @example
18
23
  * ```tsx
24
+ * // Stable module-level getKey (recommended)
25
+ * const byPathname = (location) => location.pathname;
26
+ *
19
27
  * // Restore based on pathname (same URL = same scroll)
20
- * <ScrollRestoration
21
- * getKey={(location) => location.pathname}
22
- * />
28
+ * <ScrollRestoration getKey={byPathname} />
23
29
  *
24
30
  * // Restore based on unique history entry (default)
25
- * <ScrollRestoration
26
- * getKey={(location) => location.key}
27
- * />
31
+ * // <ScrollRestoration /> — omit getKey to use location.key
28
32
  * ```
29
33
  */
30
34
  getKey?: (location: {
@@ -1,11 +1,70 @@
1
1
  /**
2
- * Filter segment IDs to only include routes and layouts.
3
- * Excludes parallels (contain .@) and loaders (contain D followed by digit).
2
+ * Build the handle-collection segment order from a raw `matched` list.
3
+ *
4
+ * Two responsibilities:
5
+ *
6
+ * 1. Drop loader sub-ids ("D" followed by a digit, e.g. "M0L0D1.user") —
7
+ * loaders never push handles.
8
+ *
9
+ * 2. Place each parallel slot id (contains ".@") immediately after its
10
+ * parent layout/route id. Raw segment-resolution emission order does NOT
11
+ * guarantee this: route-mounted parallels are resolved/pushed BEFORE the
12
+ * route handler's segment is appended (see fresh.ts:resolveSegment for
13
+ * routes, and revalidation.ts ~915-919), so matched can read
14
+ * `[..., R0.@panel, R0]`. collectHandleData consumes segmentOrder verbatim
15
+ * with later-wins semantics, so without normalization the route handler's
16
+ * Meta would override the slot's more-specific Meta — backwards.
17
+ *
18
+ * Slot-id format is `<parentShortCode>.@<slotName>`; `parentShortCode` never
19
+ * contains ".@", so splitting at the first ".@" reliably yields the parent.
4
20
  */
5
21
  export function filterSegmentOrder(matched: string[]): string[] {
6
- return matched.filter((id) => {
7
- if (id.includes(".@")) return false;
8
- if (/D\d+\./.test(id)) return false;
9
- return true;
10
- });
22
+ const slotsByParent = new Map<string, string[]>();
23
+ const nonSlots: string[] = [];
24
+ const nonSlotSet = new Set<string>();
25
+
26
+ for (const id of matched) {
27
+ if (/D\d+\./.test(id)) continue;
28
+ const slotIdx = id.indexOf(".@");
29
+ if (slotIdx >= 0) {
30
+ const parent = id.slice(0, slotIdx);
31
+ const list = slotsByParent.get(parent);
32
+ if (list) {
33
+ list.push(id);
34
+ } else {
35
+ slotsByParent.set(parent, [id]);
36
+ }
37
+ } else {
38
+ nonSlots.push(id);
39
+ nonSlotSet.add(id);
40
+ }
41
+ }
42
+
43
+ const result: string[] = [];
44
+ for (const id of nonSlots) {
45
+ result.push(id);
46
+ const slots = slotsByParent.get(id);
47
+ if (slots) result.push(...slots);
48
+ }
49
+ for (const [parent, slots] of slotsByParent) {
50
+ if (!nonSlotSet.has(parent)) result.push(...slots);
51
+ }
52
+ return result;
53
+ }
54
+
55
+ /**
56
+ * Build the "layouts and routes only" id list for useSegments().segmentIds.
57
+ *
58
+ * Strips parallel slot ids (contain ".@") and loader sub-ids ("D" followed by
59
+ * a digit, e.g. "M0L0D1.user") without reordering. Distinct from
60
+ * filterSegmentOrder, which also reorders slots after their parent for handle
61
+ * collection.
62
+ *
63
+ * Shared by SSR (ssr/index.tsx) and the client event controller
64
+ * (event-controller.ts) so both produce identical output; if they diverge,
65
+ * useSegments().segmentIds rendered during SSR and after hydration disagree
66
+ * and React reports a hydration mismatch.
67
+ */
68
+ export function filterRouteSegmentIds(matched: string[]): string[] {
69
+ return matched.filter((id) => !id.includes(".@") && !/D\d+\./.test(id));
11
70
  }
@@ -1,52 +1,4 @@
1
- // React exports for browser navigation
2
-
3
- // Hook with Zustand-style selectors
4
- export { useNavigation } from "./use-navigation.js";
5
-
6
- // Router actions hook (stable reference, no re-renders)
7
- export { useRouter } from "./use-router.js";
8
-
9
- // URL hooks
10
- export { usePathname } from "./use-pathname.js";
11
- export { useSearchParams } from "./use-search-params.js";
12
- export { useParams } from "./use-params.js";
13
-
14
- // Action state tracking hook
15
- export { useAction, type TrackedActionState } from "./use-action.js";
16
-
17
- // Segments state hook
18
- export { useSegments, type SegmentsState } from "./use-segments.js";
19
-
20
- // Handle data hook
21
- export { useHandle } from "./use-handle.js";
22
-
23
- // Client cache controls hook
24
- export {
25
- useClientCache,
26
- type ClientCacheControls,
27
- } from "./use-client-cache.js";
28
-
29
- // Provider
30
1
  export {
31
2
  NavigationProvider,
32
3
  type NavigationProviderProps,
33
4
  } from "./NavigationProvider.js";
34
-
35
- // Context (for advanced usage)
36
- export {
37
- NavigationStoreContext,
38
- type NavigationStoreContextValue,
39
- } from "./context.js";
40
-
41
- // Link component
42
- export { Link, type LinkProps, type PrefetchStrategy } from "./Link.js";
43
-
44
- // Link status hook
45
- export { useLinkStatus, type LinkStatus } from "./use-link-status.js";
46
-
47
- // Scroll restoration
48
- export {
49
- ScrollRestoration,
50
- useScrollRestoration,
51
- type ScrollRestorationProps,
52
- } from "./ScrollRestoration.js";