@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
@@ -1,34 +1,100 @@
1
1
  /**
2
2
  * Prefetch Cache
3
3
  *
4
- * In-memory cache storing prefetched Response objects for instant cache hits
5
- * on subsequent navigation. Cache key is source-dependent (includes the
6
- * current page URL) because the server's diff-based response depends on
7
- * where the user navigates from.
4
+ * In-memory cache storing eagerly-decoded prefetch payloads for instant,
5
+ * already-warm cache hits on subsequent navigation. A prefetch fetches the
6
+ * RSC partial AND decodes it (createFromFetch) up front decoding the Flight
7
+ * stream resolves the route's client references, so the route's JS chunks are
8
+ * imported during prefetch rather than on click. The decoded payload is reused
9
+ * verbatim by navigation, so a prefetched click loads no new code. Two key
10
+ * scopes are in play:
11
+ * - Wildcard (default): built by `buildPrefetchKey(rangoState, target)` —
12
+ * shape `rangoState\0/target?...`. Shared across all source pages and
13
+ * invalidated automatically when Rango state bumps (deploy or
14
+ * server-action invalidation).
15
+ * - Source-scoped: built by `buildSourceKey(rangoState, sourceHref, target)`
16
+ * — shape `rangoState\0sourceHref\0/target?...`. Embeds the Rango state
17
+ * (so rotation invalidates source-scoped entries too) plus the source
18
+ * href (so each originating page gets its own slot). Populated when the
19
+ * server tags a response with `X-RSC-Prefetch-Scope: source` (intercept
20
+ * modals etc.), OR when a Link opts in with `prefetchKey=":source"` — in
21
+ * both cases so source-sensitive responses cannot bleed into navigations
22
+ * from other pages.
8
23
  *
9
24
  * Also tracks in-flight prefetch promises. Each promise resolves to the
10
- * navigation branch of a tee'd Response, allowing navigation to adopt a
11
- * still-downloading prefetch without reparsing or buffering the body.
25
+ * decoded prefetch entry (or null), letting navigation adopt a
26
+ * still-downloading prefetch without issuing a duplicate request. A
27
+ * single promise can be registered under multiple alias keys (see
28
+ * `setInflightPromiseWithAliases`) so same-source navigations adopt via
29
+ * their source key while cross-source ones fall through to the wildcard
30
+ * alias — with consume/clear atomically removing every alias.
12
31
  *
13
32
  * Replaces the previous browser HTTP cache approach which was unreliable
14
33
  * due to response draining race conditions and browser inconsistencies.
34
+ *
35
+ * State here lives in module-level singletons (cache, inflight, generation,
36
+ * cacheTTL, etc.) rather than a per-instance factory. This is correct because
37
+ * exactly one router is live per document — an SPA navigation crossing a
38
+ * host-router boundary forces a full document reload — so the singletons are
39
+ * effectively per-document. Unit tests reset them via clearPrefetchCache().
15
40
  */
16
41
 
17
42
  import { abortAllPrefetches } from "./queue.js";
18
43
  import { invalidateRangoState } from "../rango-state.js";
44
+ import type { RscPayload } from "../types.js";
45
+
46
+ /**
47
+ * A prefetch that has been fetched AND eagerly decoded. Storing the decoded
48
+ * payload (not the raw Response) is what makes a prefetched navigation "warm":
49
+ * decoding the Flight stream during prefetch pulls the route's client chunks,
50
+ * so the click reuses ready elements and loads no new JS.
51
+ */
52
+ export interface DecodedPrefetch {
53
+ /** The eagerly-decoded RSC payload. Reused verbatim by navigation. */
54
+ payload: Promise<RscPayload>;
55
+ /**
56
+ * Resolves when the underlying RSC stream finishes draining. Navigation
57
+ * forwards this as its streamComplete so scroll/revalidation gating is
58
+ * unchanged from the fresh-fetch path.
59
+ */
60
+ streamComplete: Promise<void>;
61
+ /**
62
+ * Prefetch scope as tagged by the server via `X-RSC-Prefetch-Scope`.
63
+ * `"source"` means the response is source-page-sensitive and must not be
64
+ * reused by a navigation from a different page — navigation enforces this
65
+ * when it adopted an inflight entry through the wildcard key.
66
+ */
67
+ scope: "source" | "wildcard";
68
+ /**
69
+ * Synchronously-readable flag, flipped to true when `streamComplete` resolves
70
+ * (the entire RSC stream has drained). Navigation reads this at click time to
71
+ * tell a FULLY warmed prefetch (payload commits without suspending → safe to
72
+ * commit in a startTransition, no fallback flash) from a partially-warmed one
73
+ * (still streaming → stream its fallbacks like a cold load). Starts false.
74
+ */
75
+ complete: boolean;
76
+ }
19
77
 
