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

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 +293 -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 +2508 -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 +24 -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-snapshot.ts +368 -0
  168. package/src/cache/tag-invalidation.ts +230 -0
  169. package/src/cache/taint.ts +153 -0
  170. package/src/cache/types.ts +222 -211
  171. package/src/cache/vercel/index.ts +11 -0
  172. package/src/cache/vercel/vercel-cache-store.ts +1113 -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 +173 -35
  205. package/src/index.ts +241 -73
  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 +527 -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 +897 -0
  313. package/src/rsc/shell-serve.ts +124 -0
  314. package/src/rsc/ssr-setup.ts +144 -0
  315. package/src/rsc/transition-gate.ts +89 -0
  316. package/src/rsc/types.ts +95 -12
  317. package/src/runtime-env.ts +18 -0
  318. package/src/search-params.ts +99 -82
  319. package/src/segment-content-promise.ts +67 -0
  320. package/src/segment-loader-promise.ts +149 -0
  321. package/src/segment-system.tsx +349 -134
  322. package/src/serialize.ts +243 -0
  323. package/src/server/context.ts +459 -85
  324. package/src/server/cookie-parse.ts +32 -0
  325. package/src/server/cookie-store.ts +310 -0
  326. package/src/server/fetchable-loader-store.ts +11 -6
  327. package/src/server/handle-store.ts +123 -42
  328. package/src/server/loader-registry.ts +51 -100
  329. package/src/server/request-context.ts +848 -157
  330. package/src/server.ts +15 -8
  331. package/src/ssr/index.tsx +443 -135
  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 +76 -98
  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 +44 -0
  387. package/src/urls/path-helper-types.ts +413 -0
  388. package/src/urls/path-helper.ts +280 -0
  389. package/src/urls/pattern-types.ts +160 -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,37 @@
