@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,772 @@
1
+ /**
2
+ * Fresh Path Segment Resolution
3
+ *
4
+ * Functions for resolving segments during a full (non-revalidation) request.
5
+ * Handles loaders, layouts, routes, parallels, orphan layouts, and error boundaries.
6
+ */
7
+
8
+ import type { ReactNode } from "react";
9
+ import { invariant } from "../../errors";
10
+ import {
11
+ getParallelEntries,
12
+ getParallelSlotEntries,
13
+ type EntryData,
14
+ } from "../../server/context";
15
+ import type {
16
+ HandlerContext,
17
+ InternalHandlerContext,
18
+ ResolvedSegment,
19
+ } from "../../types";
20
+ import type { SegmentResolutionDeps } from "../types.js";
21
+ import { resolveLoaderData } from "./loader-cache.js";
22
+ import { isShellCaptureActive } from "./loader-mask.js";
23
+ import {
24
+ handleHandlerResult,
25
+ tryStaticHandler,
26
+ tryStaticSlot,
27
+ resolveLayoutComponent,
28
+ resolveWithErrorBoundary,
29
+ warnOnStreamedResponse,
30
+ buildLoaderErrorContext,
31
+ } from "./helpers.js";
32
+ import { applyViewTransitionDefault } from "./view-transition-default.js";
33
+ import { getRouterContext } from "../router-context.js";
34
+ import { observeStreamedHandler } from "./streamed-handler-telemetry.js";
35
+ import { observeHandler } from "../instrument.js";
36
+ import {
37
+ track,
38
+ RangoContext,
39
+ runInsideLoaderScope,
40
+ } from "../../server/context.js";
41
+
42
+ // ---------------------------------------------------------------------------
43
+ // Fresh path (full match, no revalidation)
44
+ // ---------------------------------------------------------------------------
45
+
46
+ /**
47
+ * Resolve loaders for an entry and emit segments.
48
+ * Loaders are run lazily via ctx.use() and memoized for parallel execution.
49
+ */
50
+ export async function resolveLoaders<TEnv>(
51
+ entry: EntryData,
52
+ ctx: HandlerContext<any, TEnv>,
53
+ belongsToRoute: boolean,
54
+ deps: SegmentResolutionDeps<TEnv>,
55
+ shortCodeOverride?: string,
56
+ ): Promise<ResolvedSegment[]> {
57
+ const loaderEntries = entry.loader ?? [];
58
+ if (loaderEntries.length === 0) return [];
59
+
60
+ const shortCode = shortCodeOverride ?? entry.shortCode;
61
+ const hasLoading = "loading" in entry && entry.loading !== undefined;
62
+ const loadingDisabled = hasLoading && entry.loading === false;
63
+
64
+ // Emit the streaming (non-awaiting) loader shape when loading is enabled OR
65
+ // during a PPR shell capture. In capture, loaders are masked with
66
+ // never-resolving promises (loader-mask.ts); the loading-disabled branch below
67
+ // AWAITS the loader promises, which would hang the capture render's match()
68
+ // forever on those masked promises. Forcing the streaming shape lets match()
69
+ // complete so the prerender can postpone the loader subtrees as holes. The
70
+ // `!loadingDisabled` short-circuit keeps the ALS check off the hot path (only
71
+ // loading-disabled entries consult it), so normal requests are unchanged.
72
+ const emitStreaming = !loadingDisabled || isShellCaptureActive();
73
+
74
+ // Error context for wrapLoaderPromise: without it, a throwing DSL loader never
75
+ // fires createRouter({ onError }) (phase "loader") nor emits the loader.error
76
+ // telemetry event — wrapLoaderPromise only builds the onError/telemetry path
77
+ // when errorContext is supplied. Built from ctx so the live render path reports
78
+ // loader failures the same way handlers/actions/routing/fetchable-loaders do.
79
+ const errorContext = buildLoaderErrorContext(ctx);
80
+
81
+ if (emitStreaming) {
82
+ // Streaming loaders: promises kick off now, settle during RSC serialization.
83
+ const segments = loaderEntries.map((loaderEntry, i) => {
84
+ const { loader } = loaderEntry;
85
+ const segmentId = `${shortCode}D${i}.${loader.$$id}`;
86
+ return {
87
+ id: segmentId,
88
+ namespace: entry.id,
89
+ type: "loader" as const,
90
+ index: i,
91
+ component: null,
92
+ params: ctx.params,
93
+ loaderId: loader.$$id,
94
+ loaderData: deps.wrapLoaderPromise(
95
+ runInsideLoaderScope(() =>
96
+ resolveLoaderData(loaderEntry, ctx, ctx.pathname),
97
+ ),
98
+ entry,
99
+ segmentId,
100
+ ctx.pathname,
101
+ errorContext,
102
+ ),
103
+ belongsToRoute,
104
+ };
105
+ });
106
+
107
+ return segments;
108
+ }
109
+
110
+ // Loading disabled: still start all loaders in parallel, but only emit
111
+ // settled promises so handlers don't stream loading placeholders.
112
+ //
113
+ // Wrap each loader promise with wrapLoaderPromise BEFORE awaiting. The wrapped
114
+ // promise resolves to a LoaderDataResult and never rejects, routing a failed
115
+ // loader to its own per-loader error boundary. Awaiting the RAW promises here
116
+ // instead would (1) propagate a rejection to the segment-level boundary,
117
+ // collapsing the whole entry and discarding successful sibling data, and
118
+ // (2) leave the other in-flight raw promises without a .catch, producing
119
+ // unhandled rejections. Mirrors the loading path and intercept-resolution.
120
+ const pendingLoaderData = loaderEntries.map((loaderEntry, i) => {
121
+ const { loader } = loaderEntry;
122
+ const segmentId = `${shortCode}D${i}.${loader.$$id}`;
123
+ const wrapped = deps.wrapLoaderPromise(
124
+ runInsideLoaderScope(() =>
125
+ resolveLoaderData(loaderEntry, ctx, ctx.pathname),
126
+ ),
127
+ entry,
128
+ segmentId,
129
+ ctx.pathname,
130
+ errorContext,
131
+ );
132
+ return { wrapped, segmentId };
133
+ });
134
+ await Promise.all(pendingLoaderData.map((p) => p.wrapped));
135
+
136
+ return loaderEntries.map((loaderEntry, i) => {
137
+ const { loader } = loaderEntry;
138
+ const pending = pendingLoaderData[i]!;
139
+ // The "loader:<id>" perf metric is recorded by observePhase at the single
140
+ // loader-metering site (useLoader, reached via ctx.use during
141
+ // resolveLoaderData), with the real per-loader duration rather than a
142
+ // Promise.all batch ceiling.
143
+ return {
144
+ id: pending.segmentId,
145
+ namespace: entry.id,
146
+ type: "loader" as const,
147
+ index: i,
148
+ component: null,
149
+ params: ctx.params,
150
+ loaderId: loader.$$id,
151
+ loaderData: pending.wrapped,
152
+ belongsToRoute,
153
+ };
154
+ });
155
+ }
156
+
157
+ /**
158
+ * Options for segment resolution.
159
+ */
160
+ export interface ResolveSegmentOptions {
161
+ /** When true, skip resolveLoaders() calls (used for pre-rendering) */
162
+ skipLoaders?: boolean;
163
+ /**
164
+ * When true, a thrown render error is re-thrown instead of being converted
165
+ * into an error-boundary segment. Set only by the pre-render path so a
166
+ * build-time render failure (and a `throw new Skip()` inside a render fn)
167
+ * surfaces to the build instead of being silently baked into a frozen error
168
+ * page served as a 200 (issue #587). The live request path leaves this unset,
169
+ * so error boundaries keep catching at request time.
170
+ */
171
+ throwOnError?: boolean;
172
+ }
173
+
174
+ /**
175
+ * Resolve segments from EntryData.
176
+ * Executes middlewares, loaders, parallels, and handlers in correct order.
177
+ * Returns array: [main segment, ...orphan layout segments]
178
+ */
179
+ export async function resolveSegment<TEnv>(
180
+ entry: EntryData,
181
+ routeKey: string,
182
+ params: Record<string, string>,
183
+ context: HandlerContext<any, TEnv>,
184
+ loaderPromises: Map<string, Promise<any>>,
185
+ deps: SegmentResolutionDeps<TEnv>,
186
+ isRouteEntry: boolean = false,
187
+ options?: ResolveSegmentOptions,
188
+ ): Promise<ResolvedSegment[]> {
189
+ const segments: ResolvedSegment[] = [];
190
+
191
+ if (entry.type === "layout" || entry.type === "cache") {
192
+ if (!options?.skipLoaders) {
193
+ const loaderSegments = await resolveLoaders(entry, context, false, deps);
194
+ segments.push(...loaderSegments);
195
+ }
196
+
197
+ // Handler-first: layout handler executes before its parallels and orphan
198
+ // layouts so that ctx.set() values are visible to all children.
199
+ (context as InternalHandlerContext<any, TEnv>)._currentSegmentId =
200
+ entry.shortCode;
201
+
202
+ const doneLayoutHandler = track(`handler:${entry.id}`, 2);
203
+ const component = await resolveLayoutComponent(entry, context);
204
+ doneLayoutHandler();
205
+
206
+ segments.push({
207
+ id: entry.shortCode,
208
+ namespace: entry.id,
209
+ type: "layout",
210
+ index: 0,
211
+ component,
212
+ loading: entry.loading === false ? null : entry.loading,
213
+ transition: applyViewTransitionDefault(
214
+ entry.transition,
215
+ deps.viewTransitionDefault,
216
+ entry.shortCode,
217
+ ),
218
+ params,
219
+ belongsToRoute: false,
220
+ layoutName: entry.id,
221
+ ...(entry.mountPath ? { mountPath: entry.mountPath } : {}),
222
+ });
223
+
224
+ const resolvedParallelEntries = new Set<string>();
225
+ for (const { slot, entry: parallelEntry } of getParallelSlotEntries(
226
+ entry.parallel,
227
+ )) {
228
+ const parallelSegments = await resolveParallelEntry(
229
+ parallelEntry,
230
+ params,
231
+ context,
232
+ false,
233
+ entry.shortCode,
234
+ deps,
235
+ options,
236
+ routeKey,
237
+ [slot],
238
+ !resolvedParallelEntries.has(parallelEntry.id),
239
+ );
240
+ segments.push(...parallelSegments);
241
+ resolvedParallelEntries.add(parallelEntry.id);
242
+ }
243
+
244
+ for (const orphan of entry.layout) {
245
+ const orphanSegments = await resolveOrphanLayout(
246
+ orphan,
247
+ params,
248
+ context,
249
+ loaderPromises,
250
+ false,
251
+ deps,
252
+ options,
253
+ routeKey,
254
+ );
255
+ segments.push(...orphanSegments);
256
+ }
257
+ } else if (entry.type === "route") {
258
+ if (!options?.skipLoaders) {
259
+ const loaderSegments = await resolveLoaders(entry, context, true, deps);
260
+ segments.push(...loaderSegments);
261
+ }
262
+
263
+ // Route handler EXECUTES before its children (orphan layouts, parallels).
264
+ // This lets the handler set() context variables that children can read
265
+ // via get(). Caching wraps all segments together (per-route, not
266
+ // per-segment), so either all run or none do -- no partial scenarios.
267
+ //
268
+ // The handler's segment is PUSHED after orphans/parallels to preserve
269
+ // the correct tree composition order (layouts wrap the route content).
270
+ (context as InternalHandlerContext<any, TEnv>)._currentSegmentId =
271
+ entry.shortCode;
272
+ let component: ReactNode | undefined = await tryStaticHandler(
273
+ entry,
274
+ entry.shortCode,
275
+ );
276
+ if (component === undefined) {
277
+ // For Passthrough routes at runtime, use the live handler instead of
278
+ // the build handler. At build time (context.build === true), always
279
+ // use the build handler from entry.handler.
280
+ const handler =
281
+ !context.build && entry.liveHandler ? entry.liveHandler : entry.handler;
282
+ const doneRouteHandler = track(`handler:${entry.id}`, 2);
283
+ if (entry.loading) {
284
+ const result = handleHandlerResult(
285
+ observeHandler(entry.id, handler, context),
286
+ );
287
+ if (result instanceof Promise) {
288
+ warnOnStreamedResponse(result, entry.id);
289
+ result.finally(doneRouteHandler).catch(() => {});
290
+ const tracked = deps.trackHandler(result, {
291
+ segmentId: entry.shortCode,
292
+ segmentType: entry.type,
293
+ });
294
+ observeStreamedHandler(
295
+ tracked,
296
+ entry.shortCode,
297
+ entry.type,
298
+ context.pathname,
299
+ routeKey,
300
+ params,
301
+ );
302
+ component = tracked;
303
+ } else {
304
+ doneRouteHandler();
305
+ component = result;
306
+ }
307
+ } else {
308
+ component = handleHandlerResult(
309
+ await observeHandler(entry.id, handler, context),
310
+ );
311
+ doneRouteHandler();
312
+ }
313
+ }
314
+
315
+ for (const orphan of entry.layout) {
316
+ const orphanSegments = await resolveOrphanLayout(
317
+ orphan,
318
+ params,
319
+ context,
320
+ loaderPromises,
321
+ true,
322
+ deps,
323
+ options,
324
+ routeKey,
325
+ entry,
326
+ );
327
+ segments.push(...orphanSegments);
328
+ }
329
+
330
+ const resolvedParallelEntries = new Set<string>();
331
+ for (const { slot, entry: parallelEntry } of getParallelSlotEntries(
332
+ entry.parallel,
333
+ )) {
334
+ const parallelSegments = await resolveParallelEntry(
335
+ parallelEntry,
336
+ params,
337
+ context,
338
+ true,
339
+ entry.shortCode,
340
+ deps,
341
+ options,
342
+ routeKey,
343
+ [slot],
344
+ !resolvedParallelEntries.has(parallelEntry.id),
345
+ );
346
+ segments.push(...parallelSegments);
347
+ resolvedParallelEntries.add(parallelEntry.id);
348
+ }
349
+
350
+ segments.push({
351
+ id: entry.shortCode,
352
+ namespace: entry.id,
353
+ type: "route",
354
+ index: 0,
355
+ component: component ?? null,
356
+ loading: entry.loading === false ? null : entry.loading,
357
+ transition: applyViewTransitionDefault(
358
+ entry.transition,
359
+ deps.viewTransitionDefault,
360
+ entry.shortCode,
361
+ ),
362
+ params,
363
+ belongsToRoute: true,
364
+ ...(entry.mountPath ? { mountPath: entry.mountPath } : {}),
365
+ });
366
+ } else {
367
+ throw new Error(`Unknown entry type: ${(entry as any).type}`);
368
+ }
369
+
370
+ return segments;
371
+ }
372
+
373
+ /**
374
+ * Resolve orphan layout with its middlewares, loaders, and parallels.
375
+ */
376
+ export async function resolveOrphanLayout<TEnv>(
377
+ orphan: EntryData,
378
+ params: Record<string, string>,
379
+ context: HandlerContext<any, TEnv>,
380
+ loaderPromises: Map<string, Promise<any>>,
381
+ belongsToRoute: boolean,
382
+ deps: SegmentResolutionDeps<TEnv>,
383
+ options?: ResolveSegmentOptions,
384
+ routeKey?: string,
385
+ /** Parent route entry — its loaders are inherited by the layout so
386
+ * parallel slots inside this layout can access them via useLoader(). */
387
+ parentRouteEntry?: EntryData,
388
+ ): Promise<ResolvedSegment[]> {
389
+ invariant(
390
+ orphan.type === "layout" || orphan.type === "cache",
391
+ `Expected orphan to be a layout or cache, got: ${orphan.type}`,
392
+ );
393
+
394
+ const segments: ResolvedSegment[] = [];
395
+ if (!options?.skipLoaders) {
396
+ const loaderSegments = await resolveLoaders(
397
+ orphan,
398
+ context,
399
+ belongsToRoute,
400
+ deps,
401
+ );
402
+ segments.push(...loaderSegments);
403
+
404
+ // Inherit parent route's loaders so parallel slots inside this layout
405
+ // can access them via useLoader(). Without this, the route's loaders
406
+ // are only in the route's OutletProvider (rendered as <Outlet /> content),
407
+ // which is a child — not a parent — of the layout's context.
408
+ if (
409
+ parentRouteEntry &&
410
+ parentRouteEntry.loader &&
411
+ parentRouteEntry.loader.length > 0 &&
412
+ Object.keys(orphan.parallel).length > 0
413
+ ) {
414
+ const inheritedLoaders = await resolveLoaders(
415
+ parentRouteEntry,
416
+ context,
417
+ belongsToRoute,
418
+ deps,
419
+ orphan.shortCode,
420
+ );
421
+ // Tag as inherited so buildMatchResult can deduplicate when safe
422
+ for (const s of inheritedLoaders) {
423
+ s._inherited = true;
424
+ }
425
+ segments.push(...inheritedLoaders);
426
+ }
427
+ }
428
+
429
+ // Handler-first: orphan layout handler executes before its parallels
430
+ // so that ctx.set() values are visible to parallel children.
431
+ const doneOrphanHandler = track(`handler:${orphan.id}`, 2);
432
+ const component = await resolveLayoutComponent(orphan, context);
433
+ doneOrphanHandler();
434
+
435
+ segments.push({
436
+ id: orphan.shortCode,
437
+ namespace: orphan.id,
438
+ type: "layout",
439
+ index: 0,
440
+ component,
441
+ params,
442
+ belongsToRoute,
443
+ layoutName: orphan.id,
444
+ loading: orphan.loading === false ? null : orphan.loading,
445
+ transition: applyViewTransitionDefault(
446
+ orphan.transition,
447
+ deps.viewTransitionDefault,
448
+ orphan.shortCode,
449
+ ),
450
+ ...(orphan.mountPath ? { mountPath: orphan.mountPath } : {}),
451
+ });
452
+
453
+ const resolvedParallelEntries = new Set<string>();
454
+ for (const { slot, entry: parallelEntry } of getParallelSlotEntries(
455
+ orphan.parallel,
456
+ )) {
457
+ const parallelSegments = await resolveParallelEntry(
458
+ parallelEntry,
459
+ params,
460
+ context,
461
+ belongsToRoute,
462
+ orphan.shortCode,
463
+ deps,
464
+ options,
465
+ routeKey,
466
+ [slot],
467
+ !resolvedParallelEntries.has(parallelEntry.id),
468
+ );
469
+ segments.push(...parallelSegments);
470
+ resolvedParallelEntries.add(parallelEntry.id);
471
+ }
472
+
473
+ return segments;
474
+ }
475
+
476
+ /**
477
+ * Resolve parallel EntryData with its loaders and slot handlers.
478
+ */
479
+ export async function resolveParallelEntry<TEnv>(
480
+ parallelEntry: EntryData,
481
+ params: Record<string, string>,
482
+ context: HandlerContext<any, TEnv>,
483
+ belongsToRoute: boolean,
484
+ parentShortCode: string,
485
+ deps: SegmentResolutionDeps<TEnv>,
486
+ options?: ResolveSegmentOptions,
487
+ routeKey?: string,
488
+ slotNames?: `@${string}`[],
489
+ includeLoaders: boolean = true,
490
+ ): Promise<ResolvedSegment[]> {
491
+ invariant(
492
+ parallelEntry.type === "parallel",
493
+ `Expected parallel entry, got: ${parallelEntry.type}`,
494
+ );
495
+
496
+ const segments: ResolvedSegment[] = [];
497
+
498
+ const slots = parallelEntry.handler as Record<
499
+ `@${string}`,
500
+ | ((ctx: HandlerContext<any, TEnv>) => ReactNode | Promise<ReactNode>)
501
+ | ReactNode
502
+ >;
503
+
504
+ const slotsToResolve = slotNames ?? (Object.keys(slots) as `@${string}`[]);
505
+
506
+ for (const slot of slotsToResolve) {
507
+ // Try static lookup first — in production, handler bodies are evicted
508
+ // and replaced with stubs that have no .handler property (undefined).
509
+ // The static store holds the pre-rendered component for these slots.
510
+ let component: ReactNode | undefined = await tryStaticSlot(
511
+ parallelEntry,
512
+ slot,
513
+ `${parentShortCode}.${slot}`,
514
+ );
515
+
516
+ if (component === undefined) {
517
+ const handler = slots[slot];
518
+ if (handler === undefined) {
519
+ continue;
520
+ }
521
+ // Pin `_currentSegmentId` to the slot's own id so handle pushes from
522
+ // inside the slot handler get their own bucket in the HandleStore.
523
+ // Parent-keying would collapse them into the parent layout's bucket;
524
+ // the partial-update merge then replaces the parent's bucket on a
525
+ // slot-only revalidation and drops layout-pushed Meta/Breadcrumbs.
526
+ // filterSegmentOrder() retains slot ids so the client preserves them.
527
+ (context as InternalHandlerContext<any, TEnv>)._currentSegmentId =
528
+ `${parentShortCode}.${slot}`;
529
+ const doneParallelHandler = track(
530
+ `handler:${parallelEntry.id}.${slot}`,
531
+ 2,
532
+ );
533
+ const hasLoadingFallback =
534
+ parallelEntry.loading !== undefined && parallelEntry.loading !== false;
535
+ if (hasLoadingFallback) {
536
+ const result =
537
+ typeof handler === "function"
538
+ ? observeHandler(`${parallelEntry.id}.${slot}`, handler, context)
539
+ : handler;
540
+ if (result instanceof Promise) {
541
+ result.finally(doneParallelHandler).catch(() => {});
542
+ const tracked = deps.trackHandler(result, {
543
+ segmentId: `${parentShortCode}.${slot}`,
544
+ segmentType: "parallel",
545
+ });
546
+ observeStreamedHandler(
547
+ tracked,
548
+ `${parentShortCode}.${slot}`,
549
+ "parallel",
550
+ context.pathname,
551
+ routeKey,
552
+ params,
553
+ );
554
+ component = tracked as ReactNode;
555
+ } else {
556
+ doneParallelHandler();
557
+ component = result as ReactNode;
558
+ }
559
+ } else {
560
+ component =
561
+ typeof handler === "function"
562
+ ? await observeHandler(
563
+ `${parallelEntry.id}.${slot}`,
564
+ handler,
565
+ context,
566
+ )
567
+ : handler;
568
+ doneParallelHandler();
569
+ }
570
+ }
571
+
572
+ segments.push({
573
+ id: `${parentShortCode}.${slot}`,
574
+ namespace: parallelEntry.id,
575
+ type: "parallel",
576
+ index: 0,
577
+ component,
578
+ loading: parallelEntry.loading === false ? null : parallelEntry.loading,
579
+ transition: applyViewTransitionDefault(
580
+ parallelEntry.transition,
581
+ deps.viewTransitionDefault,
582
+ `${parentShortCode}.${slot}`,
583
+ ),
584
+ params,
585
+ slot,
586
+ belongsToRoute,
587
+ parallelName: `${parallelEntry.id}.${slot}`,
588
+ ...(parallelEntry.mountPath
589
+ ? { mountPath: parallelEntry.mountPath }
590
+ : {}),
591
+ });
592
+ }
593
+
594
+ if (!options?.skipLoaders && includeLoaders) {
595
+ const loaderSegments = await resolveLoaders(
596
+ parallelEntry,
597
+ context,
598
+ belongsToRoute,
599
+ deps,
600
+ parentShortCode,
601
+ );
602
+ // Tag parallel-owned loaders so renderSegments can stream them
603
+ // using the parallel's loading() instead of awaiting on the layout
604
+ const parallelLoading =
605
+ parallelEntry.loading === false ? undefined : parallelEntry.loading;
606
+ if (parallelLoading) {
607
+ for (const seg of loaderSegments) {
608
+ seg.parallelLoading = parallelLoading;
609
+ }
610
+ }
611
+ segments.push(...loaderSegments);
612
+ }
613
+
614
+ return segments;
615
+ }
616
+
617
+ /**
618
+ * Resolve all segments for a route (used for single-cache-per-request pattern).
619
+ */
620
+ export async function resolveAllSegments<TEnv>(
621
+ entries: EntryData[],
622
+ routeKey: string,
623
+ params: Record<string, string>,
624
+ context: HandlerContext<any, TEnv>,
625
+ loaderPromises: Map<string, Promise<any>>,
626
+ deps: SegmentResolutionDeps<TEnv>,
627
+ options?: ResolveSegmentOptions,
628
+ ): Promise<ResolvedSegment[]> {
629
+ const allSegments: ResolvedSegment[] = [];
630
+ const seenIds = new Set<string>();
631
+
632
+ // Safe request access: during build-time prerendering, context.request
633
+ // is a throwing getter. Use undefined when unavailable.
634
+ let safeRequest: Request | undefined;
635
+ try {
636
+ safeRequest = context.request;
637
+ } catch {}
638
+
639
+ // Get telemetry sink from RouterContext (may not exist during prerendering)
640
+ let telemetry;
641
+ try {
642
+ telemetry = getRouterContext()?.telemetry;
643
+ } catch {}
644
+
645
+ for (const entry of entries) {
646
+ // Set ALS flag when entering a cache() boundary so that ctx.get()
647
+ // can guard non-cacheable variable reads. Also guards response-level
648
+ // side effects (headers.set). Persists for all descendant entries.
649
+ if (entry.type === "cache") {
650
+ const store = RangoContext.getStore();
651
+ if (store) store.insideCacheScope = true;
652
+ }
653
+ const doneEntry = track(`segment:${entry.id}`, 1);
654
+ const resolvedSegments = await resolveWithErrorBoundary(
655
+ entry,
656
+ params,
657
+ () =>
658
+ resolveSegment(
659
+ entry,
660
+ routeKey,
661
+ params,
662
+ context,
663
+ loaderPromises,
664
+ deps,
665
+ false,
666
+ options,
667
+ ),
668
+ (seg) => [seg],
669
+ deps,
670
+ { request: safeRequest, url: context.url, routeKey, telemetry },
671
+ context.pathname,
672
+ options?.throwOnError,
673
+ );
674
+ doneEntry();
675
+ // Deduplicate by segment ID. include() scopes can produce entries that
676
+ // resolve the same shared layout/loader segment. Duplicates in the segment
677
+ // array propagate to the client's matched[] and change the React tree depth.
678
+ for (const seg of resolvedSegments) {
679
+ if (!seenIds.has(seg.id)) {
680
+ seenIds.add(seg.id);
681
+ allSegments.push(seg);
682
+ }
683
+ }
684
+ }
685
+
686
+ return allSegments;
687
+ }
688
+
689
+ /**
690
+ * Resolve only loader segments for all entries (used when serving cached non-loader segments).
691
+ */
692
+ export async function resolveLoadersOnly<TEnv>(
693
+ entries: EntryData[],
694
+ context: HandlerContext<any, TEnv>,
695
+ deps: SegmentResolutionDeps<TEnv>,
696
+ ): Promise<ResolvedSegment[]> {
697
+ const loaderSegments: ResolvedSegment[] = [];
698
+ const seenIds = new Set<string>();
699
+
700
+ async function collectEntryLoaders(
701
+ entry: EntryData,
702
+ belongsToRoute: boolean,
703
+ shortCodeOverride?: string,
704
+ ): Promise<void> {
705
+ // Skip if all loaders from this entry have already been resolved
706
+ // via a parent (e.g., cache boundary wrapping a layout with shared loaders).
707
+ const entryLoaders = entry.loader ?? [];
708
+ const sc = shortCodeOverride ?? entry.shortCode;
709
+ const allAlreadySeen =
710
+ entryLoaders.length > 0 &&
711
+ entryLoaders.every((le, i) =>
712
+ seenIds.has(`${sc}D${i}.${le.loader.$$id}`),
713
+ );
714
+ if (!allAlreadySeen) {
715
+ const segments = await resolveLoaders(
716
+ entry,
717
+ context,
718
+ belongsToRoute,
719
+ deps,
720
+ shortCodeOverride,
721
+ );
722
+ for (const seg of segments) {
723
+ if (!seenIds.has(seg.id)) {
724
+ seenIds.add(seg.id);
725
+ loaderSegments.push(seg);
726
+ }
727
+ }
728
+ }
729
+
730
+ const seenParallelEntryIds = new Set<string>();
731
+ for (const parallelEntry of getParallelEntries(entry.parallel)) {
732
+ if (seenParallelEntryIds.has(parallelEntry.id)) continue;
733
+ seenParallelEntryIds.add(parallelEntry.id);
734
+ await collectEntryLoaders(parallelEntry, belongsToRoute, entry.shortCode);
735
+ }
736
+
737
+ const childBelongsToRoute = belongsToRoute || entry.type === "route";
738
+ for (const layoutEntry of entry.layout) {
739
+ await collectEntryLoaders(layoutEntry, childBelongsToRoute);
740
+ // Inherit route loaders for orphan layouts with parallels.
741
+ // Resolve directly — do NOT re-enter collectEntryLoaders with the
742
+ // route entry, as that would re-iterate route.layout and loop.
743
+ if (
744
+ entry.type === "route" &&
745
+ entry.loader &&
746
+ entry.loader.length > 0 &&
747
+ Object.keys(layoutEntry.parallel).length > 0
748
+ ) {
749
+ const inherited = await resolveLoaders(
750
+ entry,
751
+ context,
752
+ childBelongsToRoute,
753
+ deps,
754
+ layoutEntry.shortCode,
755
+ );
756
+ for (const seg of inherited) {
757
+ if (!seenIds.has(seg.id)) {
758
+ seenIds.add(seg.id);
759
+ seg._inherited = true;
760
+ loaderSegments.push(seg);
761
+ }
762
+ }
763
+ }
764
+ }
765
+ }
766
+
767
+ for (const entry of entries) {
768
+ await collectEntryLoaders(entry, entry.type === "route");
769
+ }
770
+
771
+ return loaderSegments;
772
+ }