20
- // Default TTL: 5 minutes. Overridden by initPrefetchCache() with
21
- // the server-configured prefetchCacheTTL from router options.
22
- // 0 disables the in-memory cache entirely.
23
78
  let cacheTTL = 300_000;
24
79
 
80
+ // Max stored entries before FIFO eviction. Mirrors DEFAULT_PREFETCH_CACHE_SIZE
81
+ // (router/prefetch-limits.ts); kept as a local literal so the client bundle
82
+ // doesn't pull in router-layer code, matching the cacheTTL default above.
83
+ // Overridden at startup by initPrefetchCache from server metadata.
84
+ let maxPrefetchCacheSize = 100;
85
+
25
86
  /**
26
- * Initialize the prefetch cache with the configured TTL.
27
- * Called once at app startup with the value from server metadata.
28
- * A TTL of 0 disables the in-memory cache and all prefetching.
87
+ * Initialize the prefetch cache with the configured TTL and max size.
88
+ * Called once at app startup with the values from server metadata. Each
89
+ * argument is applied only when provided, so a caller can set just the TTL.
90
+ * A TTL of 0 disables the in-memory cache and all prefetching. A size below 1
91
+ * is ignored (the default is kept) — disabling prefetch is the TTL's job.
29
92
  */
30
- export function initPrefetchCache(ttlMs: number): void {
31
- cacheTTL = ttlMs;
93
+ export function initPrefetchCache(ttlMs?: number, maxSize?: number): void {
94
+ if (ttlMs !== undefined) cacheTTL = ttlMs;
95
+ if (maxSize !== undefined && Number.isFinite(maxSize) && maxSize >= 1) {
96
+ maxPrefetchCacheSize = Math.floor(maxSize);
97
+ }
32
98
  }
33
99
 
34
100
  /**
@@ -38,46 +104,85 @@ export function initPrefetchCache(ttlMs: number): void {
38
104
  export function isPrefetchCacheDisabled(): boolean {
39
105
  return cacheTTL <= 0;
40
106
  }
41
- const MAX_PREFETCH_CACHE_SIZE = 50;
42
107
 
43
108
  interface PrefetchCacheEntry {
44
- response: Response;
109
+ entry: DecodedPrefetch;
45
110
  timestamp: number;
46
111
  }
47
112
 
48
113
  const cache = new Map<string, PrefetchCacheEntry>();
49
114
  const inflight = new Set<string>();
50
115
 
51
- /**
52
- * In-flight promise map. When a prefetch fetch is in progress, its
53
- * Promise<Response | null> is stored here so navigation can await
54
- * it instead of starting a duplicate request.
55
- */
56
- const inflightPromises = new Map<string, Promise<Response | null>>();
116
+ // Watermark: the smallest entry timestamp currently in `cache`, i.e. a lower
117
+ // bound on when the oldest entry expires (timestamp + cacheTTL). storePrefetch's
118
+ // eager expiry sweep is skipped while `now - earliestTimestamp <= cacheTTL` — the
119
+ // oldest entry cannot be expired yet, so nothing would be evicted. Deletions
120
+ // (consume/has/remove) may leave it stale-LOW, which only forces a harmless extra
121
+ // sweep that recomputes it exactly; it is never stale-high, so a skipped sweep
122
+ // can never retain an expired entry. consume/has still TTL-check lazily, so a
123
+ // skipped sweep never serves a stale entry. Infinity when the cache is empty.
124
+ let earliestTimestamp = Infinity;
125
+
126
+ const inflightPromises = new Map<string, Promise<DecodedPrefetch | null>>();
127
+
128
+ const inflightAliases = new Map<string, string[]>();
129
+
130
+ const adoptedKeys = new Set<string>();
57
131
 
58
- // Generation counter incremented on each clearPrefetchCache(). Fetches that
59
- // started before a clear carry a stale generation and must not store their
60
- // response (the data may be stale due to a server action invalidation).
61
132
  let generation = 0;
62
133
 
63
134
  /**
64
- * Build a cache key for prefetched responses.
135
+ * Build a cache key by combining a scope prefix with the target URL.
136
+ *
137
+ * Low-level primitive — callers that want a specific scope should use
138
+ * one of:
139
+ * - Wildcard (source-agnostic): prefix is the Rango state value from
140
+ * `getRangoState()`. Shared across all source pages. Invalidated
141
+ * automatically when Rango state bumps (deploy or server-action).
142
+ * Key shape: `rangoState\0/target?...`.
143
+ * - Source-scoped: use `buildSourceKey()`. Key shape:
144
+ * `rangoState\0sourceHref\0/target?...` — embeds the Rango state so
145
+ * rotation invalidates source-scoped entries alongside wildcard ones,
146
+ * plus the source page href so the key is unique per originating page.
147
+ * Populated either when the server tags a response with
148
+ * `X-RSC-Prefetch-Scope: source` (intercept modals, etc.) or when a
149
+ * Link opts in via `prefetchKey=":source"`.
65
150
  *
66
- * By default the key includes the source page href so the same target
67
- * prefetched from different pages gets separate entries (the server's
68
- * diff response depends on the source page context).
151
+ * The `_rsc_segments` query param that travels in the target URL means
152
+ * clients with different mounted segment trees naturally get different
153
+ * keys so segment-level diffs remain consistent across both scopes.
154
+ */
155
+ export function buildPrefetchKey(prefix: string, targetUrl: URL): string {
156
+ return prefix + "\0" + targetUrl.pathname + targetUrl.search;
157
+ }
158
+
159
+ /**
160
+ * Build a source-scoped cache key. Key shape:
161
+ * `rangoState\0sourceHref\0/target?...`.
69
162
  *
70
- * When `prefetchKey` is provided, the source portion is replaced with
71
- * a `*` sentinel so all custom-keyed entries share one cache slot per
72
- * target enabling source-agnostic cache reuse.
163
+ * - `rangoState` is included so state rotation invalidates source-scoped
164
+ * entries alongside wildcard ones.
165
+ * - `sourceHref` makes the key unique per originating page.
73
166
  */
74
- export function buildPrefetchKey(
167
+ export function buildSourceKey(
168
+ rangoState: string,
75
169
  sourceHref: string,
76
170
  targetUrl: URL,
77
- prefetchKey?: string | ((from: string) => string),
78
171
  ): string {
79
- const source = prefetchKey != null ? "*" : sourceHref;
80
- return source + "\0" + targetUrl.pathname + targetUrl.search;
172
+ return buildPrefetchKey(rangoState + "\0" + sourceHref, targetUrl);
173
+ }
174
+
175
+ /**
176
+ * Walk an inflight key plus any sibling aliases registered via
177
+ * `setInflightPromiseWithAliases`, invoking `fn` for each.
178
+ */
179
+ function forEachAlias(key: string, fn: (k: string) => void): void {
180
+ const aliases = inflightAliases.get(key);
181
+ if (aliases) {
182
+ for (const k of aliases) fn(k);
183
+ } else {
184
+ fn(key);
185
+ }
81
186
  }
82
187
 
83
188
  /**
@@ -96,14 +201,14 @@ export function hasPrefetch(key: string): boolean {
96
201
  }
97
202
 
98
203
  /**
99
- * Consume a cached prefetch response. Returns null if not found or expired.
100
- * One-time consumption: the entry is deleted after retrieval.
204
+ * Consume a cached, eagerly-decoded prefetch. Returns null if not found or
205
+ * expired. One-time consumption: the entry is deleted after retrieval.
101
206
  * Returns null when caching is disabled (TTL <= 0).
102
207
  *
103
208
  * Does NOT check in-flight prefetches — use consumeInflightPrefetch()
104
- * for that (returns a Promise instead of a Response).
209
+ * for that (returns a Promise instead of a resolved entry).
105
210
  */
106
- export function consumePrefetch(key: string): Response | null {
211
+ export function consumePrefetch(key: string): DecodedPrefetch | null {
107
212
  if (cacheTTL <= 0) return null;
108
213
  const entry = cache.get(key);
109
214
  if (!entry) return null;
@@ -112,63 +217,111 @@ export function consumePrefetch(key: string): Response | null {
112
217
  return null;
113
218
  }
114
219
  cache.delete(key);
115
- return entry.response;
220
+ return entry.entry;
116
221
  }
117
222
 
118
223
  /**
119
224
  * Consume an in-flight prefetch promise. Returns null if no prefetch is
120
- * in-flight for this key. The returned Promise resolves to the buffered
121
- * Response (or null if the fetch failed/was aborted).
225
+ * in-flight for this key. The returned Promise resolves to the decoded
226
+ * prefetch entry (or null if the fetch failed/was aborted, or carried a
227
+ * control header the navigation must re-fetch to honor).
122
228
  *
123
- * One-time consumption: the promise entry is removed so a second call
124
- * returns null. The `inflight` set entry is intentionally kept so that
125
- * hasPrefetch() continues to return true while the underlying fetch is
126
- * still downloading this prevents prefetchDirect() or other callers
127
- * from starting a duplicate request during the handoff window. The
128
- * inflight flag is cleaned up naturally by clearPrefetchInflight() in
129
- * the fetch's .finally().
229
+ * One-time consumption: the promise entry is removed (along with any
230
+ * sibling aliases registered via `setInflightPromiseWithAliases`) so a
231
+ * second call on any alias returns null only one caller can adopt the
232
+ * shared Response stream. The `inflight` set entry is intentionally
233
+ * kept so that `hasPrefetch()` continues to return true while the
234
+ * underlying fetch is still downloading this prevents
235
+ * `prefetchDirect()` or other callers from starting a duplicate request
236
+ * during the handoff window. The inflight flag is cleaned up naturally
237
+ * by `clearPrefetchInflight()` in the fetch's `.finally()`.
130
238
  */
131
239
  export function consumeInflightPrefetch(
132
240
  key: string,
133
- ): Promise<Response | null> | null {
241
+ ): Promise<DecodedPrefetch | null> | null {
134
242
  const promise = inflightPromises.get(key);
135
243
  if (!promise) return null;
136
- // Remove the promise (one-time consumption) but keep the inflight flag.
137
- inflightPromises.delete(key);
244
+ // Remove the promise under every alias so a second consumer cannot
245
+ // adopt the same stream and race on the body, and mark every alias as
246
+ // adopted so the pending `storePrefetch` (which resolves later, after this
247
+ // adoption) does not leave the now-owned, single-use entry in the cache map.
248
+ // `inflightAliases` is intentionally preserved — `clearPrefetchInflight()` in
249
+ // the fetch's `.finally()` still needs it to clear every inflight flag and
250
+ // adopted marker; deleting here would strand the sibling's flag forever.
251
+ forEachAlias(key, (k) => {
252
+ inflightPromises.delete(k);
253
+ adoptedKeys.add(k);
254
+ });
138
255
  return promise;
139
256
  }
140
257
 
141
258
  /**
142
- * Store a prefetch response in the in-memory cache.
143
- * The response should be a clone() of the original so the caller can
144
- * still consume the body. The clone's body streams independently.
259
+ * Store an eagerly-decoded prefetch in the in-memory cache.
145
260
  *
146
261
  * Skips storage if the generation has changed since the fetch started
147
262
  * (a server action invalidated the cache mid-flight).
148
263
  */
149
264
  export function storePrefetch(
150
265
  key: string,
151
- response: Response,
266
+ entry: DecodedPrefetch,
152
267
  fetchGeneration: number,
153
268
  ): void {
154
269
  if (cacheTTL <= 0) return;
155
270
  if (fetchGeneration !== generation) return;
156
271
 
157
- // Evict expired entries
272
+ // If a navigation already adopted this prefetch's in-flight promise, it owns
273
+ // the single-use entry (and has drained its handle generator). Do NOT also
274
+ // publish it to the cache map, or a later navigation would be served the
275
+ // exhausted entry and lose that route's handles. Clear the marker (under all
276
+ // aliases) now that the decision is made.
277
+ if (adoptedKeys.has(key)) {
278
+ forEachAlias(key, (k) => adoptedKeys.delete(k));
279
+ return;
280
+ }
281
+
282
+ // Evict expired entries — but only when the watermark says the oldest entry
283
+ // could have expired, so a burst of stores no longer walks the whole map each
284
+ // time. The sweep recomputes the exact watermark from the survivors.
158
285
  const now = Date.now();
159
- for (const [k, entry] of cache) {
160
- if (now - entry.timestamp > cacheTTL) {
161
- cache.delete(k);
286
+ if (now - earliestTimestamp > cacheTTL) {
287
+ let min = Infinity;
288
+ for (const [k, cached] of cache) {
289
+ if (now - cached.timestamp > cacheTTL) {
290
+ cache.delete(k);
291
+ } else if (cached.timestamp < min) {
292
+ min = cached.timestamp;
293
+ }
162
294
  }
295
+ earliestTimestamp = min;
163
296
  }
164
297
 
165
298
  // FIFO eviction if at capacity
166
- if (cache.size >= MAX_PREFETCH_CACHE_SIZE) {
299
+ if (cache.size >= maxPrefetchCacheSize) {
167
300
  const oldest = cache.keys().next().value;
168
301
  if (oldest) cache.delete(oldest);
169
302
  }
170
303
 
171
- cache.set(key, { response, timestamp: now });
304
+ cache.set(key, { entry, timestamp: now });
305
+ // The new entry is the newest, so it only moves the watermark when the cache
306
+ // was empty (Infinity); otherwise the older watermark stands.
307
+ if (now < earliestTimestamp) earliestTimestamp = now;
308
+ }
309
+
310
+ /**
311
+ * Remove a single stored prefetch entry. Used to evict an entry whose body
312
+ * stream stalled after headers arrived (its payload / streamComplete never
313
+ * settle), so future prefetches and navigation refetch instead of dedupe-ing
314
+ * against — and awaiting — a stuck entry.
315
+ *
316
+ * Identity-guarded: only evicts when the entry CURRENTLY stored under `key` is
317
+ * the exact `entry` we published. A generation check alone is insufficient —
318
+ * after this entry is consumed (consumePrefetch) and a fresh prefetch
319
+ * republishes under the SAME key in the SAME generation, a gen-only guard would
320
+ * delete that valid newer entry. Reference identity drops only our own stalled
321
+ * entry.
322
+ */
323
+ export function removePrefetch(key: string, entry: DecodedPrefetch): void {
324
+ if (cache.get(key)?.entry === entry) cache.delete(key);
172
325
  }
173
326
 
174
327
  /**
@@ -183,34 +336,46 @@ export function markPrefetchInflight(key: string): void {
183
336
  inflight.add(key);
184
337
  }
185
338
 
186
- /**
187
- * Store the in-flight Promise for a prefetch so navigation can reuse it.
188
- */
189
339
  export function setInflightPromise(
190
340
  key: string,
191
- promise: Promise<Response | null>,
341
+ promise: Promise<DecodedPrefetch | null>,
192
342
  ): void {
193
343
  inflightPromises.set(key, promise);
194
344
  }
195
345
 
346
+ /**
347
+ * Store the same in-flight Promise under multiple keys, recording them
348
+ * as sibling aliases. Consuming or clearing any one alias atomically
349
+ * removes every entry, guaranteeing the shared Response stream has a
350
+ * single consumer even when navigation looks up either key.
351
+ */
352
+ export function setInflightPromiseWithAliases(
353
+ keys: string[],
354
+ promise: Promise<DecodedPrefetch | null>,
355
+ ): void {
356
+ for (const k of keys) {
357
+ inflightPromises.set(k, promise);
358
+ inflightAliases.set(k, keys);
359
+ }
360
+ }
361
+
196
362
  export function clearPrefetchInflight(key: string): void {
197
- inflight.delete(key);
198
- inflightPromises.delete(key);
363
+ forEachAlias(key, (k) => {
364
+ inflight.delete(k);
365
+ inflightPromises.delete(k);
366
+ inflightAliases.delete(k);
367
+ adoptedKeys.delete(k);
368
+ });
199
369
  }
200
370
 
201
- /**
202
- * Invalidate all prefetch state. Called when server actions mutate data.
203
- * Clears the in-memory cache, cancels in-flight prefetches, and rotates
204
- * the Rango state key so CDN-cached responses are also invalidated.
205
- *
206
- * Uses abortAllPrefetches (hard cancel) because in-flight responses
207
- * may contain stale data after a mutation.
208
- */
209
371
  export function clearPrefetchCache(): void {
210
372
  generation++;
211
373
  inflight.clear();
212
374
  inflightPromises.clear();
375
+ inflightAliases.clear();
376
+ adoptedKeys.clear();
213
377
  cache.clear();
378
+ earliestTimestamp = Infinity;
214
379
  abortAllPrefetches();
215
380
  invalidateRangoState();
216
381
  }