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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (460) hide show
  1. package/AGENTS.md +17 -0
  2. package/README.md +432 -7
  3. package/dist/bin/rango.js +2073 -213
  4. package/dist/testing/vitest.js +82 -0
  5. package/dist/vite/index.js +7258 -2714
  6. package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
  7. package/package.json +140 -67
  8. package/skills/api-client/SKILL.md +211 -0
  9. package/skills/breadcrumbs/SKILL.md +329 -0
  10. package/skills/bundle-analysis/SKILL.md +159 -0
  11. package/skills/cache-guide/SKILL.md +487 -0
  12. package/skills/caching/SKILL.md +357 -25
  13. package/skills/comparison/SKILL.md +50 -0
  14. package/skills/comparison/agents/openai.yaml +4 -0
  15. package/skills/comparison/references/framework-comparison.md +837 -0
  16. package/skills/composability/SKILL.md +246 -0
  17. package/skills/css/SKILL.md +76 -0
  18. package/skills/debug-manifest/SKILL.md +16 -10
  19. package/skills/document-cache/SKILL.md +87 -62
  20. package/skills/fonts/SKILL.md +6 -4
  21. package/skills/handler-use/SKILL.md +364 -0
  22. package/skills/hooks/SKILL.md +557 -79
  23. package/skills/host-router/SKILL.md +320 -0
  24. package/skills/i18n/SKILL.md +276 -0
  25. package/skills/intercept/SKILL.md +207 -15
  26. package/skills/layout/SKILL.md +146 -6
  27. package/skills/links/SKILL.md +304 -25
  28. package/skills/loader/SKILL.md +616 -54
  29. package/skills/middleware/SKILL.md +217 -37
  30. package/skills/migrate-nextjs/SKILL.md +611 -0
  31. package/skills/migrate-react-router/SKILL.md +927 -0
  32. package/skills/mime-routes/SKILL.md +42 -11
  33. package/skills/observability/SKILL.md +194 -0
  34. package/skills/parallel/SKILL.md +284 -3
  35. package/skills/ppr/SKILL.md +426 -0
  36. package/skills/prerender/SKILL.md +437 -52
  37. package/skills/rango/SKILL.md +369 -22
  38. package/skills/react-compiler/SKILL.md +168 -0
  39. package/skills/response-routes/SKILL.md +263 -121
  40. package/skills/route/SKILL.md +350 -21
  41. package/skills/router-setup/SKILL.md +246 -33
  42. package/skills/scripts/SKILL.md +179 -0
  43. package/skills/server-actions/SKILL.md +775 -0
  44. package/skills/shell-manifest/SKILL.md +185 -0
  45. package/skills/streams-and-websockets/SKILL.md +283 -0
  46. package/skills/tailwind/SKILL.md +27 -3
  47. package/skills/testing/SKILL.md +126 -222
  48. package/skills/testing/bindings.md +103 -0
  49. package/skills/testing/cache-prerender.md +127 -0
  50. package/skills/testing/client-components.md +124 -0
  51. package/skills/testing/e2e-parity.md +125 -0
  52. package/skills/testing/flight.md +91 -0
  53. package/skills/testing/handles.md +131 -0
  54. package/skills/testing/loader.md +128 -0
  55. package/skills/testing/middleware.md +99 -0
  56. package/skills/testing/render-handler.md +122 -0
  57. package/skills/testing/response-routes.md +95 -0
  58. package/skills/testing/reverse-and-types.md +85 -0
  59. package/skills/testing/server-actions.md +107 -0
  60. package/skills/testing/server-tree.md +128 -0
  61. package/skills/testing/setup.md +123 -0
  62. package/skills/theme/SKILL.md +9 -8
  63. package/skills/typesafety/SKILL.md +532 -103
  64. package/skills/use-cache/SKILL.md +367 -0
  65. package/skills/vercel/SKILL.md +128 -0
  66. package/skills/view-transitions/SKILL.md +337 -0
  67. package/src/__augment-tests__/augment.ts +81 -0
  68. package/src/__augment-tests__/augmented.check.ts +116 -0
  69. package/src/__internal.ts +77 -44
  70. package/src/bin/rango.ts +312 -15
  71. package/src/browser/action-coordinator.ts +114 -0
  72. package/src/browser/action-fence.ts +47 -0
  73. package/src/browser/app-shell.ts +39 -0
  74. package/src/browser/app-version.ts +14 -0
  75. package/src/browser/connection-warmup.ts +134 -0
  76. package/src/browser/cookie-name.ts +140 -0
  77. package/src/browser/event-controller.ts +293 -202
  78. package/src/browser/history-state.ts +101 -0
  79. package/src/browser/index.ts +3 -3
  80. package/src/browser/intercept-utils.ts +52 -0
  81. package/src/browser/invalidate-client-cache.ts +52 -0
  82. package/src/browser/link-interceptor.ts +24 -4
  83. package/src/browser/logging.ts +11 -0
  84. package/src/browser/merge-segment-loaders.ts +20 -12
  85. package/src/browser/navigation-bridge.ts +385 -576
  86. package/src/browser/navigation-client.ts +245 -75
  87. package/src/browser/navigation-store-handle.ts +38 -0
  88. package/src/browser/navigation-store.ts +184 -118
  89. package/src/browser/navigation-transaction.ts +247 -0
  90. package/src/browser/network-error-handler.ts +88 -0
  91. package/src/browser/partial-update.ts +412 -364
  92. package/src/browser/prefetch/cache.ts +359 -0
  93. package/src/browser/prefetch/fetch.ts +452 -0
  94. package/src/browser/prefetch/observer.ts +65 -0
  95. package/src/browser/prefetch/policy.ts +48 -0
  96. package/src/browser/prefetch/queue.ts +209 -0
  97. package/src/browser/prefetch/resource-ready.ts +77 -0
  98. package/src/browser/rango-state.ts +194 -0
  99. package/src/browser/react/Link.tsx +275 -68
  100. package/src/browser/react/NavigationProvider.tsx +265 -109
  101. package/src/browser/react/ScrollRestoration.tsx +10 -6
  102. package/src/browser/react/context.ts +11 -0
  103. package/src/browser/react/filter-segment-order.ts +70 -0
  104. package/src/browser/react/index.ts +0 -48
  105. package/src/browser/react/location-state-shared.ts +272 -60
  106. package/src/browser/react/location-state.ts +90 -20
  107. package/src/browser/react/mount-context.ts +6 -1
  108. package/src/browser/react/nonce-context.ts +23 -0
  109. package/src/browser/react/shallow-equal.ts +27 -0
  110. package/src/browser/react/use-action.ts +35 -66
  111. package/src/browser/react/use-handle.ts +39 -126
  112. package/src/browser/react/use-href.tsx +8 -1
  113. package/src/browser/react/use-link-status.ts +39 -13
  114. package/src/browser/react/use-navigation.ts +53 -69
  115. package/src/browser/react/use-params.ts +75 -0
  116. package/src/browser/react/use-pathname.ts +47 -0
  117. package/src/browser/react/use-reverse.ts +106 -0
  118. package/src/browser/react/use-router.ts +98 -0
  119. package/src/browser/react/use-search-params.ts +51 -0
  120. package/src/browser/react/use-segments.ts +72 -99
  121. package/src/browser/response-adapter.ts +164 -0
  122. package/src/browser/rsc-router.tsx +300 -72
  123. package/src/browser/scroll-restoration.ts +138 -50
  124. package/src/browser/segment-reconciler.ts +243 -0
  125. package/src/browser/segment-structure-assert.ts +17 -1
  126. package/src/browser/server-action-bridge.ts +668 -613
  127. package/src/browser/types.ts +223 -51
  128. package/src/browser/validate-redirect-origin.ts +56 -0
  129. package/src/build/collect-fallback-refs.ts +107 -0
  130. package/src/build/generate-manifest.ts +252 -161
  131. package/src/build/generate-route-types.ts +41 -1038
  132. package/src/build/index.ts +12 -7
  133. package/src/build/prefix-tree-utils.ts +123 -0
  134. package/src/build/route-trie.ts +225 -42
  135. package/src/build/route-types/ast-helpers.ts +25 -0
  136. package/src/build/route-types/ast-route-extraction.ts +105 -0
  137. package/src/build/route-types/codegen.ts +113 -0
  138. package/src/build/route-types/include-resolution.ts +812 -0
  139. package/src/build/route-types/param-extraction.ts +51 -0
  140. package/src/build/route-types/per-module-writer.ts +144 -0
  141. package/src/build/route-types/router-processing.ts +695 -0
  142. package/src/build/route-types/scan-filter.ts +85 -0
  143. package/src/build/route-types/source-scan.ts +216 -0
  144. package/src/build/runtime-discovery.ts +223 -0
  145. package/src/cache/background-task.ts +34 -0
  146. package/src/cache/cache-error.ts +104 -0
  147. package/src/cache/cache-key-utils.ts +60 -0
  148. package/src/cache/cache-policy.ts +199 -0
  149. package/src/cache/cache-runtime.ts +525 -0
  150. package/src/cache/cache-scope.ts +298 -332
  151. package/src/cache/cache-tag.ts +103 -0
  152. package/src/cache/cf/cf-base64.ts +33 -0
  153. package/src/cache/cf/cf-cache-constants.ts +127 -0
  154. package/src/cache/cf/cf-cache-store.ts +2500 -158
  155. package/src/cache/cf/cf-cache-types.ts +349 -0
  156. package/src/cache/cf/cf-kv-utils.ts +46 -0
  157. package/src/cache/cf/cf-tag-marker-memo.ts +105 -0
  158. package/src/cache/cf/index.ts +17 -17
  159. package/src/cache/document-cache.ts +199 -92
  160. package/src/cache/handle-capture.ts +81 -0
  161. package/src/cache/handle-snapshot.ts +111 -0
  162. package/src/cache/index.ts +29 -35
  163. package/src/cache/memory-segment-store.ts +363 -30
  164. package/src/cache/profile-registry.ts +88 -0
  165. package/src/cache/read-through-swr.ts +178 -0
  166. package/src/cache/segment-codec.ts +248 -0
  167. package/src/cache/shell-cache.ts +386 -0
  168. package/src/cache/tag-invalidation.ts +230 -0
  169. package/src/cache/taint.ts +153 -0
  170. package/src/cache/types.ts +156 -211
  171. package/src/cache/vercel/index.ts +11 -0
  172. package/src/cache/vercel/vercel-cache-store.ts +1102 -0
  173. package/src/client.rsc.tsx +43 -21
  174. package/src/client.tsx +131 -347
  175. package/src/cloudflare/index.ts +11 -0
  176. package/src/cloudflare/tracing.ts +109 -0
  177. package/src/component-utils.ts +23 -4
  178. package/src/components/DefaultDocument.tsx +13 -3
  179. package/src/context-var.ts +168 -0
  180. package/src/debug.ts +19 -9
  181. package/src/decode-loader-results.ts +52 -0
  182. package/src/defer.ts +185 -0
  183. package/src/deps/ssr.ts +0 -1
  184. package/src/encode-kv.ts +49 -0
  185. package/src/errors.ts +106 -10
  186. package/src/escape-script.ts +52 -0
  187. package/src/handle.ts +110 -35
  188. package/src/handles/MetaTags.tsx +83 -59
  189. package/src/handles/Scripts.tsx +183 -0
  190. package/src/handles/breadcrumbs.ts +93 -0
  191. package/src/handles/deferred-resolution.ts +127 -0
  192. package/src/handles/is-thenable.ts +18 -0
  193. package/src/handles/meta.ts +44 -53
  194. package/src/handles/script.ts +244 -0
  195. package/src/host/cookie-handler.ts +20 -65
  196. package/src/host/errors.ts +21 -30
  197. package/src/host/index.ts +13 -9
  198. package/src/host/pattern-matcher.ts +50 -79
  199. package/src/host/router.ts +151 -121
  200. package/src/host/testing.ts +45 -32
  201. package/src/host/types.ts +52 -11
  202. package/src/host/utils.ts +2 -2
  203. package/src/href-client.ts +192 -57
  204. package/src/index.rsc.ts +177 -35
  205. package/src/index.ts +255 -71
  206. package/src/internal-debug.ts +9 -2
  207. package/src/loader-store.ts +500 -0
  208. package/src/loader.rsc.ts +31 -99
  209. package/src/loader.ts +30 -12
  210. package/src/missing-id-error.ts +68 -0
  211. package/src/outlet-context.ts +1 -1
  212. package/src/outlet-provider.tsx +41 -0
  213. package/src/prerender/param-hash.ts +16 -14
  214. package/src/prerender/store.ts +121 -21
  215. package/src/prerender.ts +460 -26
  216. package/src/redirect-origin.ts +100 -0
  217. package/src/regex-escape.ts +8 -0
  218. package/src/render-error-thrower.tsx +20 -0
  219. package/src/response-utils.ts +62 -0
  220. package/src/reverse.ts +198 -128
  221. package/src/root-error-boundary.tsx +42 -48
  222. package/src/route-content-wrapper.tsx +22 -77
  223. package/src/route-definition/dsl-helpers.ts +1116 -0
  224. package/src/route-definition/helper-factories.ts +88 -0
  225. package/src/route-definition/helpers-types.ts +505 -0
  226. package/src/route-definition/index.ts +54 -0
  227. package/src/route-definition/redirect.ts +134 -0
  228. package/src/route-definition/resolve-handler-use.ts +160 -0
  229. package/src/route-definition/use-item-types.ts +29 -0
  230. package/src/route-definition.ts +1 -1481
  231. package/src/route-map-builder.ts +82 -144
  232. package/src/route-name.ts +53 -0
  233. package/src/route-types.ts +71 -45
  234. package/src/router/basename.ts +14 -0
  235. package/src/router/content-negotiation.ts +263 -0
  236. package/src/router/debug-manifest.ts +72 -0
  237. package/src/router/error-handling.ts +54 -27
  238. package/src/router/find-match.ts +245 -0
  239. package/src/router/handler-context.ts +377 -125
  240. package/src/router/instrument.ts +350 -0
  241. package/src/router/intercept-resolution.ts +59 -28
  242. package/src/router/lazy-includes.ts +254 -0
  243. package/src/router/loader-resolution.ts +421 -157
  244. package/src/router/logging.ts +106 -6
  245. package/src/router/manifest.ts +131 -57
  246. package/src/router/match-api.ts +167 -246
  247. package/src/router/match-context.ts +4 -24
  248. package/src/router/match-handlers.ts +440 -0
  249. package/src/router/match-middleware/background-revalidation.ts +117 -93
  250. package/src/router/match-middleware/cache-lookup.ts +297 -150
  251. package/src/router/match-middleware/cache-store.ts +123 -51
  252. package/src/router/match-middleware/intercept-resolution.ts +44 -43
  253. package/src/router/match-middleware/segment-resolution.ts +64 -22
  254. package/src/router/match-pipelines.ts +11 -87
  255. package/src/router/match-result.ts +121 -50
  256. package/src/router/metrics.ts +219 -28
  257. package/src/router/middleware-types.ts +93 -0
  258. package/src/router/middleware.ts +505 -441
  259. package/src/router/navigation-snapshot.ts +133 -0
  260. package/src/router/params-util.ts +23 -0
  261. package/src/router/parse-pattern.ts +115 -0
  262. package/src/router/pattern-matching.ts +311 -142
  263. package/src/router/prefetch-cache-ttl.ts +51 -0
  264. package/src/router/prefetch-limits.ts +37 -0
  265. package/src/router/prerender-match.ts +547 -0
  266. package/src/router/preview-match.ts +102 -0
  267. package/src/router/request-classification.ts +278 -0
  268. package/src/router/revalidation.ts +203 -62
  269. package/src/router/route-snapshot.ts +246 -0
  270. package/src/router/router-context.ts +45 -48
  271. package/src/router/router-interfaces.ts +554 -0
  272. package/src/router/router-options.ts +779 -0
  273. package/src/router/router-registry.ts +21 -0
  274. package/src/router/segment-resolution/fresh.ts +772 -0
  275. package/src/router/segment-resolution/helpers.ts +348 -0
  276. package/src/router/segment-resolution/loader-cache.ts +250 -0
  277. package/src/router/segment-resolution/loader-mask.ts +44 -0
  278. package/src/router/segment-resolution/revalidation.ts +1331 -0
  279. package/src/router/segment-resolution/static-store.ts +81 -0
  280. package/src/router/segment-resolution/streamed-handler-telemetry.ts +52 -0
  281. package/src/router/segment-resolution/view-transition-default.ts +56 -0
  282. package/src/router/segment-resolution.ts +25 -1354
  283. package/src/router/segment-wrappers.ts +292 -0
  284. package/src/router/state-cookie-name.ts +33 -0
  285. package/src/router/substitute-pattern-params.ts +75 -0
  286. package/src/router/telemetry-otel.ts +261 -0
  287. package/src/router/telemetry.ts +377 -0
  288. package/src/router/timeout.ts +128 -0
  289. package/src/router/tracing.ts +206 -0
  290. package/src/router/trie-matching.ts +240 -61
  291. package/src/router/types.ts +23 -70
  292. package/src/router/url-params.ts +57 -0
  293. package/src/router.ts +781 -2378
  294. package/src/rsc/full-payload.ts +70 -0
  295. package/src/rsc/handler-context.ts +46 -0
  296. package/src/rsc/handler.ts +905 -1142
  297. package/src/rsc/helpers.ts +275 -19
  298. package/src/rsc/index.ts +2 -25
  299. package/src/rsc/json-route-result.ts +38 -0
  300. package/src/rsc/loader-fetch.ts +305 -0
  301. package/src/rsc/manifest-init.ts +77 -0
  302. package/src/rsc/nonce.ts +14 -0
  303. package/src/rsc/origin-guard.ts +155 -0
  304. package/src/rsc/progressive-enhancement.ts +502 -0
  305. package/src/rsc/redirect-guard.ts +99 -0
  306. package/src/rsc/response-cache-serve.ts +238 -0
  307. package/src/rsc/response-error.ts +104 -0
  308. package/src/rsc/response-route-handler.ts +257 -0
  309. package/src/rsc/rsc-rendering.ts +337 -0
  310. package/src/rsc/runtime-warnings.ts +55 -0
  311. package/src/rsc/server-action.ts +522 -0
  312. package/src/rsc/shell-capture.ts +439 -0
  313. package/src/rsc/ssr-setup.ts +144 -0
  314. package/src/rsc/transition-gate.ts +89 -0
  315. package/src/rsc/types.ts +95 -12
  316. package/src/runtime-env.ts +18 -0
  317. package/src/search-params.ts +99 -82
  318. package/src/segment-content-promise.ts +67 -0
  319. package/src/segment-loader-promise.ts +149 -0
  320. package/src/segment-system.tsx +349 -134
  321. package/src/serialize.ts +243 -0
  322. package/src/server/context.ts +452 -85
  323. package/src/server/cookie-parse.ts +32 -0
  324. package/src/server/cookie-store.ts +310 -0
  325. package/src/server/fetchable-loader-store.ts +11 -6
  326. package/src/server/handle-store.ts +123 -42
  327. package/src/server/live.ts +130 -0
  328. package/src/server/loader-registry.ts +51 -100
  329. package/src/server/request-context.ts +842 -157
  330. package/src/server.ts +15 -8
  331. package/src/ssr/index.tsx +412 -136
  332. package/src/ssr/ssr-root.tsx +228 -0
  333. package/src/static-handler.ts +45 -18
  334. package/src/testing/cache-status.ts +162 -0
  335. package/src/testing/collect-handle.ts +46 -0
  336. package/src/testing/dispatch.ts +701 -0
  337. package/src/testing/dom.entry.ts +22 -0
  338. package/src/testing/e2e/fixture.ts +188 -0
  339. package/src/testing/e2e/index.ts +128 -0
  340. package/src/testing/e2e/matchers.ts +35 -0
  341. package/src/testing/e2e/page-helpers.ts +272 -0
  342. package/src/testing/e2e/parity.ts +387 -0
  343. package/src/testing/e2e/server.ts +195 -0
  344. package/src/testing/flight-matchers.ts +97 -0
  345. package/src/testing/flight-normalize.ts +11 -0
  346. package/src/testing/flight-runtime.d.ts +57 -0
  347. package/src/testing/flight-tree.ts +682 -0
  348. package/src/testing/flight.entry.ts +52 -0
  349. package/src/testing/flight.ts +257 -0
  350. package/src/testing/generated-routes.ts +199 -0
  351. package/src/testing/index.ts +105 -0
  352. package/src/testing/internal/context.ts +371 -0
  353. package/src/testing/internal/flight-client-globals.ts +30 -0
  354. package/src/testing/internal/seed-vars.ts +54 -0
  355. package/src/testing/render-handler.ts +357 -0
  356. package/src/testing/render-route.tsx +584 -0
  357. package/src/testing/run-loader.ts +385 -0
  358. package/src/testing/run-middleware.ts +205 -0
  359. package/src/testing/run-transition-when.ts +164 -0
  360. package/src/testing/vitest-stubs/cloudflare-email.ts +9 -0
  361. package/src/testing/vitest-stubs/cloudflare-workers.ts +21 -0
  362. package/src/testing/vitest-stubs/plugin-rsc.ts +16 -0
  363. package/src/testing/vitest-stubs/version.ts +5 -0
  364. package/src/testing/vitest.ts +305 -0
  365. package/src/theme/ThemeProvider.tsx +40 -72
  366. package/src/theme/ThemeScript.tsx +12 -14
  367. package/src/theme/constants.ts +57 -15
  368. package/src/theme/index.ts +3 -20
  369. package/src/theme/theme-context.ts +5 -35
  370. package/src/theme/theme-script.ts +43 -39
  371. package/src/theme/use-theme.ts +0 -3
  372. package/src/types/boundaries.ts +123 -0
  373. package/src/types/cache-types.ts +207 -0
  374. package/src/types/error-types.ts +132 -0
  375. package/src/types/global-namespace.ts +113 -0
  376. package/src/types/handler-context.ts +839 -0
  377. package/src/types/index.ts +81 -0
  378. package/src/types/loader-types.ts +212 -0
  379. package/src/types/request-scope.ts +112 -0
  380. package/src/types/route-config.ts +138 -0
  381. package/src/types/route-entry.ts +114 -0
  382. package/src/types/segments.ts +271 -0
  383. package/src/types.ts +1 -1795
  384. package/src/urls/include-helper.ts +162 -0
  385. package/src/urls/include-provider.ts +71 -0
  386. package/src/urls/index.ts +43 -0
  387. package/src/urls/path-helper-types.ts +413 -0
  388. package/src/urls/path-helper.ts +275 -0
  389. package/src/urls/pattern-types.ts +124 -0
  390. package/src/urls/response-types.ts +109 -0
  391. package/src/urls/type-extraction.ts +316 -0
  392. package/src/urls/urls-function.ts +80 -0
  393. package/src/urls.ts +1 -1341
  394. package/src/use-loader.tsx +406 -141
  395. package/src/vercel/index.ts +11 -0
  396. package/src/vercel/tracing.ts +88 -0
  397. package/src/vite/debug.ts +185 -0
  398. package/src/vite/discovery/bundle-postprocess.ts +182 -0
  399. package/src/vite/discovery/discover-routers.ts +389 -0
  400. package/src/vite/discovery/discovery-errors.ts +255 -0
  401. package/src/vite/discovery/gate-state.ts +171 -0
  402. package/src/vite/discovery/prerender-collection.ts +467 -0
  403. package/src/vite/discovery/route-types-writer.ts +214 -0
  404. package/src/vite/discovery/self-gen-tracking.ts +73 -0
  405. package/src/vite/discovery/state.ts +161 -0
  406. package/src/vite/discovery/virtual-module-codegen.ts +183 -0
  407. package/src/vite/index.ts +23 -2255
  408. package/src/vite/inject-client-debug.ts +36 -0
  409. package/src/vite/plugin-types.ts +303 -0
  410. package/src/vite/plugins/cjs-to-esm.ts +90 -0
  411. package/src/vite/plugins/client-ref-dedup.ts +120 -0
  412. package/src/vite/plugins/client-ref-hashing.ts +118 -0
  413. package/src/vite/plugins/cloudflare-protocol-loader-hook.d.mts +23 -0
  414. package/src/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
  415. package/src/vite/plugins/cloudflare-protocol-stub.ts +194 -0
  416. package/src/vite/{expose-action-id.ts → plugins/expose-action-id.ts} +88 -110
  417. package/src/vite/{expose-id-utils.ts → plugins/expose-id-utils.ts} +89 -79
  418. package/src/vite/plugins/expose-ids/export-analysis.ts +363 -0
  419. package/src/vite/plugins/expose-ids/handler-transform.ts +130 -0
  420. package/src/vite/plugins/expose-ids/loader-transform.ts +64 -0
  421. package/src/vite/plugins/expose-ids/router-transform.ts +199 -0
  422. package/src/vite/plugins/expose-ids/types.ts +45 -0
  423. package/src/vite/plugins/expose-internal-ids.ts +805 -0
  424. package/src/vite/plugins/performance-tracks.ts +89 -0
  425. package/src/vite/plugins/refresh-cmd.ts +127 -0
  426. package/src/vite/plugins/use-cache-transform.ts +313 -0
  427. package/src/vite/plugins/vercel-output.ts +384 -0
  428. package/src/vite/plugins/version-injector.ts +94 -0
  429. package/src/vite/plugins/version-plugin.ts +263 -0
  430. package/src/vite/plugins/virtual-entries.ts +234 -0
  431. package/src/vite/plugins/virtual-stub-plugin.ts +29 -0
  432. package/src/vite/rango.ts +560 -0
  433. package/src/vite/router-discovery.ts +1638 -0
  434. package/src/vite/{ast-handler-extract.ts → utils/ast-handler-extract.ts} +200 -37
  435. package/src/vite/utils/banner.ts +36 -0
  436. package/src/vite/utils/bundle-analysis.ts +132 -0
  437. package/src/vite/utils/client-chunks.ts +184 -0
  438. package/src/vite/utils/directive-prologue.ts +40 -0
  439. package/src/vite/utils/forward-user-plugins.ts +171 -0
  440. package/src/vite/utils/manifest-utils.ts +15 -0
  441. package/src/vite/utils/package-resolution.ts +89 -0
  442. package/src/vite/utils/prerender-utils.ts +249 -0
  443. package/src/vite/utils/shared-utils.ts +269 -0
  444. package/CLAUDE.md +0 -43
  445. package/dist/vite/index.named-routes.gen.ts +0 -103
  446. package/src/browser/lru-cache.ts +0 -69
  447. package/src/browser/react/use-client-cache.ts +0 -56
  448. package/src/browser/request-controller.ts +0 -164
  449. package/src/browser/shallow.ts +0 -35
  450. package/src/cache/memory-store.ts +0 -253
  451. package/src/handles/index.ts +0 -6
  452. package/src/href-context.ts +0 -33
  453. package/src/network-error-thrower.tsx +0 -21
  454. package/src/router.gen.ts +0 -6
  455. package/src/static-handler.gen.ts +0 -5
  456. package/src/urls.gen.ts +0 -8
  457. package/src/vite/expose-internal-ids.ts +0 -1167
  458. package/src/vite/package-resolution.ts +0 -125
  459. package/src/vite/virtual-entries.ts +0 -114
  460. /package/src/vite/{version.d.ts → plugins/version.d.ts} +0 -0
