@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,5 +1,8 @@
1
1
  import { tryTrieMatch } from "./trie-matching.js";
2
- import { getRouteTrie, getRouterTrie } from "../route-map-builder.js";
2
+ import {
3
+ getRouterTrie,
4
+ isRouterTrieAuthoritative,
5
+ } from "../route-map-builder.js";
3
6
  import {
4
7
  findMatch as findRouteMatch,
5
8
  isLazyEvaluationNeeded,
@@ -8,39 +11,55 @@ import {
8
11
  import type { MetricsStore } from "../server/context";
9
12
  import type { RouteEntry } from "../types";
10
13
 
14
+ // The single-entry cache is module-lifetime, keyed only on pathname, so the same
15
+ // result object is handed to every same-pathname request. ctx.params aliases
16
+ // this object, so handlers mutating it would corrupt the cache for later requests.
17
+ // Clone params; entry/flags are read-only and shared safely.
18
+ function cloneMatchResult<TEnv>(
19
+ r: RouteMatchResult<TEnv> | null,
20
+ ): RouteMatchResult<TEnv> | null {
21
+ return r ? { ...r, params: { ...r.params } } : null;
22
+ }
23
+
11
24
  export interface FindMatchDeps<TEnv = any> {
12
25
  routesEntries: RouteEntry<TEnv>[];
13
- evaluateLazyEntry: (entry: RouteEntry<TEnv>) => void;
26
+ // Returns a Promise only when it had to resolve an async include provider
27
+ // (`() => import()`); void for eager/precomputed includes, so the per-entry
28
+ // match loop pays no microtask in the common case.
29
+ evaluateLazyEntry: (entry: RouteEntry<TEnv>) => void | Promise<void>;
14
30
  routerId: string;
15
31
  }
16
32
 
17
33
  /**
18
34
  * Create a findMatch function bound to router state.
19
35
  * Includes single-entry cache to avoid redundant matching within the same request.
36
+ *
37
+ * Async because a lazy include may be backed by an async provider
38
+ * (`() => import("./routes")`) that must be resolved before its routes can be
39
+ * matched. The hot path is unaffected: `await` only suspends on the first
40
+ * request to a not-yet-loaded dynamic include; eager routes resolve in the same
41
+ * microtask. Every caller already runs inside an async match pipeline.
20
42
  */
21
43
  export function createFindMatch<TEnv = any>(
22
44
  deps: FindMatchDeps<TEnv>,
23
- ): (pathname: string, ms?: MetricsStore) => RouteMatchResult<TEnv> | null {
45
+ ): (
46
+ pathname: string,
47
+ ms?: MetricsStore,
48
+ ) => Promise<RouteMatchResult<TEnv> | null> {
24
49
  // Single-entry cache for findMatch to avoid redundant matching within the same request.
25
50
  // previewMatch and match both call findMatch with the same pathname — this ensures
26
51
  // the route matching work (which may check thousands of routes) only happens once.
27
52
  let lastFindMatchPathname: string | null = null;
28
53
  let lastFindMatchResult: RouteMatchResult<TEnv> | null = null;
29
54
 
30
- // Wrapper for findMatch that uses routesEntries
31
- // Handles lazy evaluation by evaluating lazy entries on first match.
32
- // Phase 1: try O(path_length) trie match.
33
- // Phase 2: fall back to regex iteration.
34
- return function findMatch(
55
+ return async function findMatch(
35
56
  pathname: string,
36
57
  ms?: MetricsStore,
37
- ): RouteMatchResult<TEnv> | null {
38
- // Return cached result if same pathname (avoids double-match per request)
58
+ ): Promise<RouteMatchResult<TEnv> | null> {
39
59
  if (lastFindMatchPathname === pathname) {
40
- return lastFindMatchResult;
60
+ return cloneMatchResult(lastFindMatchResult);
41
61
  }
42
62
 
43
- // Helper to push sub-metrics
44
63
  const pushMetric = ms
45
64
  ? (label: string, start: number) => {
46
65
  ms.metrics.push({
@@ -51,17 +70,15 @@ export function createFindMatch<TEnv = any>(
51
70
  }
52
71
  : undefined;
53
72
 
54
- // Phase 1: Try trie match (O(path_length))
55
- // Only use the per-router trie. The global trie merges routes from ALL
56
- // routers and must not be used — in multi-router setups (host routing)
57
- // overlapping paths like "/" would match the wrong app's route.
58
73
  const routeTrie = getRouterTrie(deps.routerId);
74
+ let trieMatched = false;
59
75
  if (routeTrie) {
60
76
  const trieStart = performance.now();
61
77
  const trieResult = tryTrieMatch(routeTrie, pathname);
62
78
  pushMetric?.("match:trie", trieStart);
63
79
 
64
80
  if (trieResult) {
81
+ trieMatched = true;
65
82
  // Find the RouteEntry that contains this route.
66
83
  // Multiple entries can share the same staticPrefix (e.g., several
67
84
  // include("/", patterns) calls all produce staticPrefix=""). Evaluate
@@ -69,11 +86,33 @@ export function createFindMatch<TEnv = any>(
69
86
  const entryStart = performance.now();
70
87
  let entry: RouteEntry<TEnv> | undefined;
71
88
  let fallbackEntry: RouteEntry<TEnv> | undefined;
89
+ let candidateError: unknown;
90
+ let candidateFailed = false;
72
91
 
73
92
  for (const e of deps.routesEntries) {
74
93
  if (e.staticPrefix !== trieResult.sp) continue;
75
94
  if (!fallbackEntry) fallbackEntry = e;
76
- deps.evaluateLazyEntry(e);
95
+ try {
96
+ const ev = deps.evaluateLazyEntry(e);
97
+ if (ev) await ev;
98
+ } catch (err) {
99
+ // Multiple async includes can share one collapsed static prefix, so
100
+ // this loop may evaluate a candidate that is NOT the route's owner.
101
+ // Remember the failure and keep scanning: if another candidate owns
102
+ // the route, this sibling's failure is correctly isolated (its
103
+ // _lazyInflight was cleared, so a later request retries). But if NO
104
+ // candidate ends up owning it, the failing one WAS the owner — a
105
+ // real, trie-matched route whose module failed to import — and we
106
+ // rethrow below as a 5xx rather than silently 404ing (see finding 3:
107
+ // owner failure must stay loud).
108
+ candidateError = err;
109
+ candidateFailed = true;
110
+ console.error(
111
+ `[@rangojs/router] include at "${e.staticPrefix}" failed to load: ` +
112
+ `${(err as Error)?.message ?? String(err)}`,
113
+ );
114
+ continue;
115
+ }
77
116
  if (
78
117
  e.routes &&
79
118
  trieResult.routeKey in (e.routes as Record<string, unknown>)
@@ -83,12 +122,16 @@ export function createFindMatch<TEnv = any>(
83
122
  }
84
123
  }
85
124
 
86
- // If no entry had the route in its routes map, use the first matching
87
- // entry as fallback (handles main entry with inline routes not yet
88
- // reflected in its routes object).
125
+ // The trie matched this route, so it EXISTS. If no same-prefix candidate
126
+ // could own it AND one failed to load, the owning module is the one that
127
+ // threw surface the module failure as a 5xx, not the misleading 404 a
128
+ // fallthrough would give.
129
+ if (!entry && candidateFailed) {
130
+ throw candidateError;
131
+ }
132
+
89
133
  if (!entry) entry = fallbackEntry;
90
134
 
91
- // If entry not found (nested include not yet discovered), evaluate parent
92
135
  if (!entry) {
93
136
  const parent = deps.routesEntries.find(
94
137
  (e) =>
@@ -97,7 +140,8 @@ export function createFindMatch<TEnv = any>(
97
140
  );
98
141
  if (parent) {
99
142
  const lazyStart = performance.now();
100
- deps.evaluateLazyEntry(parent);
143
+ const ev = deps.evaluateLazyEntry(parent);
144
+ if (ev) await ev;
101
145
  pushMetric?.("match:lazy-eval", lazyStart);
102
146
  }
103
147
  entry = deps.routesEntries.find(
@@ -112,9 +156,7 @@ export function createFindMatch<TEnv = any>(
112
156
  entry,
113
157
  routeKey: trieResult.routeKey,
114
158
  params: trieResult.params,
115
- optionalParams: new Set(trieResult.optionalParams || []),
116
159
  redirectTo: trieResult.redirectTo,
117
- ancestry: trieResult.ancestry,
118
160
  ...(trieResult.pr ? { pr: true } : {}),
119
161
  ...(trieResult.pt ? { pt: true } : {}),
120
162
  ...(trieResult.responseType
@@ -125,17 +167,25 @@ export function createFindMatch<TEnv = any>(
125
167
  : {}),
126
168
  ...(trieResult.rscFirst ? { rscFirst: true } : {}),
127
169
  };
128
- return lastFindMatchResult;
170
+ return cloneMatchResult(lastFindMatchResult);
129
171
  }
172
+ } else if (isRouterTrieAuthoritative(deps.routerId)) {
173
+ // Authoritative miss (#664): this trie was deserialized from the
174
+ // COMPLETE build manifest, so trailing-slash redirects are already
175
+ // trie-native hits and a miss means no route exists. Skip the regex
176
+ // fallback — the only route-count-proportional match path — and do
177
+ // not evaluate lazy includes for unmatched (bot-probe) traffic.
178
+ // Trie hits that need lazy splicing keep the fallback loop below
179
+ // (trieMatched === true never reaches this branch).
180
+ lastFindMatchPathname = pathname;
181
+ lastFindMatchResult = null;
182
+ return null;
130
183
  }
131
184
  }
132
185
 
133
- // Phase 2: Fall back to existing matching (regex iteration)
134
186
  const regexStart = performance.now();
135
187
  let result = findRouteMatch(pathname, deps.routesEntries);
136
188
 
137
- // If we hit a lazy entry that needs evaluation, evaluate and retry.
138
- // Cap iterations to prevent infinite loops from pathological nesting.
139
189
  const MAX_LAZY_ITERATIONS = 100;
140
190
  let iterations = 0;
141
191
  while (isLazyEvaluationNeeded(result)) {
@@ -148,13 +198,62 @@ export function createFindMatch<TEnv = any>(
148
198
  lastFindMatchResult = null;
149
199
  return null;
150
200
  }
151
- deps.evaluateLazyEntry(result.lazyEntry);
201
+ try {
202
+ const ev = deps.evaluateLazyEntry(result.lazyEntry);
203
+ if (ev) await ev;
204
+ } catch (err) {
205
+ // If the trie matched this pathname, the route is REAL and this lazy
206
+ // entry owns (part of) it — a module-load failure is a genuine error, so
207
+ // propagate it as a 5xx. If the trie did NOT match, this is a
208
+ // genuinely-unmatched pathname (a typo, a bot probe): its 404 must not be
209
+ // upgraded to a 500 just because some lazy include — e.g. a failing root
210
+ // `include("/")` — happened to need evaluating while we probed. Isolate
211
+ // and fall through to the 404 (finding 4: the two match phases agree —
212
+ // owner failure is loud, unmatched stays 404).
213
+ if (trieMatched) throw err;
214
+ console.error(
215
+ `[@rangojs/router] include at "${result.lazyEntry.staticPrefix}" ` +
216
+ `failed to load while resolving unmatched path "${pathname}": ` +
217
+ `${(err as Error)?.message ?? String(err)}`,
218
+ );
219
+ lastFindMatchPathname = pathname;
220
+ lastFindMatchResult = null;
221
+ return null;
222
+ }
152
223
  result = findRouteMatch(pathname, deps.routesEntries);
153
224
  }
154
225
  pushMetric?.("match:regex-fallback", regexStart);
155
226
 
227
+ // The trie is the single source of truth and is built before findMatch in
228
+ // both dev (handler rebuild) and production (ensureRouterManifest). If the
229
+ // trie was present yet the regex fallback resolved a real match, the trie
230
+ // has a gap (e.g. a route shape it cannot represent) and dev/prod could
231
+ // diverge if the trie were ever absent. Surface it in dev; folded out in
232
+ // production builds.
233
+ //
234
+ // Suppress when the trie DID match (`trieMatched`): that path falls through
235
+ // to the regex fallback only on the first request to a not-yet-spliced lazy
236
+ // entry (e.g. a 2+-level nested include whose deeper parent has not been
237
+ // evaluated). The trie knew the route; runtime lazy discovery simply lagged.
238
+ // That is the supported lazy-include flow, not a trie gap, so warning on it
239
+ // is a false positive (it manufactures bug reports and erodes the signal).
240
+ if (
241
+ process.env.NODE_ENV !== "production" &&
242
+ routeTrie &&
243
+ !trieMatched &&
244
+ result &&
245
+ !isLazyEvaluationNeeded(result)
246
+ ) {
247
+ console.warn(
248
+ `[@rangojs/router] Route "${pathname}" resolved via the regex fallback ` +
249
+ `even though the route trie was present. The trie should be the single ` +
250
+ `matching source of truth; this indicates a trie gap. Please report this ` +
251
+ `with your route configuration.`,
252
+ );
253
+ }
254
+
156
255
  lastFindMatchPathname = pathname;
157
256
  lastFindMatchResult = result;
158
- return result;
257
+ return cloneMatchResult(result);
159
258
  };
160
259
  }
@@ -18,6 +18,13 @@ import { isInsideCacheScope } from "../server/context.js";
18
18
  import { NOCACHE_SYMBOL, assertNotInsideCacheExec } from "../cache/taint.js";
19
19
  import { isAutoGeneratedRouteName } from "../route-name.js";
20
20
  import { PRERENDER_PASSTHROUGH } from "../prerender.js";
21
+ import { substitutePatternParams } from "./substitute-pattern-params.js";
22
+ import { fireAndForgetWaitUntil } from "../types/request-scope.js";
23
+
24
+ // Mutating Headers methods guarded so they throw inside "use cache" / cache()
25
+ // scope. Module-level constant (read-only, .has() lookups) so it is allocated
26
+ // once at module load instead of per createHandlerContext (per-request) call.
27
+ const MUTATING_HEADERS_METHODS = new Set(["set", "append", "delete"]);
21
28
 
22
29
  /**
23
30
  * Strip internal _rsc* query params from a URL.
@@ -158,51 +165,14 @@ export function createReverseFunction(
158
165
  );
159
166
  }
160
167
 
161
- let result = pattern;
162
-
163
168
  // Merge current request params as defaults, explicit params override
164
169
  const effectiveParams = currentParams
165
170
  ? { ...currentParams, ...hrefParams }
166
171
  : hrefParams;
167
172
 
168
- // Substitute params (strip constraint and optional syntax: :param(a|b)? -> value)
169
- // Optional params (:param?) are omitted when not provided
170
- if (effectiveParams) {
171
- let hadOmittedOptional = false;
172
- // First pass: optional params (trailing ?)
173
- result = result.replace(
174
- /:([a-zA-Z_][a-zA-Z0-9_]*)(\([^)]*\))?(\?)/g,
175
- (_, key) => {
176
- const value = effectiveParams[key];
177
- // Empty string is treated as omitted — the trie matcher fills
178
- // unmatched optional params with "" (not undefined), so reverse
179
- // must collapse those segments instead of leaving empty slots.
180
- if (value === undefined || value === "") {
181
- hadOmittedOptional = true;
182
- return "";
183
- }
184
- return encodeURIComponent(value);
185
- },
186
- );
187
- // Second pass: required params (no trailing ?)
188
- result = result.replace(
189
- /:([a-zA-Z_][a-zA-Z0-9_]*)(\([^)]*\))?(?!\?)/g,
190
- (_, key) => {
191
- const value = effectiveParams[key];
192
- if (value === undefined) {
193
- throw new Error(`Missing param "${key}" for route "${name}"`);
194
- }
195
- return encodeURIComponent(value);
196
- },
197
- );
198
- // Clean up slashes only when an optional param was actually omitted,
199
- // so intentional trailing-slash patterns like "/blog/" are preserved.
200
- if (hadOmittedOptional) {
201
- const hadTrailingSlash = pattern.length > 1 && pattern.endsWith("/");
202
- result = result.replace(/\/\/+/g, "/").replace(/\/+$/, "") || "/";
203
- if (hadTrailingSlash && !result.endsWith("/")) result += "/";
204
- }
205
- }
173
+ let result = effectiveParams
174
+ ? substitutePatternParams(pattern, effectiveParams, name)
175
+ : pattern;
206
176
 
207
177
  // Append search params as query string
208
178
  if (search) {
@@ -247,7 +217,7 @@ export function createHandlerContext<TEnv>(
247
217
  // Guard mutating Headers methods so they throw inside "use cache" or cache() scope.
248
218
  // Uses lazy `ctx` reference (assigned below) — only the specific handler ctx
249
219
  // is stamped by cache-runtime, not the shared request context.
250
- const MUTATING_HEADERS_METHODS = new Set(["set", "append", "delete"]);
220
+ // MUTATING_HEADERS_METHODS is hoisted to module scope (constant, read-only).
251
221
  let ctx: InternalHandlerContext<any, TEnv>;
252
222
  const guardedHeaders = new Proxy(stubResponse.headers, {
253
223
  get(target, prop, receiver) {
@@ -281,8 +251,12 @@ export function createHandlerContext<TEnv>(
281
251
  search: searchSchema ? resolvedSearchParams : {},
282
252
  pathname,
283
253
  url,
284
- originalUrl: new URL(request.url),
254
+ originalUrl: requestContext?.originalUrl ?? new URL(request.url),
285
255
  env: bindings,
256
+ waitUntil: requestContext
257
+ ? requestContext.waitUntil.bind(requestContext)
258
+ : fireAndForgetWaitUntil,
259
+ executionContext: requestContext?.executionContext,
286
260
  _variables: variables,
287
261
  get: ((keyOrVar: any) => {
288
262
  // Read-time guard: non-cacheable var inside cache() → throw.
@@ -387,6 +361,12 @@ export function createPrerenderContext<TEnv>(
387
361
  "Configure buildEnv in your rango() plugin options to enable build-time env access.",
388
362
  );
389
363
  },
364
+ // Build-time prerender has no live request. waitUntil is a true no-op
365
+ // (running fn() here would fire side effects during build, which is
366
+ // incorrect — these are meant to outlive the live response).
367
+ // executionContext is absent for the same reason.
368
+ waitUntil: () => {},
369
+ executionContext: undefined,
390
370
  _variables: variables,
391
371
  get: ((keyOrVar: any) => contextGet(variables, keyOrVar)) as any,
392
372
  set: ((keyOrVar: any, value: any) => {
@@ -476,6 +456,11 @@ export function createStaticContext<TEnv>(
476
456
  "Configure buildEnv in your rango() plugin options to enable build-time env access.",
477
457
  );
478
458
  },
459
+ // Static() handlers have no live request. waitUntil is a true no-op
460
+ // (running fn() here would fire side effects during build, which is
461
+ // incorrect). executionContext is absent for the same reason.
462
+ waitUntil: () => {},
463
+ executionContext: undefined,
479
464
  _variables: variables,
480
465
  get: ((keyOrVar: any) => contextGet(variables, keyOrVar)) as any,
481
466
  set: ((keyOrVar: any, value: any) => {