1
+ /**
2
+ * Resolve the client-side prefetch limits — the in-memory prefetch cache size
3
+ * and the speculative-prefetch queue concurrency — once, at router init. Both
4
+ * are positive-integer counts shipped to the browser in payload metadata; the
5
+ * browser entry feeds them to the prefetch cache (`initPrefetchCache`) and the
6
+ * prefetch queue (`setPrefetchConcurrency`) at startup.
7
+ *
8
+ * Policy note: this is a SEPARATE policy from resolvePrefetchCacheTTL
9
+ * (prefetch-cache-ttl.ts). These are counts, not durations, so the disable
10
+ * paths differ: a TTL of `false`/0 intentionally turns prefetching off, but a
11
+ * cache size or concurrency of 0 would silently break prefetching while leaving
12
+ * it nominally enabled — never the intent of passing a number. So any value
13
+ * that is not a finite integer >= 1 (0, negative, fractional below 1, NaN,
14
+ * Infinity, undefined) falls back to the default. Finite values >= 1 are
15
+ * floored. Do not unify this guard with the TTL guard.
16
+ */
17
+
18
+ export const DEFAULT_PREFETCH_CACHE_SIZE = 100;
19
+ export const DEFAULT_PREFETCH_CONCURRENCY = 2;
20
+
21
+ export function resolvePrefetchCacheSize(raw: number | undefined): number {
22
+ return resolvePositiveCount(raw, DEFAULT_PREFETCH_CACHE_SIZE);
23
+ }
24
+
25
+ export function resolvePrefetchConcurrency(raw: number | undefined): number {
26
+ return resolvePositiveCount(raw, DEFAULT_PREFETCH_CONCURRENCY);
27
+ }
28
+
29
+ function resolvePositiveCount(
30
+ raw: number | undefined,
31
+ fallback: number,
32
+ ): number {
33
+ if (typeof raw === "number" && Number.isFinite(raw) && raw >= 1) {
34
+ return Math.floor(raw);
35
+ }
36
+ return fallback;
37
+ }
@@ -0,0 +1,547 @@
1
+ import { type ReactNode } from "react";
2
+ import { createHandleStore } from "../server/handle-store.js";
3
+ import { resolveSegmentHandleValues } from "../handles/deferred-resolution.js";
4
+ import { getRequestContext } from "../server/request-context.js";
5
+ import {
6
+ runWithRequestContext,
7
+ type RequestContext,
8
+ } from "../server/request-context.js";
9
+ import { contextGet, contextSet, hasContextVars } from "../context-var.js";
10
+ import {
11
+ createPrerenderContext,
12
+ createStaticContext,
13
+ createReverseFunction,
14
+ } from "./handler-context.js";
15
+ import { detectPrerenderPassthrough } from "../prerender.js";
16
+ import { isRouteRootScoped } from "../route-map-builder.js";
17
+ import { setupBuildUse } from "./loader-resolution.js";
18
+ import { loadManifest } from "./manifest.js";
19
+ import { traverseBack } from "./pattern-matching.js";
20
+ import type { RouterContext } from "./router-context.js";
21
+ import type { ResolveSegmentOptions } from "./segment-resolution.js";
22
+ import { runWithRouterContext } from "./router-context.js";
23
+ import type { EntryData, InterceptEntry } from "../server/context";
24
+ import type {
25
+ HandlerContext,
26
+ InternalHandlerContext,
27
+ ResolvedSegment,
28
+ } from "../types";
29
+ import type {
30
+ SerializedSegmentData,
31
+ SegmentHandleData,
32
+ } from "../cache/types.js";
33
+ import type { RouteMatchResult } from "./pattern-matching.js";
34
+
35
+ export interface PrerenderMatchDeps<TEnv = any> {
36
+ findMatch: (
37
+ pathname: string,
38
+ ) => RouteMatchResult<TEnv> | null | Promise<RouteMatchResult<TEnv> | null>;
39
+ buildRouterContext: () => RouterContext<TEnv>;
40
+ mergedRouteMap: Record<string, string>;
41
+ resolveAllSegments: (
42
+ entries: EntryData[],
43
+ routeKey: string,
44
+ params: Record<string, string>,
45
+ context: HandlerContext<any, TEnv>,
46
+ loaderPromises: Map<string, Promise<any>>,
47
+ options?: ResolveSegmentOptions,
48
+ ) => Promise<ResolvedSegment[]>;
49
+ }
50
+
51
+ /**
52
+ * Build-time pre-render match. Resolves segments with a BuildContext
53
+ * (no request/env/headers/cookies), skipping middleware and loaders.
54
+ */
55
+ export async function matchForPrerender<TEnv = any>(
56
+ pathname: string,
57
+ params: Record<string, string>,
58
+ deps: PrerenderMatchDeps<TEnv>,
59
+ buildVars?: Record<string, any>,
60
+ isPassthroughRoute?: boolean,
61
+ buildEnv?: TEnv,
62
+ /** Dev-only: check getParams() for passthrough routes to skip unknown params. */
63
+ devMode?: boolean,
64
+ ): Promise<{
65
+ segments: SerializedSegmentData[];
66
+ /** RSC-encoded handle map ("" when none) — see handle-snapshot.ts. Encoded in
67
+ * the producer (where the Flight codec resolves) so the node-side build/dev
68
+ * sinks can persist it without touching the codec. */
69
+ handles: string;
70
+ routeName: string;
71
+ params: Record<string, string>;
72
+ interceptSegments?: SerializedSegmentData[];
73
+ /** RSC-encoded MERGED (main + intercept) handle map for the intercept artifact;
74
+ * the sinks store it as-is (no longer merge raw records). */
75
+ interceptHandles?: string;
76
+ passthrough?: true;
77
+ } | null> {
78
+ // 1. Find the matching route entry
79
+ const matched = await deps.findMatch(pathname);
80
+ if (!matched) return null;
81
+
82
+ // Use params from trie match if available, fall back to provided params
83
+ const matchedParams = matched.params ?? params;
84
+ const matchedPassthroughRoute = isPassthroughRoute ?? matched.pt === true;
85
+
86
+ // Build RouterContext for loadManifest/traverseBack
87
+ const routerCtx = deps.buildRouterContext();
88
+
89
+ // Passthrough sentinel result: an unknown-param Passthrough route falls
90
+ // through to the live handler at runtime, so no artifact is baked. A fresh
91
+ // object is returned per call (no site mutates or identity-compares it).
92
+ const passthroughResult = () => ({
93
+ segments: [],
94
+ handles: "",
95
+ routeName: matched.routeKey,
96
+ params: matchedParams,
97
+ passthrough: true as const,
98
+ });
99
+
100
+ return runWithRouterContext(routerCtx, async () => {
101
+ // 2. Load the manifest entry tree
102
+ const manifestEntry = await loadManifest(
103
+ matched.entry,
104
+ matched.routeKey,
105
+ pathname,
106
+ undefined,
107
+ false,
108
+ );
109
+
110
+ // 3. Build ancestor chain [root, ..., route]
111
+ const entries: EntryData[] = [];
112
+ for (const entry of traverseBack(manifestEntry)) {
113
+ entries.push(entry);
114
+ }
115
+
116
+ // 3b. Dev-mode passthrough shortcut: if the route is a Passthrough route
117
+ // and has getParams(), check if the matched params are in the known list.
118
+ // In production, only known params are pre-rendered; unknown params fall
119
+ // through to the live handler. Mirror that behavior in dev mode to avoid
120
+ // rendering unknown params with build: true.
121
+ // Vars collected from getParams() probe — merged into render context below.
122
+ let devProbeBuildVars: Record<string, any> | undefined;
123
+
124
+ if (devMode && matchedPassthroughRoute) {
125
+ const routeEntry = entries.find(
126
+ (
127
+ e,
128
+ ): e is EntryData & {
129
+ type: "route";
130
+ prerenderDef: { getParams: (ctx: any) => Promise<any[]> | any[] };
131
+ } =>
132
+ e.type === "route" &&
133
+ !!(e as any).isPassthrough &&
134
+ !!(e as any).prerenderDef?.getParams,
135
+ );
136
+ if (routeEntry) {
137
+ try {
138
+ const probeBuildVars: Record<string, any> = {};
139
+ const knownParamsList = await routeEntry.prerenderDef.getParams({
140
+ build: true as const,
141
+ dev: true,
142
+ set: ((keyOrVar: any, value: any) => {
143
+ contextSet(probeBuildVars, keyOrVar, value);
144
+ }) as any,
145
+ reverse: createReverseFunction(deps.mergedRouteMap),
146
+ get env() {
147
+ if (buildEnv !== undefined) return buildEnv;
148
+ throw new Error(
149
+ "[rango] ctx.env is not available during dev-mode getParams(). " +
150
+ "Configure buildEnv in your rango() plugin options to enable build-time env access.",
151
+ );
152
+ },
153
+ });
154
+ // Compare only the keys returned by getParams — ignore mount params
155
+ // from include() prefixes that aren't part of the handler's params.
156
+ const isKnown = knownParamsList.some((known: Record<string, any>) => {
157
+ const knownKeys = Object.keys(known);
158
+ return knownKeys.every(
159
+ (k) => String(known[k]) === String(matchedParams[k]),
160
+ );
161
+ });
162
+ if (!isKnown) {
163
+ return passthroughResult();
164
+ }
165
+ // Preserve vars set by getParams() for the render context
166
+ if (hasContextVars(probeBuildVars)) {
167
+ devProbeBuildVars = probeBuildVars;
168
+ }
169
+ } catch (err: any) {
170
+ // Mirror production semantics (prerender-collection.ts):
171
+ // Skip errors are intentional — treat as passthrough.
172
+ // All other errors propagate so dev surfaces them.
173
+ if (err?.name === "Skip") {
174
+ return passthroughResult();
175
+ }
176
+ throw err;
177
+ }
178
+ }
179
+ }
180
+
181
+ // 4. Create handle store for collecting handle data
182
+ const handleStore = createHandleStore();
183
+
184
+ // 5. Create a minimal request context with the handle store
185
+ // Shallow-copy getParams vars so each param set is independent.
186
+ // In dev mode, merge vars from the getParams() probe if the caller
187
+ // didn't provide buildVars (production passes them from expandPrerenderRoutes).
188
+ const effectiveBuildVars = buildVars ?? devProbeBuildVars;
189
+ const variables: Record<string, any> = effectiveBuildVars
190
+ ? { ...effectiveBuildVars }
191
+ : {};
192
+ const stubRes = new Response(null, { status: 200 });
193
+ const minimalRequestContext: RequestContext<TEnv> = {
194
+ env: buildEnv ?? ({} as TEnv),
195
+ request: new Request("http://prerender" + pathname),
196
+ url: new URL("http://prerender" + pathname),
197
+ originalUrl: new URL("http://prerender" + pathname),
198
+ pathname,
199
+ searchParams: new URLSearchParams(),
200
+ _variables: variables,
201
+ get: ((keyOrVar: any) => contextGet(variables, keyOrVar)) as any,
202
+ set: ((keyOrVar: any, value: any) => {
203
+ contextSet(variables, keyOrVar, value);
204
+ }) as any,
205
+ params: matchedParams,
206
+ res: stubRes,
207
+ cookie: () => undefined,
208
+ cookies: () => ({}),
209
+ setCookie: () => {},
210
+ deleteCookie: () => {},
211
+ header: () => {},
212
+ setStatus: () => {},
213
+ _setStatus: () => {},
214
+ _rotateStateCookie: () => {},
215
+ _setKeepCacheDirective: () => {},
216
+ use: (() => {
217
+ throw new Error("use() not available during pre-rendering");
218
+ }) as any,
219
+ method: "GET",
220
+ _handleStore: handleStore,
221
+ _requestTags: new Set<string>(),
222
+ waitUntil: () => {},
223
+ onResponse: () => {},
224
+ _onResponseCallbacks: [],
225
+ setLocationState() {},
226
+ _locationState: undefined,
227
+ _renderBarrier: Promise.resolve(),
228
+ _resolveRenderBarrier: () => {},
229
+ _reportedErrors: new WeakSet<object>(),
230
+ reverse: createReverseFunction(
231
+ deps.mergedRouteMap,
232
+ matched.routeKey,
233
+ matchedParams,
234
+ matched.routeKey ? isRouteRootScoped(matched.routeKey) : undefined,
235
+ ),
236
+ };
237
+
238
+ return runWithRequestContext(minimalRequestContext, async () => {
239
+ // 6. Create prerender context with synthetic URL.
240
+ // Prerender handlers get params, pathname, url, searchParams, search,
241
+ // reverse, use(handle), and optionally env (when buildEnv is configured).
242
+ const buildCtx = createPrerenderContext<TEnv>(
243
+ matchedParams,
244
+ pathname,
245
+ deps.mergedRouteMap,
246
+ matched.routeKey,
247
+ variables,
248
+ matchedPassthroughRoute,
249
+ buildEnv,
250
+ devMode,
251
+ );
252
+
253
+ // 7. Wire use() for handles only (loaders throw)
254
+ setupBuildUse(buildCtx);
255
+
256
+ // 8. Resolve all segments with skipLoaders
257
+ const loaderPromises = new Map<string, Promise<any>>();
258
+ const allSegments = await deps.resolveAllSegments(
259
+ entries,
260
+ matched.routeKey,
261
+ matchedParams,
262
+ buildCtx,
263
+ loaderPromises,
264
+ // throwOnError: a render failure (or `throw new Skip()`) must reach the
265
+ // build/dev caller, not be baked into a frozen error page (issue #587).
266
+ { skipLoaders: true, throwOnError: true },
267
+ );
268
+
269
+ // 9. Detect passthrough sentinel: handler returned ctx.passthrough().
270
+ // When the route declares loading(), the handler result is deferred so the
271
+ // component is a thenable resolving to the sentinel — detectPrerenderPassthrough
272
+ // resolves thenables before testing (a sync check would miss it and bake a
273
+ // corrupt artifact).
274
+ if (await detectPrerenderPassthrough(allSegments)) {
275
+ return passthroughResult();
276
+ }
277
+
278
+ // 10. Filter out any loader segments (belt-and-suspenders)
279
+ const nonLoaderSegments = allSegments.filter((s) => s.type !== "loader");
280
+
281
+ // 11. Wait for handles to settle
282
+ handleStore.seal();
283
+ await handleStore.settled;
284
+
285
+ // 12. Serialize segments using the cache serializer
286
+ const { serializeSegments } = await import("../cache/segment-codec.js");
287
+ const { encodeHandles } = await import("../cache/handle-snapshot.js");
288
+ const serializedSegments = await serializeSegments(nonLoaderSegments);
289
+
290
+ // 13. Collect handle data per segment (skip segments with no handle data).
291
+ // Encoded through the Flight codec (not stored raw) so Promise/ReactNode
292
+ // handle values survive the JSON-serialized build artifact / dev wire —
293
+ // the same fix the runtime cache uses. Encode happens here, in the RSC
294
+ // environment where the codec resolves; the node-side sinks only persist.
295
+ const handlesRecord: Record<string, SegmentHandleData> = {};
296
+ for (const seg of nonLoaderSegments) {
297
+ const segHandles = handleStore.getDataForSegment(seg.id);
298
+ if (Object.keys(segHandles).length > 0) {
299
+ // Resolve deferred values before encoding so the baked artifact holds
300
+ // resolved data (prerender = build-time cache).
301
+ handlesRecord[seg.id] = await resolveSegmentHandleValues(segHandles);
302
+ }
303
+ }
304
+ const handles = await encodeHandles(handlesRecord);
305
+
306
+ // Use the trie-level route key (e.g., "docs", "docs.article")
307
+ const routeName = matched.routeKey;
308
+
309
+ // 14. Resolve intercept segments for this route (if any ancestor defines
310
+ // an intercept targeting this route). At build time we skip when()
311
+ // evaluation -- we pre-render all intercepts unconditionally and let
312
+ // runtime matching decide which to serve.
313
+ let interceptSegments: SerializedSegmentData[] | undefined;
314
+ let interceptHandles: string | undefined;
315
+
316
+ const foundIntercepts: {
317
+ intercept: InterceptEntry;
318
+ entry: EntryData;
319
+ }[] = [];
320
+ let current: EntryData | null = manifestEntry;
321
+ while (current) {
322
+ // Flatten the entry and its sibling layouts into one source list, the
323
+ // same traversal findInterceptForRoute uses; the build keeps ALL matches
324
+ // (not just the innermost) and skips when(). intercept/layout are
325
+ // non-optional arrays, so empty ones are a no-op here.
326
+ for (const source of [current, ...current.layout]) {
327
+ for (const ic of source.intercept) {
328
+ if (ic.routeName === matched.routeKey) {
329
+ foundIntercepts.push({ intercept: ic, entry: source });
330
+ }
331
+ }
332
+ }
333
+ current = current.parent;
334
+ }
335
+
336
+ if (foundIntercepts.length > 0) {
337
+ const interceptResolvedSegments: typeof nonLoaderSegments = [];
338
+
339
+ for (const { intercept, entry: parentEntry } of foundIntercepts) {
340
+ // setupBuildUse keys handle pushes by ctx._currentSegmentId. The main
341
+ // resolveAllSegments pass left it on the route's segment id, so pin it
342
+ // to THIS intercept's slot id before resolving the handler/layout --
343
+ // otherwise the intercept's ctx.use() handle pushes land in the wrong
344
+ // bucket and getDataForSegment(seg.id) below drops them from the baked
345
+ // artifact. Re-pinned per iteration so multiple intercepts targeting
346
+ // the same route each get their own id (mirrors fresh.ts parallel-slot
347
+ // pinning).
348
+ const interceptSegmentId = `${parentEntry.shortCode}.${intercept.slotName}`;
349
+ (buildCtx as InternalHandlerContext<any, TEnv>)._currentSegmentId =
350
+ interceptSegmentId;
351
+
352
+ // Resolve handler
353
+ const handlerRaw =
354
+ typeof intercept.handler === "function"
355
+ ? intercept.handler(buildCtx)
356
+ : intercept.handler;
357
+ const handlerResolved =
358
+ handlerRaw instanceof Promise ? await handlerRaw : handlerRaw;
359
+ if (handlerResolved instanceof Response) {
360
+ // Handler returned a redirect/response -- skip this intercept
361
+ continue;
362
+ }
363
+ const component: ReactNode = handlerResolved;
364
+
365
+ // Resolve layout (if any)
366
+ let layoutElement: ReactNode | undefined;
367
+ if (intercept.layout) {
368
+ if (typeof intercept.layout === "function") {
369
+ const layoutResult = await intercept.layout(buildCtx);
370
+ if (layoutResult instanceof Response) continue;
371
+ layoutElement = layoutResult;
372
+ } else {
373
+ layoutElement = intercept.layout;
374
+ }
375
+ }
376
+
377
+ interceptResolvedSegments.push({
378
+ id: interceptSegmentId,
379
+ namespace: `intercept:${intercept.routeName}`,
380
+ type: "parallel" as const,
381
+ index: 0,
382
+ component,
383
+ loading: intercept.loading === false ? null : intercept.loading,
384
+ layout: layoutElement,
385
+ params: matchedParams,
386
+ slot: intercept.slotName,
387
+ belongsToRoute: true,
388
+ parallelName: `intercept:${intercept.routeName}.${intercept.slotName}`,
389
+ });
390
+ }
391
+
392
+ if (interceptResolvedSegments.length > 0) {
393
+ // Wait for handles again (intercept handlers may have called use())
394
+ await handleStore.settled;
395
+ interceptSegments = await serializeSegments(
396
+ interceptResolvedSegments,
397
+ );
398
+ const interceptHandlesRecord: Record<string, SegmentHandleData> = {};
399
+ for (const seg of interceptResolvedSegments) {
400
+ const segHandles = handleStore.getDataForSegment(seg.id);
401
+ if (Object.keys(segHandles).length > 0) {
402
+ interceptHandlesRecord[seg.id] =
403
+ await resolveSegmentHandleValues(segHandles);
404
+ }
405
+ }
406
+ // The intercept artifact serves main + intercept segments together, so
407
+ // encode the MERGED handle map here (the sinks no longer merge raw
408
+ // records — they store this pre-encoded string as-is).
409
+ interceptHandles = await encodeHandles({
410
+ ...handlesRecord,
411
+ ...interceptHandlesRecord,
412
+ });
413
+ }
414
+ }
415
+
416
+ return {
417
+ segments: serializedSegments,
418
+ handles,
419
+ routeName,
420
+ params: matchedParams,
421
+ interceptSegments,
422
+ interceptHandles,
423
+ };
424
+ });
425
+ });
426
+ }
427
+
428
+ /**
429
+ * Render a single Static handler at build time.
430
+ * Creates a minimal BuildContext, calls the handler, and RSC-serializes
431
+ * the component. Returns the encoded Flight string (or null on failure).
432
+ * Used by the Vite plugin to collect static segment data at build time.
433
+ */
434
+ export async function renderStaticSegment<TEnv = any>(
435
+ handler: Function,
436
+ handlerId: string,
437
+ mergedRouteMap: Record<string, string>,
438
+ routeName?: string,
439
+ buildEnv?: TEnv,
440
+ devMode?: boolean,
441
+ ): Promise<{ encoded: string; handles: string } | null> {
442
+ const syntheticUrl = new URL("http://prerender/");
443
+ const syntheticRequest = new Request(syntheticUrl);
444
+
445
+ // Create a HandleStore to capture handle data pushed during rendering
446
+ const handleStore = createHandleStore();
447
+
448
+ // Minimal request context so setupBuildUse can find the HandleStore
449
+ const stubRes = new Response(null, { status: 200 });
450
+ const minimalRequestContext: RequestContext<TEnv> = {
451
+ env: buildEnv ?? ({} as TEnv),
452
+ request: syntheticRequest,
453
+ url: syntheticUrl,
454
+ originalUrl: syntheticUrl,
455
+ pathname: "/",
456
+ searchParams: syntheticUrl.searchParams,
457
+ _variables: {},
458
+ get: () => undefined as any,
459
+ set: () => {},
460
+ params: {},
461
+ res: stubRes,
462
+ cookie: () => undefined,
463
+ cookies: () => ({}),
464
+ setCookie: () => {},
465
+ deleteCookie: () => {},
466
+ header: () => {},
467
+ setStatus: () => {},
468
+ _setStatus: () => {},
469
+ _rotateStateCookie: () => {},
470
+ _setKeepCacheDirective: () => {},
471
+ use: (() => {
472
+ throw new Error("use() not available during static pre-rendering");
473
+ }) as any,
474
+ method: "GET",
475
+ _handleStore: handleStore,
476
+ _requestTags: new Set<string>(),
477
+ waitUntil: () => {},
478
+ onResponse: () => {},
479
+ _onResponseCallbacks: [],
480
+ setLocationState() {},
481
+ _locationState: undefined,
482
+ _renderBarrier: Promise.resolve(),
483
+ _resolveRenderBarrier: () => {},
484
+ _reportedErrors: new WeakSet<object>(),
485
+ reverse: createReverseFunction(
486
+ mergedRouteMap,
487
+ routeName,
488
+ {},
489
+ routeName ? isRouteRootScoped(routeName) : undefined,
490
+ ),
491
+ };
492
+
493
+ return runWithRequestContext(minimalRequestContext, async () => {
494
+ // Static handlers get only reverse, use(handle), and optionally env.
495
+ const buildCtx = createStaticContext<TEnv>(
496
+ mergedRouteMap,
497
+ routeName,
498
+ buildEnv,
499
+ devMode,
500
+ );
501
+
502
+ // Set segment ID so handle pushes are keyed correctly
503
+ (buildCtx as InternalHandlerContext<any, TEnv>)._currentSegmentId =
504
+ handlerId;
505
+
506
+ setupBuildUse(buildCtx);
507
+
508
+ const raw = await handler(buildCtx);
509
+
510
+ // Static handlers must return a ReactNode. A returned Response (e.g. an
511
+ // accidental redirect()) would otherwise be serialized as a corrupt build
512
+ // artifact; fail loudly instead. The fresh/revalidation paths route handler
513
+ // results through handleHandlerResult, which throws Responses; the static
514
+ // build path bypasses that, so guard here.
515
+ if (raw instanceof Response) {
516
+ throw new TypeError(
517
+ `Static handler "${routeName}" returned a Response. Static handlers must return a ReactNode; ` +
518
+ `Responses (redirects, file responses) are not supported during static pre-rendering.`,
519
+ );
520
+ }
521
+
522
+ const segment: ResolvedSegment = {
523
+ id: handlerId,
524
+ namespace: handlerId,
525
+ type: "layout",
526
+ index: 0,
527
+ component: raw,
528
+ params: {},
529
+ belongsToRoute: false,
530
+ };
531
+
532
+ const { serializeSegments } = await import("../cache/segment-codec.js");
533
+ const { encodeHandleValue } = await import("../cache/handle-snapshot.js");
534
+ const [serialized] = await serializeSegments([segment]);
535
+
536
+ // Collect handle data pushed during rendering and Flight-encode it (so
537
+ // Promise/ReactNode handle values survive the JSON build artifact). "" when
538
+ // nothing was pushed.
539
+ const segHandles = handleStore.getDataForSegment(handlerId);
540
+ const handles =
541
+ Object.keys(segHandles).length > 0
542
+ ? await encodeHandleValue(await resolveSegmentHandleValues(segHandles))
543
+ : "";
544
+
545
+ return { encoded: serialized.encoded, handles };
546
+ });
547
+ }
@@ -0,0 +1,102 @@
1
+ import { negotiateRoute } from "./content-negotiation.js";
2
+ import { runWithRouterLogContext, withRouterLogScope } from "./logging.js";
3
+ import type { EntryData } from "../server/context";
4
+ import type { RouteMatchResult } from "./pattern-matching.js";
5
+ import type { MiddlewareFn } from "./middleware.js";
6
+ import { resolveRoute } from "./route-snapshot.js";
7
+
8
+ export interface PreviewMatchDeps<TEnv = any> {
9
+ findMatch: (
10
+ pathname: string,
11
+ ) => RouteMatchResult<TEnv> | null | Promise<RouteMatchResult<TEnv> | null>;
12
+ }
13
+
14
+ /**
15
+ * Preview match - returns route middleware without segment resolution.
16
+ * Also returns responseType and handler for response routes (non-RSC short-circuit).
17
+ */
18
+ export async function previewMatch<TEnv = any>(
19
+ request: Request,
20
+ _context: TEnv,
21
+ deps: PreviewMatchDeps<TEnv>,
22
+ ): Promise<{
23
+ routeMiddleware?: Array<{
24
+ handler: MiddlewareFn;
25
+ params: Record<string, string>;
26
+ }>;
27
+ responseType?: string;
28
+ handler?: Function;
29
+ params?: Record<string, string>;
30
+ negotiated?: boolean;
31
+ manifestEntry?: EntryData;
32
+ routeKey?: string;
33
+ } | null> {
34
+ return runWithRouterLogContext(
35
+ { request, transaction: "previewMatch" },
36
+ async () =>
37
+ withRouterLogScope("previewMatch", async () => {
38
+ const url = new URL(request.url);
39
+ const pathname = url.pathname;
40
+
41
+ // Route resolution via snapshot (lite mode: skip entries/cacheScope
42
+ // since previewMatch only needs matched, manifestEntry, routeMiddleware,
43
+ // and responseType)
44
+ const result = await resolveRoute<TEnv>(pathname, {
45
+ findMatch: deps.findMatch,
46
+ lite: true,
47
+ });
48
+
49
+ if (!result) {
50
+ return null;
51
+ }
52
+
53
+ // Skip redirect check - will be handled in full match
54
+ if (result.type === "redirect") {
55
+ return { routeMiddleware: undefined };
56
+ }
57
+
58
+ const snapshot = result.snapshot;
59
+ const { matched, manifestEntry, routeMiddleware, responseType } =
60
+ snapshot;
61
+
62
+ const negotiation = await negotiateRoute(request, pathname, snapshot);
63
+ if (negotiation) {
64
+ return {
65
+ routeMiddleware:
66
+ negotiation.routeMiddleware.length > 0
67
+ ? negotiation.routeMiddleware
68
+ : undefined,
69
+ responseType: negotiation.responseType,
70
+ handler: negotiation.handler,
71
+ params: matched.params,
72
+ manifestEntry: negotiation.manifestEntry,
73
+ routeKey: matched.routeKey,
74
+ // omitted unless a variant negotiated, preserving the prior public
75
+ // shape (absent for plain response routes, not negotiated:false)
76
+ ...(negotiation.negotiated ? { negotiated: true } : {}),
77
+ };
78
+ }
79
+
80
+ // No negotiation or RSC won — return default route info
81
+ const hasVariants =
82
+ matched.negotiateVariants && matched.negotiateVariants.length > 0;
83
+ return {
84
+ routeMiddleware:
85
+ routeMiddleware.length > 0 ? routeMiddleware : undefined,
86
+ params: matched.params,
87
+ routeKey: matched.routeKey,
88
+ ...(responseType
89
+ ? {
90
+ responseType,
91
+ handler:
92
+ manifestEntry.type === "route"
93
+ ? manifestEntry.handler
94
+ : undefined,
95
+ manifestEntry,
96
+ }
97
+ : {}),
98
+ ...(hasVariants ? { negotiated: true } : {}),
99
+ };
100
+ }),
101
+ );
102
+ }