@@ -0,0 +1,263 @@
1
+ /**
2
+ * Content Negotiation Utilities
3
+ *
4
+ * Pure functions for HTTP Accept header parsing and response type matching.
5
+ * Used by previewMatch and classifyRequest for content negotiation between
6
+ * RSC routes and response routes (JSON, text, image, stream, etc.).
7
+ */
8
+
9
+ import type { EntryData } from "../server/context.js";
10
+ import type { NegotiateVariant } from "../build/route-trie.js";
11
+ import type { CollectedMiddleware } from "./middleware-types.js";
12
+ import { collectRouteMiddleware } from "./middleware.js";
13
+ import { loadManifest } from "./manifest.js";
14
+ import { traverseBack } from "./pattern-matching.js";
15
+ import type { RouteMatchResult } from "./pattern-matching.js";
16
+ import type { RouteSnapshot } from "./route-snapshot.js";
17
+
18
+ export const RESPONSE_TYPE_MIME: Record<string, string> = {
19
+ json: "application/json",
20
+ text: "text/plain",
21
+ xml: "application/xml",
22
+ html: "text/html",
23
+ md: "text/markdown",
24
+ };
25
+
26
+ export const MIME_RESPONSE_TYPE: Record<string, string> = Object.fromEntries(
27
+ Object.entries(RESPONSE_TYPE_MIME).map(([tag, mime]) => [mime, tag]),
28
+ );
29
+
30
+ export type NamedRouteEntry =
31
+ | string
32
+ | { path: string; search?: Record<string, string> };
33
+
34
+ export function flattenNamedRoutes(
35
+ routeNames?: Record<string, NamedRouteEntry>,
36
+ ): Record<string, string> {
37
+ if (!routeNames) return {};
38
+ const flattened: Record<string, string> = {};
39
+ for (const [name, entry] of Object.entries(routeNames)) {
40
+ flattened[name] = typeof entry === "string" ? entry : entry.path;
41
+ }
42
+ return flattened;
43
+ }
44
+
45
+ export interface AcceptEntry {
46
+ mime: string;
47
+ q: number;
48
+ order: number;
49
+ }
50
+
51
+ /**
52
+ * Parse an Accept header into a sorted array of MIME entries.
53
+ * Respects q-values (default 1.0) and uses client order as tiebreaker
54
+ * when q-values are equal (matching Express/Hono behavior).
55
+ */
56
+ export function parseAcceptTypes(accept: string): AcceptEntry[] {
57
+ const entries: AcceptEntry[] = [];
58
+ const parts = accept.split(",");
59
+ for (let i = 0; i < parts.length; i++) {
60
+ const part = parts[i]!;
61
+ const segments = part.split(";");
62
+ const mime = segments[0]!.trim().toLowerCase();
63
+ if (!mime) continue;
64
+ let q = 1.0;
65
+ for (let j = 1; j < segments.length; j++) {
66
+ const param = segments[j]!.trim();
67
+ if (param.startsWith("q=")) {
68
+ q = Math.max(0, Math.min(1, Number(param.slice(2)) || 0));
69
+ }
70
+ }
71
+ entries.push({ mime, q, order: i });
72
+ }
73
+ entries.sort((a, b) => b.q - a.q || a.order - b.order);
74
+ return entries;
75
+ }
76
+
77
+ export const RSC_RESPONSE_TYPE = "__rsc__";
78
+
79
+ /**
80
+ * Pick the best negotiate variant by walking the client's sorted Accept list.
81
+ * For each accepted MIME type (in q-value/order priority), check if any
82
+ * candidate serves that type. Wildcards match the first candidate.
83
+ * Falls back to the first candidate if nothing matches.
84
+ */
85
+ export function pickNegotiateVariant<
86
+ T extends { routeKey: string; responseType: string },
87
+ >(acceptEntries: AcceptEntry[], candidates: T[]): T {
88
+ const byCandidateMime = new Map<string, T>();
89
+ for (const c of candidates) {
90
+ const mime =
91
+ c.responseType === RSC_RESPONSE_TYPE
92
+ ? "text/html"
93
+ : RESPONSE_TYPE_MIME[c.responseType];
94
+ if (mime && !byCandidateMime.has(mime)) {
95
+ byCandidateMime.set(mime, c);
96
+ }
97
+ }
98
+
99
+ for (const entry of acceptEntries) {
100
+ if (entry.q === 0) continue;
101
+ if (entry.mime === "*/*") return candidates[0]!;
102
+ if (entry.mime.endsWith("/*")) {
103
+ const typePrefix = entry.mime.slice(0, entry.mime.indexOf("/"));
104
+ for (const [mime, candidate] of byCandidateMime) {
105
+ if (mime.startsWith(typePrefix + "/")) return candidate;
106
+ }
107
+ continue;
108
+ }
109
+ const match = byCandidateMime.get(entry.mime);
110
+ if (match) return match;
111
+ }
112
+ return candidates[0]!;
113
+ }
114
+
115
+ /**
116
+ * Re-key params from the primary leaf's names to a winning variant's names.
117
+ *
118
+ * The trie match builds `params` positionally under the primary leaf's pa, so
119
+ * `/widgets/:id` matched as the primary yields `{ id }` even when the winning
120
+ * `/widgets/:file` response variant expects `{ file }`. Both share the same trie
121
+ * terminal, so they bind the same number of positional named params; we zip the
122
+ * variant's pa against the named values in insertion order (which is the
123
+ * primary's pa order). The wildcard key (`*`) is positional-independent and left
124
+ * untouched.
125
+ *
126
+ * Mutates `params` in place. No-op when `variantPa` is absent, when names already
127
+ * match (the common case), or when the positional count diverges (defensive:
128
+ * never corrupt params by zipping mismatched lengths).
129
+ */
130
+ export function rekeyParamsForVariant(
131
+ params: Record<string, string>,
132
+ variantPa: string[] | undefined,
133
+ ): void {
134
+ if (!variantPa || variantPa.length === 0) return;
135
+
136
+ const namedKeys: string[] = [];
137
+ for (const key in params) {
138
+ if (key !== "*") namedKeys.push(key);
139
+ }
140
+ if (namedKeys.length !== variantPa.length) return;
141
+
142
+ let identical = true;
143
+ for (let i = 0; i < variantPa.length; i++) {
144
+ if (namedKeys[i] !== variantPa[i]) {
145
+ identical = false;
146
+ break;
147
+ }
148
+ }
149
+ if (identical) return;
150
+
151
+ const values = namedKeys.map((k) => params[k]!);
152
+ for (const key of namedKeys) delete params[key];
153
+ for (let i = 0; i < variantPa.length; i++) {
154
+ params[variantPa[i]!] = values[i]!;
155
+ }
156
+ }
157
+
158
+ /**
159
+ * Result of content negotiation for a route with negotiate variants.
160
+ */
161
+ export interface NegotiationResult {
162
+ /** The winning response type */
163
+ responseType: string;
164
+ /** Handler function for the winning variant */
165
+ handler: Function;
166
+ /** Manifest entry for the winning variant (may differ from primary) */
167
+ manifestEntry: EntryData;
168
+ /** Route middleware for the winning variant */
169
+ routeMiddleware: CollectedMiddleware[];
170
+ /** True when negotiation selected a variant; false for a plain response route. */
171
+ negotiated: boolean;
172
+ }
173
+
174
+ /**
175
+ * Perform content negotiation for a route with negotiate variants.
176
+ *
177
+ * Returns a NegotiationResult when a response route wins negotiation.
178
+ * Returns null when RSC wins or no negotiation is needed.
179
+ *
180
+ * Shared by previewMatch and classifyRequest to avoid duplicating
181
+ * the candidate-building and variant-loading logic.
182
+ */
183
+ export async function negotiateRoute(
184
+ request: Request,
185
+ pathname: string,
186
+ snapshot: RouteSnapshot,
187
+ ): Promise<NegotiationResult | null> {
188
+ const { matched, manifestEntry, routeMiddleware, responseType } = snapshot;
189
+ if (!matched.negotiateVariants || matched.negotiateVariants.length === 0) {
190
+ // No variants: a plain response route still yields a result (negotiated:false)
191
+ // so callers don't re-derive it; RSC routes (no responseType/handler) -> null.
192
+ const handler =
193
+ manifestEntry.type === "route" ? manifestEntry.handler : undefined;
194
+ if (responseType && handler) {
195
+ return {
196
+ responseType,
197
+ handler: handler as Function,
198
+ manifestEntry,
199
+ routeMiddleware,
200
+ negotiated: false,
201
+ };
202
+ }
203
+ return null;
204
+ }
205
+
206
+ const acceptEntries = parseAcceptTypes(request.headers.get("accept") || "");
207
+
208
+ // Variants carry the variant's own pa (positional param names); the synthetic
209
+ // primary/RSC candidates have none (their params are already keyed correctly).
210
+ const variants = matched.negotiateVariants as NegotiateVariant[];
211
+ let candidates: NegotiateVariant[];
212
+ if (responseType) {
213
+ candidates = [...variants, { routeKey: matched.routeKey, responseType }];
214
+ } else {
215
+ const rscCandidate = {
216
+ routeKey: matched.routeKey,
217
+ responseType: RSC_RESPONSE_TYPE,
218
+ };
219
+ candidates = matched.rscFirst
220
+ ? [rscCandidate, ...variants]
221
+ : [...variants, rscCandidate];
222
+ }
223
+
224
+ const variant = pickNegotiateVariant(acceptEntries, candidates);
225
+
226
+ if (variant.responseType === RSC_RESPONSE_TYPE) {
227
+ return null;
228
+ }
229
+
230
+ if (responseType && variant.routeKey === matched.routeKey) {
231
+ return {
232
+ responseType,
233
+ handler: manifestEntry.handler as Function,
234
+ manifestEntry,
235
+ routeMiddleware,
236
+ negotiated: true,
237
+ };
238
+ }
239
+ // The trie extracted params under the PRIMARY leaf's pa, but the winning
240
+ // variant's handler is keyed by the variant's own param names. Re-key in place
241
+ // so plan.route.params (and the variant middleware collected just below) see
242
+ // the variant's names. No-op when the variant has no pa or shares the primary's
243
+ // names (the common case).
244
+ rekeyParamsForVariant(matched.params, variant.pa);
245
+ const negotiateEntry = await loadManifest(
246
+ matched.entry,
247
+ variant.routeKey,
248
+ pathname,
249
+ undefined,
250
+ false,
251
+ );
252
+ const variantMiddleware = collectRouteMiddleware(
253
+ traverseBack(negotiateEntry),
254
+ matched.params,
255
+ );
256
+ return {
257
+ responseType: variant.responseType,
258
+ handler: negotiateEntry.handler as Function,
259
+ manifestEntry: negotiateEntry,
260
+ routeMiddleware: variantMiddleware,
261
+ negotiated: true,
262
+ };
263
+ }
@@ -0,0 +1,72 @@
1
+ import { type EntryData, getContext } from "../server/context";
2
+ import { serializeManifest, type SerializedManifest } from "../debug.js";
3
+ import { createRouteHelpers } from "../route-definition.js";
4
+ import MapRootLayout from "../server/root-layout.js";
5
+ import type { RouteEntry, TrailingSlashMode } from "../types";
6
+
7
+ /**
8
+ * Build a serialized manifest from all route entries for debug inspection.
9
+ * Used by the `debugManifest()` method on the router instance.
10
+ */
11
+ export async function buildDebugManifest<TEnv = any>(
12
+ routesEntries: RouteEntry<TEnv>[],
13
+ ): Promise<SerializedManifest> {
14
+ const manifest = new Map<string, EntryData>();
15
+
16
+ for (const entry of routesEntries) {
17
+ const Store = {
18
+ manifest,
19
+ namespace: `debug.M${entry.mountIndex}`,
20
+ parent: null as EntryData | null,
21
+ counters: {} as Record<string, number>,
22
+ mountIndex: entry.mountIndex,
23
+ patterns: new Map<string, string>(),
24
+ trailingSlash: new Map<string, TrailingSlashMode>(),
25
+ };
26
+
27
+ await getContext().runWithStore(
28
+ Store,
29
+ `debug.M${entry.mountIndex}`,
30
+ null,
31
+ async () => {
32
+ const helpers = createRouteHelpers();
33
+
34
+ // Wrap handler execution in root layout (same as loadManifest)
35
+ let promiseResult: Promise<any> | null = null;
36
+ helpers.layout(MapRootLayout, () => {
37
+ const result = entry.handler();
38
+ if (result instanceof Promise) {
39
+ promiseResult = result;
40
+ return [];
41
+ }
42
+ return result;
43
+ });
44
+
45
+ if (promiseResult !== null) {
46
+ const load = await (promiseResult as Promise<any>);
47
+ if (load && typeof load === "object" && "default" in load) {
48
+ // Promise<{ default: fn }> — e.g. dynamic import
49
+ if (typeof load.default !== "function") {
50
+ throw new Error(
51
+ `[@rangojs/router] Unsupported async handler: { default } must be a function, ` +
52
+ `got ${typeof load.default}. Use () => import('./urls') for lazy loading.`,
53
+ );
54
+ }
55
+ load.default(helpers);
56
+ } else if (typeof load === "function") {
57
+ // Promise<fn>
58
+ load(helpers);
59
+ } else {
60
+ // Reject unsupported async handler results (same policy as manifest.ts)
61
+ throw new Error(
62
+ `[@rangojs/router] Unsupported async handler result (${typeof load}). ` +
63
+ `Lazy route handlers must resolve to a function or { default: fn }.`,
64
+ );
65
+ }
66
+ }
67
+ },
68
+ );
69
+ }
70
+
71
+ return serializeManifest(manifest);
72
+ }
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Router Error Handling Utilities
3
3
  *
