@rangojs/router 0.0.0-experimental.9c9afef3 → 0.0.0-experimental.a014d2b7

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 (402) hide show
  1. package/AGENTS.md +8 -0
  2. package/README.md +245 -49
  3. package/dist/bin/rango.js +440 -133
  4. package/dist/testing/vitest.js +82 -0
  5. package/dist/vite/index.js +3373 -1176
  6. package/dist/vite/index.js.bak +5448 -0
  7. package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
  8. package/package.json +68 -14
  9. package/skills/api-client/SKILL.md +211 -0
  10. package/skills/breadcrumbs/SKILL.md +64 -2
  11. package/skills/bundle-analysis/SKILL.md +159 -0
  12. package/skills/cache-guide/SKILL.md +224 -32
  13. package/skills/caching/SKILL.md +279 -17
  14. package/skills/composability/SKILL.md +27 -3
  15. package/skills/css/SKILL.md +76 -0
  16. package/skills/debug-manifest/SKILL.md +4 -2
  17. package/skills/document-cache/SKILL.md +78 -55
  18. package/skills/handler-use/SKILL.md +364 -0
  19. package/skills/hooks/SKILL.md +250 -30
  20. package/skills/host-router/SKILL.md +83 -23
  21. package/skills/i18n/SKILL.md +276 -0
  22. package/skills/intercept/SKILL.md +87 -18
  23. package/skills/layout/SKILL.md +35 -9
  24. package/skills/links/SKILL.md +249 -17
  25. package/skills/loader/SKILL.md +235 -9
  26. package/skills/middleware/SKILL.md +52 -13
  27. package/skills/migrate-nextjs/SKILL.md +584 -0
  28. package/skills/migrate-react-router/SKILL.md +771 -0
  29. package/skills/mime-routes/SKILL.md +28 -1
  30. package/skills/observability/SKILL.md +172 -0
  31. package/skills/parallel/SKILL.md +77 -7
  32. package/skills/prerender/SKILL.md +172 -125
  33. package/skills/rango/SKILL.md +251 -22
  34. package/skills/react-compiler/SKILL.md +168 -0
  35. package/skills/response-routes/SKILL.md +123 -48
  36. package/skills/route/SKILL.md +70 -5
  37. package/skills/router-setup/SKILL.md +65 -8
  38. package/skills/scripts/SKILL.md +179 -0
  39. package/skills/server-actions/SKILL.md +775 -0
  40. package/skills/streams-and-websockets/SKILL.md +283 -0
  41. package/skills/tailwind/SKILL.md +27 -3
  42. package/skills/testing/SKILL.md +130 -0
  43. package/skills/testing/bindings.md +103 -0
  44. package/skills/testing/cache-prerender.md +127 -0
  45. package/skills/testing/client-components.md +124 -0
  46. package/skills/testing/e2e-parity.md +125 -0
  47. package/skills/testing/flight.md +91 -0
  48. package/skills/testing/handles.md +129 -0
  49. package/skills/testing/loader.md +128 -0
  50. package/skills/testing/middleware.md +99 -0
  51. package/skills/testing/render-handler.md +122 -0
  52. package/skills/testing/response-routes.md +95 -0
  53. package/skills/testing/reverse-and-types.md +84 -0
  54. package/skills/testing/server-actions.md +107 -0
  55. package/skills/testing/server-tree.md +128 -0
  56. package/skills/testing/setup.md +123 -0
  57. package/skills/typesafety/SKILL.md +322 -29
  58. package/skills/use-cache/SKILL.md +57 -14
  59. package/skills/view-transitions/SKILL.md +337 -0
  60. package/src/__augment-tests__/augment.ts +81 -0
  61. package/src/__augment-tests__/augmented.check.ts +116 -0
  62. package/src/__internal.ts +1 -66
  63. package/src/browser/action-coordinator.ts +53 -36
  64. package/src/browser/action-fence.ts +47 -0
  65. package/src/browser/app-shell.ts +39 -0
  66. package/src/browser/app-version.ts +14 -0
  67. package/src/browser/connection-warmup.ts +134 -0
  68. package/src/browser/cookie-name.ts +140 -0
  69. package/src/browser/event-controller.ts +192 -150
  70. package/src/browser/history-state.ts +21 -0
  71. package/src/browser/index.ts +3 -3
  72. package/src/browser/invalidate-client-cache.ts +52 -0
  73. package/src/browser/navigation-bridge.ts +131 -30
  74. package/src/browser/navigation-client.ts +186 -100
  75. package/src/browser/navigation-store-handle.ts +38 -0
  76. package/src/browser/navigation-store.ts +157 -74
  77. package/src/browser/navigation-transaction.ts +9 -59
  78. package/src/browser/network-error-handler.ts +34 -7
  79. package/src/browser/partial-update.ts +165 -112
  80. package/src/browser/prefetch/cache.ts +205 -62
  81. package/src/browser/prefetch/fetch.ts +347 -39
  82. package/src/browser/prefetch/queue.ts +42 -8
  83. package/src/browser/rango-state.ts +158 -76
  84. package/src/browser/react/Link.tsx +102 -15
  85. package/src/browser/react/NavigationProvider.tsx +295 -119
  86. package/src/browser/react/ScrollRestoration.tsx +10 -6
  87. package/src/browser/react/context.ts +7 -2
  88. package/src/browser/react/deferred-handle-resolution.ts +75 -0
  89. package/src/browser/react/filter-segment-order.ts +66 -7
  90. package/src/browser/react/index.ts +0 -48
  91. package/src/browser/react/location-state-shared.ts +178 -8
  92. package/src/browser/react/location-state.ts +39 -14
  93. package/src/browser/react/use-action.ts +6 -15
  94. package/src/browser/react/use-handle.ts +23 -69
  95. package/src/browser/react/use-href.tsx +8 -1
  96. package/src/browser/react/use-link-status.ts +33 -8
  97. package/src/browser/react/use-navigation.ts +32 -7
  98. package/src/browser/react/use-params.ts +20 -10
  99. package/src/browser/react/use-reverse.ts +106 -0
  100. package/src/browser/react/use-router.ts +46 -11
  101. package/src/browser/react/use-search-params.ts +0 -5
  102. package/src/browser/react/use-segments.ts +11 -21
  103. package/src/browser/response-adapter.ts +99 -8
  104. package/src/browser/rsc-router.tsx +114 -24
  105. package/src/browser/scroll-restoration.ts +37 -22
  106. package/src/browser/segment-reconciler.ts +36 -14
  107. package/src/browser/segment-structure-assert.ts +2 -2
  108. package/src/browser/server-action-bridge.ts +222 -72
  109. package/src/browser/types.ts +102 -12
  110. package/src/browser/validate-redirect-origin.ts +43 -16
  111. package/src/build/collect-fallback-refs.ts +107 -0
  112. package/src/build/generate-manifest.ts +65 -40
  113. package/src/build/generate-route-types.ts +5 -1
  114. package/src/build/index.ts +8 -2
  115. package/src/build/prefix-tree-utils.ts +123 -0
  116. package/src/build/route-trie.ts +165 -36
  117. package/src/build/route-types/ast-route-extraction.ts +15 -8
  118. package/src/build/route-types/codegen.ts +16 -5
  119. package/src/build/route-types/include-resolution.ts +125 -24
  120. package/src/build/route-types/param-extraction.ts +6 -3
  121. package/src/build/route-types/per-module-writer.ts +22 -6
  122. package/src/build/route-types/router-processing.ts +260 -94
  123. package/src/build/route-types/scan-filter.ts +9 -2
  124. package/src/build/route-types/source-scan.ts +216 -0
  125. package/src/build/runtime-discovery.ts +9 -20
  126. package/src/cache/cache-error.ts +104 -0
  127. package/src/cache/cache-key-utils.ts +29 -13
  128. package/src/cache/cache-policy.ts +108 -34
  129. package/src/cache/cache-runtime.ts +224 -41
  130. package/src/cache/cache-scope.ts +188 -82
  131. package/src/cache/cache-tag.ts +103 -0
  132. package/src/cache/cf/cf-base64.ts +33 -0
  133. package/src/cache/cf/cf-cache-constants.ts +127 -0
  134. package/src/cache/cf/cf-cache-store.ts +1989 -378
  135. package/src/cache/cf/cf-cache-types.ts +349 -0
  136. package/src/cache/cf/cf-kv-utils.ts +46 -0
  137. package/src/cache/cf/cf-tag-marker-memo.ts +105 -0
  138. package/src/cache/cf/index.ts +6 -16
  139. package/src/cache/document-cache.ts +89 -21
  140. package/src/cache/handle-snapshot.ts +70 -0
  141. package/src/cache/index.ts +10 -20
  142. package/src/cache/memory-segment-store.ts +136 -37
  143. package/src/cache/profile-registry.ts +46 -31
  144. package/src/cache/read-through-swr.ts +56 -12
  145. package/src/cache/segment-codec.ts +9 -17
  146. package/src/cache/tag-invalidation.ts +230 -0
  147. package/src/cache/types.ts +37 -100
  148. package/src/client.rsc.tsx +44 -21
  149. package/src/client.tsx +119 -290
  150. package/src/cloudflare/index.ts +11 -0
  151. package/src/cloudflare/tracing.ts +109 -0
  152. package/src/component-utils.ts +19 -0
  153. package/src/components/DefaultDocument.tsx +8 -2
  154. package/src/context-var.ts +18 -6
  155. package/src/decode-loader-results.ts +52 -0
  156. package/src/defer.ts +196 -0
  157. package/src/deps/ssr.ts +0 -1
  158. package/src/encode-kv.ts +49 -0
  159. package/src/errors.ts +30 -4
  160. package/src/escape-script.ts +52 -0
  161. package/src/handle.ts +70 -22
  162. package/src/handles/MetaTags.tsx +62 -19
  163. package/src/handles/Scripts.tsx +183 -0
  164. package/src/handles/breadcrumbs.ts +37 -8
  165. package/src/handles/is-thenable.ts +19 -0
  166. package/src/handles/meta.ts +51 -40
  167. package/src/handles/script.ts +244 -0
  168. package/src/host/cookie-handler.ts +9 -60
  169. package/src/host/errors.ts +0 -24
  170. package/src/host/index.ts +8 -2
  171. package/src/host/pattern-matcher.ts +23 -52
  172. package/src/host/router.ts +107 -99
  173. package/src/host/testing.ts +40 -27
  174. package/src/host/types.ts +37 -4
  175. package/src/host/utils.ts +1 -1
  176. package/src/href-client.ts +137 -22
  177. package/src/index.rsc.ts +99 -13
  178. package/src/index.ts +139 -19
  179. package/src/internal-debug.ts +11 -10
  180. package/src/loader-store.ts +500 -0
  181. package/src/loader.rsc.ts +20 -13
  182. package/src/loader.ts +12 -11
  183. package/src/missing-id-error.ts +68 -0
  184. package/src/outlet-context.ts +1 -1
  185. package/src/outlet-provider.tsx +1 -5
  186. package/src/prerender/param-hash.ts +16 -16
  187. package/src/prerender/store.ts +37 -41
  188. package/src/prerender.ts +198 -82
  189. package/src/redirect-origin.ts +100 -0
  190. package/src/regex-escape.ts +8 -0
  191. package/src/render-error-thrower.tsx +20 -0
  192. package/src/response-utils.ts +62 -0
  193. package/src/reverse.ts +65 -15
  194. package/src/root-error-boundary.tsx +1 -19
  195. package/src/route-content-wrapper.tsx +19 -77
  196. package/src/route-definition/dsl-helpers.ts +461 -304
  197. package/src/route-definition/helper-factories.ts +28 -140
  198. package/src/route-definition/helpers-types.ts +143 -69
  199. package/src/route-definition/index.ts +4 -2
  200. package/src/route-definition/redirect.ts +51 -10
  201. package/src/route-definition/resolve-handler-use.ts +160 -0
  202. package/src/route-definition/use-item-types.ts +29 -0
  203. package/src/route-map-builder.ts +0 -16
  204. package/src/route-types.ts +37 -46
  205. package/src/router/basename.ts +14 -0
  206. package/src/router/content-negotiation.ts +164 -17
  207. package/src/router/error-handling.ts +45 -18
  208. package/src/router/find-match.ts +44 -23
  209. package/src/router/handler-context.ts +52 -31
  210. package/src/router/instrument.ts +350 -0
  211. package/src/router/intercept-resolution.ts +48 -24
  212. package/src/router/lazy-includes.ts +15 -52
  213. package/src/router/loader-resolution.ts +268 -56
  214. package/src/router/logging.ts +0 -6
  215. package/src/router/manifest.ts +40 -42
  216. package/src/router/match-api.ts +124 -204
  217. package/src/router/match-context.ts +0 -22
  218. package/src/router/match-handlers.ts +58 -58
  219. package/src/router/match-middleware/background-revalidation.ts +40 -24
  220. package/src/router/match-middleware/cache-lookup.ts +170 -276
  221. package/src/router/match-middleware/cache-store.ts +64 -52
  222. package/src/router/match-middleware/intercept-resolution.ts +0 -22
  223. package/src/router/match-middleware/segment-resolution.ts +45 -14
  224. package/src/router/match-pipelines.ts +1 -42
  225. package/src/router/match-result.ts +87 -39
  226. package/src/router/metrics.ts +0 -34
  227. package/src/router/middleware-types.ts +7 -140
  228. package/src/router/middleware.ts +266 -169
  229. package/src/router/navigation-snapshot.ts +131 -0
  230. package/src/router/params-util.ts +23 -0
  231. package/src/router/pattern-matching.ts +132 -90
  232. package/src/router/prefetch-cache-ttl.ts +51 -0
  233. package/src/router/prerender-match.ts +195 -56
  234. package/src/router/preview-match.ts +32 -102
  235. package/src/router/request-classification.ts +276 -0
  236. package/src/router/revalidation.ts +123 -73
  237. package/src/router/route-snapshot.ts +244 -0
  238. package/src/router/router-context.ts +3 -28
  239. package/src/router/router-interfaces.ts +115 -35
  240. package/src/router/router-options.ts +172 -15
  241. package/src/router/router-registry.ts +2 -5
  242. package/src/router/segment-resolution/fresh.ts +162 -84
  243. package/src/router/segment-resolution/helpers.ts +86 -6
  244. package/src/router/segment-resolution/loader-cache.ts +76 -39
  245. package/src/router/segment-resolution/revalidation.ts +351 -321
  246. package/src/router/segment-resolution/static-store.ts +19 -5
  247. package/src/router/segment-resolution/streamed-handler-telemetry.ts +52 -0
  248. package/src/router/segment-resolution/view-transition-default.ts +56 -0
  249. package/src/router/segment-resolution.ts +5 -1
  250. package/src/router/segment-wrappers.ts +6 -5
  251. package/src/router/state-cookie-name.ts +33 -0
  252. package/src/router/substitute-pattern-params.ts +56 -0
  253. package/src/router/telemetry-otel.ts +161 -199
  254. package/src/router/telemetry.ts +96 -19
  255. package/src/router/timeout.ts +0 -20
  256. package/src/router/tracing.ts +206 -0
  257. package/src/router/trie-matching.ts +163 -59
  258. package/src/router/types.ts +9 -63
  259. package/src/router/url-params.ts +44 -0
  260. package/src/router.ts +157 -54
  261. package/src/rsc/handler-context.ts +3 -2
  262. package/src/rsc/handler.ts +655 -529
  263. package/src/rsc/helpers.ts +168 -46
  264. package/src/rsc/index.ts +2 -5
  265. package/src/rsc/json-route-result.ts +38 -0
  266. package/src/rsc/loader-fetch.ts +122 -31
  267. package/src/rsc/manifest-init.ts +33 -42
  268. package/src/rsc/origin-guard.ts +39 -25
  269. package/src/rsc/progressive-enhancement.ts +131 -14
  270. package/src/rsc/redirect-guard.ts +99 -0
  271. package/src/rsc/response-cache-serve.ts +238 -0
  272. package/src/rsc/response-error.ts +79 -12
  273. package/src/rsc/response-route-handler.ts +99 -189
  274. package/src/rsc/rsc-rendering.ts +109 -74
  275. package/src/rsc/runtime-warnings.ts +23 -10
  276. package/src/rsc/server-action.ts +287 -115
  277. package/src/rsc/ssr-setup.ts +18 -2
  278. package/src/rsc/transition-gate.ts +89 -0
  279. package/src/rsc/types.ts +29 -9
  280. package/src/runtime-env.ts +18 -0
  281. package/src/search-params.ts +35 -30
  282. package/src/segment-content-promise.ts +67 -0
  283. package/src/segment-loader-promise.ts +149 -0
  284. package/src/segment-system.tsx +236 -202
  285. package/src/serialize.ts +243 -0
  286. package/src/server/context.ts +224 -52
  287. package/src/server/cookie-parse.ts +32 -0
  288. package/src/server/cookie-store.ts +80 -5
  289. package/src/server/handle-store.ts +40 -38
  290. package/src/server/loader-registry.ts +38 -46
  291. package/src/server/request-context.ts +401 -173
  292. package/src/ssr/index.tsx +24 -16
  293. package/src/static-handler.ts +27 -18
  294. package/src/testing/cache-status.ts +162 -0
  295. package/src/testing/collect-handle.ts +40 -0
  296. package/src/testing/dispatch.ts +701 -0
  297. package/src/testing/dom.entry.ts +22 -0
  298. package/src/testing/e2e/fixture.ts +188 -0
  299. package/src/testing/e2e/index.ts +128 -0
  300. package/src/testing/e2e/matchers.ts +35 -0
  301. package/src/testing/e2e/page-helpers.ts +272 -0
  302. package/src/testing/e2e/parity.ts +387 -0
  303. package/src/testing/e2e/server.ts +195 -0
  304. package/src/testing/flight-matchers.ts +97 -0
  305. package/src/testing/flight-normalize.ts +11 -0
  306. package/src/testing/flight-runtime.d.ts +57 -0
  307. package/src/testing/flight-tree.ts +682 -0
  308. package/src/testing/flight.entry.ts +52 -0
  309. package/src/testing/flight.ts +257 -0
  310. package/src/testing/generated-routes.ts +183 -0
  311. package/src/testing/index.ts +105 -0
  312. package/src/testing/internal/context.ts +371 -0
  313. package/src/testing/internal/flight-client-globals.ts +30 -0
  314. package/src/testing/internal/seed-vars.ts +54 -0
  315. package/src/testing/render-handler.ts +357 -0
  316. package/src/testing/render-route.tsx +581 -0
  317. package/src/testing/run-loader.ts +385 -0
  318. package/src/testing/run-middleware.ts +205 -0
  319. package/src/testing/run-transition-when.ts +164 -0
  320. package/src/testing/vitest-stubs/cloudflare-email.ts +9 -0
  321. package/src/testing/vitest-stubs/cloudflare-workers.ts +21 -0
  322. package/src/testing/vitest-stubs/plugin-rsc.ts +16 -0
  323. package/src/testing/vitest-stubs/version.ts +5 -0
  324. package/src/testing/vitest.ts +305 -0
  325. package/src/theme/ThemeProvider.tsx +20 -58
  326. package/src/theme/ThemeScript.tsx +7 -9
  327. package/src/theme/constants.ts +52 -13
  328. package/src/theme/index.ts +0 -7
  329. package/src/theme/theme-context.ts +1 -5
  330. package/src/theme/theme-script.ts +22 -21
  331. package/src/theme/use-theme.ts +0 -3
  332. package/src/types/boundaries.ts +0 -35
  333. package/src/types/cache-types.ts +17 -8
  334. package/src/types/error-types.ts +30 -90
  335. package/src/types/global-namespace.ts +54 -41
  336. package/src/types/handler-context.ts +125 -71
  337. package/src/types/index.ts +3 -10
  338. package/src/types/loader-types.ts +40 -11
  339. package/src/types/request-scope.ts +112 -0
  340. package/src/types/route-config.ts +6 -50
  341. package/src/types/route-entry.ts +12 -7
  342. package/src/types/segments.ts +136 -15
  343. package/src/urls/include-helper.ts +33 -70
  344. package/src/urls/index.ts +1 -11
  345. package/src/urls/path-helper-types.ts +68 -18
  346. package/src/urls/path-helper.ts +57 -111
  347. package/src/urls/pattern-types.ts +48 -19
  348. package/src/urls/response-types.ts +25 -22
  349. package/src/urls/type-extraction.ts +58 -139
  350. package/src/urls/urls-function.ts +1 -19
  351. package/src/use-loader.tsx +346 -89
  352. package/src/vite/debug.ts +185 -0
  353. package/src/vite/discovery/bundle-postprocess.ts +36 -38
  354. package/src/vite/discovery/discover-routers.ts +130 -85
  355. package/src/vite/discovery/discovery-errors.ts +194 -0
  356. package/src/vite/discovery/gate-state.ts +171 -0
  357. package/src/vite/discovery/prerender-collection.ts +214 -132
  358. package/src/vite/discovery/route-types-writer.ts +40 -84
  359. package/src/vite/discovery/self-gen-tracking.ts +27 -1
  360. package/src/vite/discovery/state.ts +57 -4
  361. package/src/vite/discovery/virtual-module-codegen.ts +14 -34
  362. package/src/vite/index.ts +6 -0
  363. package/src/vite/inject-client-debug.ts +36 -0
  364. package/src/vite/plugin-types.ts +178 -5
  365. package/src/vite/plugins/cjs-to-esm.ts +16 -19
  366. package/src/vite/plugins/client-ref-dedup.ts +16 -11
  367. package/src/vite/plugins/client-ref-hashing.ts +28 -15
  368. package/src/vite/plugins/cloudflare-protocol-loader-hook.d.mts +23 -0
  369. package/src/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
  370. package/src/vite/plugins/cloudflare-protocol-stub.ts +194 -0
  371. package/src/vite/plugins/expose-action-id.ts +48 -95
  372. package/src/vite/plugins/expose-id-utils.ts +96 -51
  373. package/src/vite/plugins/expose-ids/export-analysis.ts +101 -34
  374. package/src/vite/plugins/expose-ids/handler-transform.ts +15 -64
  375. package/src/vite/plugins/expose-ids/loader-transform.ts +14 -24
  376. package/src/vite/plugins/expose-ids/router-transform.ts +118 -29
  377. package/src/vite/plugins/expose-internal-ids.ts +553 -317
  378. package/src/vite/plugins/performance-tracks.ts +64 -170
  379. package/src/vite/plugins/refresh-cmd.ts +89 -27
  380. package/src/vite/plugins/use-cache-transform.ts +73 -83
  381. package/src/vite/plugins/version-injector.ts +40 -29
  382. package/src/vite/plugins/version-plugin.ts +37 -40
  383. package/src/vite/plugins/virtual-entries.ts +39 -25
  384. package/src/vite/rango.ts +118 -114
  385. package/src/vite/router-discovery.ts +941 -142
  386. package/src/vite/utils/ast-handler-extract.ts +26 -35
  387. package/src/vite/utils/banner.ts +1 -1
  388. package/src/vite/utils/bundle-analysis.ts +10 -15
  389. package/src/vite/utils/client-chunks.ts +184 -0
  390. package/src/vite/utils/directive-prologue.ts +40 -0
  391. package/src/vite/utils/forward-user-plugins.ts +171 -0
  392. package/src/vite/utils/manifest-utils.ts +4 -59
  393. package/src/vite/utils/package-resolution.ts +20 -52
  394. package/src/vite/utils/prerender-utils.ts +81 -34
  395. package/src/vite/utils/shared-utils.ts +92 -42
  396. package/src/browser/action-response-classifier.ts +0 -99
  397. package/src/browser/debug-channel.ts +0 -93
  398. package/src/browser/react/use-client-cache.ts +0 -58
  399. package/src/browser/shallow.ts +0 -40
  400. package/src/handles/index.ts +0 -7
  401. package/src/network-error-thrower.tsx +0 -23
  402. package/src/router/middleware-cookies.ts +0 -55
@@ -4,13 +4,16 @@ import type {
4
4
  NavigateOptionsInternal,
5
5
  ResolvedSegment,
6
6
  } from "./types.js";
7
+ import { setAppVersion } from "./app-version.js";
8
+ import { isActionFenceActive } from "./action-fence.js";
9
+ import { getRangoState } from "./rango-state.js";
7
10
  import * as React from "react";
8
11
  import { startTransition } from "react";
9
12
  import {
10
13
  createNavigationTransaction,
11
14
  resolveNavigationState,
12
15
  } from "./navigation-transaction.js";
13
- import { buildHistoryState } from "./history-state.js";
16
+ import { buildHistoryState, pushHistoryWithIdx } from "./history-state.js";
14
17
  import {
15
18
  handleNavigationStart,
16
19
  handleNavigationEnd,
@@ -29,6 +32,7 @@ import { isInterceptOnlyCache } from "./intercept-utils.js";
29
32
  import {
30
33
  toNetworkError,
31
34
  emitNetworkError,
35
+ emitNavigationError,
32
36
  isBackgroundSuppressible,
33
37
  } from "./network-error-handler.js";
34
38
  import { debugLog } from "./logging.js";
@@ -47,7 +51,7 @@ export { createNavigationTransaction };
47
51
  */
48
52
  export interface NavigationBridgeConfigWithController extends NavigationBridgeConfig {
49
53
  eventController: EventController;
50
- /** RSC version from initial payload metadata */
54
+ /** RSC version from initial payload metadata. */
51
55
  version?: string;
52
56
  }
53
57
 
@@ -67,8 +71,8 @@ export interface NavigationBridgeConfigWithController extends NavigationBridgeCo
67
71
  export function createNavigationBridge(
68
72
  config: NavigationBridgeConfigWithController,
69
73
  ): NavigationBridge {
70
- const { store, client, eventController, onUpdate, renderSegments, version } =
71
- config;
74
+ const { store, client, eventController, onUpdate, renderSegments } = config;
75
+ let version = config.version;
72
76
 
73
77
  // Create shared partial updater
74
78
  const fetchPartialUpdate = createPartialUpdater({
@@ -76,7 +80,7 @@ export function createNavigationBridge(
76
80
  client,
77
81
  onUpdate,
78
82
  renderSegments,
79
- version,
83
+ getVersion: () => version,
80
84
  });
81
85
 
82
86
  return {
@@ -158,11 +162,7 @@ export function createNavigationBridge(
158
162
  },
159
163
  {},
160
164
  );
161
- if (options.replace) {
162
- window.history.replaceState(historyState, "", url);
163
- } else {
164
- window.history.pushState(historyState, "", url);
165
- }
165
+ pushHistoryWithIdx(historyState, url, options?.replace ?? false);
166
166
 
167
167
  // Ensure new history entry has a scroll restoration key
168
168
  ensureHistoryKey();
@@ -216,17 +216,20 @@ export function createNavigationBridge(
216
216
  store.setInterceptSourceUrl(null);
217
217
  }
218
218
 
219
- // Before navigating away, update the source page's cache with the latest handleData.
220
- // This ensures the cache has correct handleData even if handles were streaming.
219
+ // Before navigating away, update the source page's cache with the latest
220
+ // handleData. This ensures the cache has correct handleData even if handles
221
+ // were streaming. Use updateCacheHandleData (not cacheSegmentsForHistory):
222
+ // the source page's segments are unchanged, so this is a handleData refresh,
223
+ // not a commit. Critically it PRESERVES the entry's stale flag — when the
224
+ // source page has a deferred Meta still pending, its entry was marked stale
225
+ // (invalidate-on-pending) so a popstate return revalidates; re-committing it
226
+ // here would reset stale to false and serve the carried (pre-resolution)
227
+ // title as fresh. It also leaves the nav-instance token intact.
221
228
  const sourceHistoryKey = store.getHistoryKey();
222
229
  const sourceCached = store.getCachedSegments(sourceHistoryKey);
223
230
  if (sourceCached?.segments && sourceCached.segments.length > 0) {
224
231
  const currentHandleData = eventController.getHandleState().data;
225
- store.cacheSegmentsForHistory(
226
- sourceHistoryKey,
227
- sourceCached.segments,
228
- currentHandleData,
229
- );
232
+ store.updateCacheHandleData(sourceHistoryKey, currentHandleData);
230
233
  }
231
234
 
232
235
  // Check if we have cached segments for target URL
@@ -260,18 +263,24 @@ export function createNavigationBridge(
260
263
  // 2. routes that CAN be intercepted - we don't know if this navigation will intercept
261
264
  // 3. when leaving intercept - we need fresh non-intercept segments from server
262
265
  // 4. redirect-with-state - force re-render so hooks read fresh state
266
+ // 5. stale cache - server action invalidated it, need fresh data with loading state
263
267
  const hasUsableCache =
264
268
  cachedSegments &&
265
269
  cachedSegments.length > 0 &&
266
270
  !isInterceptOnlyCache(cachedSegments) &&
267
271
  !hasInterceptCache &&
268
272
  !isLeavingIntercept &&
273
+ !cached?.stale &&
269
274
  !options?._skipCache;
270
275
 
276
+ // Forward navigations always await fetchPartialUpdate before rendering,
277
+ // so useNavigation should always report "loading". skipLoadingState is
278
+ // only used for popstate background revalidation (line ~526) where
279
+ // cached content renders instantly without a network wait.
271
280
  const tx = createNavigationTransaction(store, eventController, url, {
272
281
  ...options,
273
282
  state: resolvedState,
274
- skipLoadingState: hasUsableCache,
283
+ skipLoadingState: false,
275
284
  });
276
285
 
277
286
  // REVALIDATE: Fetch fresh data from server
@@ -320,8 +329,15 @@ export function createNavigationBridge(
320
329
  } as NavigateOptionsInternal);
321
330
  }
322
331
 
323
- if (error instanceof DOMException && error.name === "AbortError") {
324
- debugLog("[Browser] Navigation aborted by newer navigation");
332
+ // Aborted, or superseded by a newer navigation. A superseded nav may
333
+ // reject with a non-AbortError (e.g. a Flight decode that fails after its
334
+ // signal was aborted), so check the signal too -- otherwise we would
335
+ // render a boundary that clobbers the newer navigation's content.
336
+ if (
337
+ (error instanceof DOMException && error.name === "AbortError") ||
338
+ tx.handle.signal.aborted
339
+ ) {
340
+ debugLog("[Browser] Navigation aborted or superseded");
325
341
  return;
326
342
  }
327
343
 
@@ -338,7 +354,13 @@ export function createNavigationBridge(
338
354
  return;
339
355
  }
340
356
 
341
- throw error;
357
+ // A response we could not process (undecodable Flight body, or an
358
+ // unanticipated failure building the response). Surface the route's
359
+ // error boundary rather than let the rejection abort the navigation
360
+ // silently. Prefetched responses funnel here too: a failed warm-prefetch
361
+ // payload rejects on consumption and propagates to this catch.
362
+ console.error("[Browser] Unprocessable navigation response:", error);
363
+ emitNavigationError(onUpdate, error, url);
342
364
  } finally {
343
365
  tx[Symbol.dispose]();
344
366
  }
@@ -367,6 +389,14 @@ export function createNavigationBridge(
367
389
  tx.with({ url: window.location.href, replace: true, scroll: false }),
368
390
  );
369
391
  } catch (error) {
392
+ // Aborted or superseded: bail without rendering a boundary (see navigate()).
393
+ if (
394
+ (error instanceof DOMException && error.name === "AbortError") ||
395
+ tx.handle.signal.aborted
396
+ ) {
397
+ return;
398
+ }
399
+
370
400
  const networkError = toNetworkError(error, {
371
401
  url: window.location.href,
372
402
  operation: "revalidation",
@@ -379,7 +409,12 @@ export function createNavigationBridge(
379
409
  emitNetworkError(onUpdate, networkError, window.location.href);
380
410
  return;
381
411
  }
382
- throw error;
412
+
413
+ // refresh() shares the fetchPartialUpdate chokepoint with navigate()/
414
+ // popstate, so an unprocessable response must surface the error boundary
415
+ // here too rather than become an uncaught rejection.
416
+ console.error("[Browser] Unprocessable refresh response:", error);
417
+ emitNavigationError(onUpdate, error, window.location.href);
383
418
  } finally {
384
419
  tx[Symbol.dispose]();
385
420
  }
@@ -411,6 +446,15 @@ export function createNavigationBridge(
411
446
  eventController.abortAllActions();
412
447
  }
413
448
 
449
+ // Popstate that exits an intercept to a non-intercept destination. The
450
+ // fallback fetch path below needs `leave-intercept` mode so it filters
451
+ // the cached @modal segment from the request and forces a re-render —
452
+ // otherwise a cache-miss popstate whose server response has an empty
453
+ // diff hits the "no changes" branch in partial-update and the modal
454
+ // stays on screen.
455
+ const isLeavingIntercept =
456
+ !isIntercept && currentInterceptSource !== null;
457
+
414
458
  // Compute history key from URL (with intercept suffix if applicable)
415
459
  const historyKey = generateHistoryKey(url, { intercept: isIntercept });
416
460
 
@@ -434,11 +478,22 @@ export function createNavigationBridge(
434
478
  // Helper to check if streaming is in progress
435
479
  const isStreaming = () => eventController.getState().isStreaming;
436
480
 
481
+ // Surface any external rotation of the rango state cookie (a server
482
+ // Set-Cookie, a sibling tab, a cookie clear) BEFORE reading the stale bit.
483
+ // The divergence observer only runs inside getRangoState() — fetch-time —
484
+ // so a popstate-first interaction would otherwise serve a pre-mutation
485
+ // page as fresh and never fetch to trigger the observer. Reading here lets
486
+ // the observer mark the history cache stale so getCachedSegments sees it.
487
+ getRangoState();
488
+
437
489
  // Check if we can restore from history cache
438
490
  const cached = store.getCachedSegments(historyKey);
439
491
  const cachedSegments = cached?.segments;
440
492
  const cachedHandleData = cached?.handleData;
441
- const isStale = cached?.stale ?? false;
493
+ // While an action is in flight the fence persists no stale flag, so OR it
494
+ // in here: a popstate during the flight serves the cached entry AND
495
+ // revalidates (SWR) instead of serving it as fresh.
496
+ const isStale = (cached?.stale ?? false) || isActionFenceActive();
442
497
 
443
498
  if (cachedSegments && cachedSegments.length > 0) {
444
499
  // Update store to point to this history entry
@@ -447,6 +502,12 @@ export function createNavigationBridge(
447
502
  store.setCurrentUrl(url);
448
503
  store.setPath(new URL(url).pathname);
449
504
 
505
+ // Restore router identity from cache so subsequent navigations
506
+ // don't falsely detect an app switch.
507
+ if (cached?.routerId) {
508
+ store.setRouterId?.(cached.routerId);
509
+ }
510
+
450
511
  // Render from cache - force await to skip loading fallbacks
451
512
  try {
452
513
  const root = await renderSegments(cachedSegments, {
@@ -474,7 +535,14 @@ export function createNavigationBridge(
474
535
  },
475
536
  scroll: { restore: true, isStreaming },
476
537
  };
477
- const hasTransition = cachedSegments.some((s) => s.transition);
538
+ // Intercept-driven popstate (entering OR leaving an intercept) only
539
+ // mutates the parallel slot; the main outlet shows the same content.
540
+ // Skip startViewTransition in those cases — same rationale as the
541
+ // intercept guard in partial-update.ts's hasTransition computation.
542
+ const hasTransition =
543
+ !isIntercept &&
544
+ !isLeavingIntercept &&
545
+ cachedSegments.some((s) => s.transition);
478
546
  if (hasTransition) {
479
547
  startTransition(() => {
480
548
  if (addTransitionType) {
@@ -489,8 +557,19 @@ export function createNavigationBridge(
489
557
  // SWR: If stale, trigger background revalidation
490
558
  if (isStale) {
491
559
  debugLog("[Browser] Cache is stale, background revalidating...");
492
- // Background revalidation - don't await, just fire and forget
493
- const segmentIds = cachedSegments.map((s) => s.id);
560
+ // Background revalidation - don't await, just fire and forget.
561
+ // When the entry's handles are incomplete (a deferred Meta was still
562
+ // pending when the user navigated away — see handlesPending), send NO
563
+ // segment IDs so the server returns a FULL re-render with the handle
564
+ // stream. A normal stale revalidation sends the cached IDs and the
565
+ // server returns a diff-only payload that omits unchanged segments'
566
+ // handles, so a deferred Meta would never re-stream and the title
567
+ // would stay the pre-resolution carry. handlesPending is set only for
568
+ // the deferred-Meta-aborted case, so action/cross-tab SWR keeps the
569
+ // cheap diff path.
570
+ const segmentIds = cached?.handlesPending
571
+ ? []
572
+ : cachedSegments.map((s) => s.id);
494
573
 
495
574
  const tx = createNavigationTransaction(
496
575
  store,
@@ -555,13 +634,22 @@ export function createNavigationBridge(
555
634
  intercept: isIntercept,
556
635
  interceptSourceUrl,
557
636
  }),
558
- isIntercept ? { type: "navigate", interceptSourceUrl } : undefined,
637
+ isIntercept
638
+ ? { type: "navigate", interceptSourceUrl }
639
+ : isLeavingIntercept
640
+ ? { type: "leave-intercept" }
641
+ : undefined,
559
642
  );
560
643
  // Restore scroll position after fetch completes
561
644
  handleNavigationEnd({ restore: true, isStreaming });
562
645
  } catch (error) {
563
- if (error instanceof DOMException && error.name === "AbortError") {
564
- debugLog("[Browser] Popstate navigation aborted");
646
+ // Aborted or superseded by a newer navigation: bail without clobbering
647
+ // content with a boundary (see navigate()).
648
+ if (
649
+ (error instanceof DOMException && error.name === "AbortError") ||
650
+ tx.handle.signal.aborted
651
+ ) {
652
+ debugLog("[Browser] Popstate navigation aborted or superseded");
565
653
  return;
566
654
  }
567
655
 
@@ -578,7 +666,10 @@ export function createNavigationBridge(
578
666
  return;
579
667
  }
580
668
 
581
- throw error;
669
+ // Unprocessable response on a back/forward navigation: surface the
670
+ // error boundary instead of an uncaught rejection (see navigate()).
671
+ console.error("[Browser] Unprocessable popstate response:", error);
672
+ emitNavigationError(onUpdate, error, url);
582
673
  } finally {
583
674
  tx[Symbol.dispose]();
584
675
  }
@@ -632,6 +723,16 @@ export function createNavigationBridge(
632
723
  window.removeEventListener("pageshow", handlePageShow);
633
724
  };
634
725
  },
726
+
727
+ getVersion(): string | undefined {
728
+ return version;
729
+ },
730
+
731
+ updateVersion(newVersion: string): void {
732
+ version = newVersion;
733
+ setAppVersion(newVersion);
734
+ store.clearHistoryCache();
735
+ },
635
736
  };
636
737
  }
637
738