4
- * Error boundary and not-found boundary handling for RSC Router.
4
+ * Error boundary and not-found boundary handling for Rango.
5
5
  * Also includes the shared invokeOnError utility for error callback invocation.
6
6
  */
7
7
 
@@ -60,7 +60,7 @@ export function invokeOnError<TEnv = any>(
60
60
  error: unknown,
61
61
  phase: ErrorPhase,
62
62
  context: InvokeOnErrorContext<TEnv>,
63
- logPrefix: string = "Router"
63
+ logPrefix: string = "Router",
64
64
  ): void {
65
65
  if (!onError) return;
66
66
 
@@ -74,8 +74,8 @@ export function invokeOnError<TEnv = any>(
74
74
  phase,
75
75
  request: context.request,
76
76
  url: context.url,
77
- pathname: context.url.pathname,
78
- method: context.request.method,
77
+ pathname: context.url?.pathname,
78
+ method: context.request?.method,
79
79
  routeKey: context.routeKey,
80
80
  params: context.params,
81
81
  segmentId: context.segmentId,
@@ -112,21 +112,15 @@ export function invokeOnError<TEnv = any>(
112
112
  */
113
113
  export function findNearestErrorBoundary(
114
114
  entry: EntryData | null,
115
- defaultErrorBoundary?: ReactNode | ErrorBoundaryHandler
115
+ defaultErrorBoundary?: ReactNode | ErrorBoundaryHandler,
116
116
  ): ReactNode | ErrorBoundaryHandler | null {
117
117
  let current: EntryData | null = entry;
118
118
 
119
119
  while (current) {
120
- // Check if this entry has error boundaries defined
121
120
  if (current.errorBoundary && current.errorBoundary.length > 0) {
122
- // Return the last error boundary (most recently defined takes precedence)
123
121
  return current.errorBoundary[current.errorBoundary.length - 1];
124
122
  }
125
123
 
126
- // Check orphan layouts for error boundaries
127
- // Orphan layouts are siblings that render alongside the main route chain
128
- // They can define error boundaries that catch errors from routes in the same route group
129
- // Check from first to last (first sibling takes precedence as the "outer" wrapper)
130
124
  if (current.layout && current.layout.length > 0) {
131
125
  for (const orphan of current.layout) {
132
126
  if (orphan.errorBoundary && orphan.errorBoundary.length > 0) {
@@ -148,16 +142,26 @@ export function findNearestErrorBoundary(
148
142
  */
149
143
  export function findNearestNotFoundBoundary(
150
144
  entry: EntryData | null,
151
- defaultNotFoundBoundary?: ReactNode | NotFoundBoundaryHandler
145
+ defaultNotFoundBoundary?: ReactNode | NotFoundBoundaryHandler,
152
146
  ): ReactNode | NotFoundBoundaryHandler | null {
153
147
  let current: EntryData | null = entry;
154
148
 
155
149
  while (current) {
156
- // Check if this entry has notFound boundaries defined
157
150
  if (current.notFoundBoundary && current.notFoundBoundary.length > 0) {
158
- // Return the last notFound boundary (most recently defined takes precedence)
159
151
  return current.notFoundBoundary[current.notFoundBoundary.length - 1];
160
152
  }
153
+
154
+ // Check orphan layouts mirroring findNearestErrorBoundary: notFoundBoundary
155
+ // attaches identically (onto parent.notFoundBoundary), and an orphan layout
156
+ // (parent=null) is reachable only via this scan. First sibling is "outer".
157
+ if (current.layout && current.layout.length > 0) {
158
+ for (const orphan of current.layout) {
159
+ if (orphan.notFoundBoundary && orphan.notFoundBoundary.length > 0) {
160
+ return orphan.notFoundBoundary[orphan.notFoundBoundary.length - 1];
161
+ }
162
+ }
163
+ }
164
+
161
165
  current = current.parent;
162
166
  }
163
167
 
@@ -165,6 +169,37 @@ export function findNearestNotFoundBoundary(
165
169
  return defaultNotFoundBoundary || null;
166
170
  }
167
171
 
172
+ /**
173
+ * Normalize an error's cause into a Flight-serializable shape.
174
+ * ErrorInfo.cause crosses the RSC serialization boundary (via
175
+ * LoaderDataResult.error + error-segment fallback props); a non-serializable
176
+ * cause (function, class instance, circular object) would make Flight
177
+ * serialization throw and mask the original loader error.
178
+ */
179
+ function normalizeCause(cause: unknown): unknown {
180
+ if (cause == null) return undefined;
181
+ const t = typeof cause;
182
+ if (t === "string" || t === "number" || t === "boolean") return cause;
183
+ // The whole body is guarded: even `instanceof`/clone can run user code (a
184
+ // Proxy trap, a throwing getter), and this helper must never throw —
185
+ // throwing here would mask the original loader error it exists to protect.
186
+ try {
187
+ if (cause instanceof Error) {
188
+ return { name: cause.name, message: cause.message, stack: cause.stack };
189
+ }
190
+ // Prefer preserving a serializable object/array intact (Flight uses
191
+ // structured-clone-like semantics); fall back to a string when the value
192
+ // is circular, host-bound, or otherwise non-serializable.
193
+ return structuredClone(cause);
194
+ } catch {
195
+ try {
196
+ return String(cause);
197
+ } catch {
198
+ return "[unstringifiable cause]";
199
+ }
200
+ }
201
+ }
202
+
168
203
  /**
169
204
  * Create ErrorInfo from an error object
170
205
  * Sanitizes error details in production
@@ -172,7 +207,7 @@ export function findNearestNotFoundBoundary(
172
207
  export function createErrorInfo(
173
208
  error: unknown,
174
209
  segmentId: string,
175
- segmentType: ErrorInfo["segmentType"]
210
+ segmentType: ErrorInfo["segmentType"],
176
211
  ): ErrorInfo {
177
212
  const isDev = process.env.NODE_ENV !== "production";
178
213
 
@@ -182,7 +217,7 @@ export function createErrorInfo(
182
217
  name: error.name,
183
218
  code: (error as any).code,
184
219
  stack: isDev ? error.stack : undefined,
185
- cause: isDev ? error.cause : undefined,
220
+ cause: isDev ? normalizeCause(error.cause) : undefined,
186
221
  segmentId,
187
222
  segmentType,
188
223
  };
@@ -205,24 +240,19 @@ export function createErrorSegment(
205
240
  errorInfo: ErrorInfo,
206
241
  fallback: ReactNode | ErrorBoundaryHandler,
207
242
  entry: EntryData,
208
- params: Record<string, string>
243
+ params: Record<string, string>,
209
244
  ): ResolvedSegment {
210
- // Determine the component to render
211
245
  let component: ReactNode;
212
246
 
213
247
  if (typeof fallback === "function") {
214
- // ErrorBoundaryHandler - call with error info
215
248
  const props: ErrorBoundaryFallbackProps = {
216
249
  error: errorInfo,
217
250
  };
218
251
  component = fallback(props);
219
252
  } else {
220
- // Static ReactNode fallback
221
253
  component = fallback;
222
254
  }
223
255
 
224
- // Error segment uses the same ID as the layout that has the error boundary
225
- // The error boundary content replaces the layout's outlet content
226
256
  return {
227
257
  id: entry.shortCode,
228
258
  namespace: entry.id,
@@ -241,7 +271,7 @@ export function createNotFoundInfo(
241
271
  error: { message: string },
242
272
  segmentId: string,
243
273
  segmentType: NotFoundInfo["segmentType"],
244
- pathname?: string
274
+ pathname?: string,
245
275
  ): NotFoundInfo {
246
276
  return {
247
277
  message: error.message,
@@ -259,19 +289,16 @@ export function createNotFoundSegment(
259
289
  notFoundInfo: NotFoundInfo,
260
290
  fallback: ReactNode | NotFoundBoundaryHandler,
261
291
  entry: EntryData,
262
- params: Record<string, string>
292
+ params: Record<string, string>,
263
293
  ): ResolvedSegment {
264
- // Determine the component to render
265
294
  let component: ReactNode;
266
295
 
267
296
  if (typeof fallback === "function") {
268
- // NotFoundBoundaryHandler - call with props
269
297
  const props: NotFoundBoundaryFallbackProps = {
270
298
  notFound: notFoundInfo,
271
299
  };
272
300
  component = fallback(props);
273
301
  } else {
274
- // Static ReactNode fallback
275
302
  component = fallback;
276
303
  }
277
304