@rangojs/router 0.0.0-experimental.19 → 0.0.0-experimental.1c0bdfad

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 (406) hide show
  1. package/AGENTS.md +17 -0
  2. package/README.md +291 -61
  3. package/dist/bin/rango.js +544 -143
  4. package/dist/testing/vitest.js +82 -0
  5. package/dist/vite/index.js +3744 -1329
  6. package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
  7. package/package.json +67 -13
  8. package/skills/api-client/SKILL.md +211 -0
  9. package/skills/breadcrumbs/SKILL.md +312 -0
  10. package/skills/bundle-analysis/SKILL.md +159 -0
  11. package/skills/cache-guide/SKILL.md +247 -23
  12. package/skills/caching/SKILL.md +322 -19
  13. package/skills/composability/SKILL.md +27 -2
  14. package/skills/css/SKILL.md +76 -0
  15. package/skills/debug-manifest/SKILL.md +4 -2
  16. package/skills/document-cache/SKILL.md +78 -55
  17. package/skills/handler-use/SKILL.md +364 -0
  18. package/skills/hooks/SKILL.md +282 -60
  19. package/skills/host-router/SKILL.md +278 -0
  20. package/skills/i18n/SKILL.md +276 -0
  21. package/skills/intercept/SKILL.md +50 -6
  22. package/skills/layout/SKILL.md +35 -9
  23. package/skills/links/SKILL.md +249 -17
  24. package/skills/loader/SKILL.md +297 -31
  25. package/skills/middleware/SKILL.md +52 -13
  26. package/skills/migrate-nextjs/SKILL.md +584 -0
  27. package/skills/migrate-react-router/SKILL.md +771 -0
  28. package/skills/mime-routes/SKILL.md +28 -1
  29. package/skills/observability/SKILL.md +172 -0
  30. package/skills/parallel/SKILL.md +203 -7
  31. package/skills/prerender/SKILL.md +155 -111
  32. package/skills/rango/SKILL.md +251 -23
  33. package/skills/react-compiler/SKILL.md +168 -0
  34. package/skills/response-routes/SKILL.md +123 -48
  35. package/skills/route/SKILL.md +104 -9
  36. package/skills/router-setup/SKILL.md +124 -11
  37. package/skills/scripts/SKILL.md +179 -0
  38. package/skills/server-actions/SKILL.md +775 -0
  39. package/skills/streams-and-websockets/SKILL.md +283 -0
  40. package/skills/tailwind/SKILL.md +27 -3
  41. package/skills/testing/SKILL.md +125 -222
  42. package/skills/testing/bindings.md +103 -0
  43. package/skills/testing/cache-prerender.md +127 -0
  44. package/skills/testing/client-components.md +124 -0
  45. package/skills/testing/e2e-parity.md +125 -0
  46. package/skills/testing/flight.md +91 -0
  47. package/skills/testing/handles.md +129 -0
  48. package/skills/testing/loader.md +128 -0
  49. package/skills/testing/middleware.md +99 -0
  50. package/skills/testing/render-handler.md +121 -0
  51. package/skills/testing/response-routes.md +95 -0
  52. package/skills/testing/reverse-and-types.md +84 -0
  53. package/skills/testing/server-actions.md +107 -0
  54. package/skills/testing/server-tree.md +128 -0
  55. package/skills/testing/setup.md +123 -0
  56. package/skills/typesafety/SKILL.md +357 -52
  57. package/skills/use-cache/SKILL.md +46 -14
  58. package/skills/view-transitions/SKILL.md +294 -0
  59. package/src/__augment-tests__/augment.ts +81 -0
  60. package/src/__augment-tests__/augmented.check.ts +116 -0
  61. package/src/__internal.ts +67 -40
  62. package/src/bin/rango.ts +18 -0
  63. package/src/browser/action-coordinator.ts +53 -36
  64. package/src/browser/action-fence.ts +47 -0
  65. package/src/browser/app-shell.ts +39 -0
  66. package/src/browser/app-version.ts +14 -0
  67. package/src/browser/connection-warmup.ts +134 -0
  68. package/src/browser/cookie-name.ts +140 -0
  69. package/src/browser/event-controller.ts +197 -150
  70. package/src/browser/history-state.ts +21 -0
  71. package/src/browser/index.ts +3 -3
  72. package/src/browser/invalidate-client-cache.ts +52 -0
  73. package/src/browser/link-interceptor.ts +4 -0
  74. package/src/browser/navigation-bridge.ts +200 -30
  75. package/src/browser/navigation-client.ts +217 -58
  76. package/src/browser/navigation-store-handle.ts +38 -0
  77. package/src/browser/navigation-store.ts +76 -67
  78. package/src/browser/navigation-transaction.ts +18 -66
  79. package/src/browser/network-error-handler.ts +34 -7
  80. package/src/browser/partial-update.ts +187 -112
  81. package/src/browser/prefetch/cache.ts +312 -30
  82. package/src/browser/prefetch/fetch.ts +344 -47
  83. package/src/browser/prefetch/policy.ts +6 -0
  84. package/src/browser/prefetch/queue.ts +126 -20
  85. package/src/browser/prefetch/resource-ready.ts +77 -0
  86. package/src/browser/rango-state.ts +158 -76
  87. package/src/browser/react/Link.tsx +125 -18
  88. package/src/browser/react/NavigationProvider.tsx +135 -120
  89. package/src/browser/react/ScrollRestoration.tsx +10 -6
  90. package/src/browser/react/context.ts +7 -2
  91. package/src/browser/react/filter-segment-order.ts +66 -7
  92. package/src/browser/react/index.ts +0 -48
  93. package/src/browser/react/location-state-shared.ts +178 -8
  94. package/src/browser/react/location-state.ts +39 -14
  95. package/src/browser/react/use-action.ts +6 -15
  96. package/src/browser/react/use-handle.ts +23 -69
  97. package/src/browser/react/use-href.tsx +8 -1
  98. package/src/browser/react/use-link-status.ts +33 -8
  99. package/src/browser/react/use-navigation.ts +32 -7
  100. package/src/browser/react/use-params.ts +20 -10
  101. package/src/browser/react/use-reverse.ts +106 -0
  102. package/src/browser/react/use-router.ts +46 -11
  103. package/src/browser/react/use-search-params.ts +0 -5
  104. package/src/browser/react/use-segments.ts +11 -21
  105. package/src/browser/response-adapter.ts +80 -5
  106. package/src/browser/rsc-router.tsx +226 -75
  107. package/src/browser/scroll-restoration.ts +54 -42
  108. package/src/browser/segment-reconciler.ts +36 -9
  109. package/src/browser/segment-structure-assert.ts +2 -2
  110. package/src/browser/server-action-bridge.ts +619 -442
  111. package/src/browser/types.ts +115 -11
  112. package/src/browser/validate-redirect-origin.ts +43 -16
  113. package/src/build/collect-fallback-refs.ts +107 -0
  114. package/src/build/generate-manifest.ts +65 -40
  115. package/src/build/generate-route-types.ts +7 -1
  116. package/src/build/index.ts +8 -2
  117. package/src/build/prefix-tree-utils.ts +123 -0
  118. package/src/build/route-trie.ts +182 -37
  119. package/src/build/route-types/ast-route-extraction.ts +15 -8
  120. package/src/build/route-types/codegen.ts +16 -5
  121. package/src/build/route-types/include-resolution.ts +125 -24
  122. package/src/build/route-types/param-extraction.ts +6 -3
  123. package/src/build/route-types/per-module-writer.ts +22 -6
  124. package/src/build/route-types/router-processing.ts +392 -106
  125. package/src/build/route-types/scan-filter.ts +9 -2
  126. package/src/build/route-types/source-scan.ts +216 -0
  127. package/src/build/runtime-discovery.ts +9 -20
  128. package/src/cache/cache-error.ts +104 -0
  129. package/src/cache/cache-key-utils.ts +29 -13
  130. package/src/cache/cache-policy.ts +108 -34
  131. package/src/cache/cache-runtime.ts +214 -48
  132. package/src/cache/cache-scope.ts +236 -89
  133. package/src/cache/cache-tag.ts +103 -0
  134. package/src/cache/cf/cf-base64.ts +33 -0
  135. package/src/cache/cf/cf-cache-constants.ts +127 -0
  136. package/src/cache/cf/cf-cache-store.ts +2224 -171
  137. package/src/cache/cf/cf-cache-types.ts +349 -0
  138. package/src/cache/cf/cf-kv-utils.ts +46 -0
  139. package/src/cache/cf/cf-tag-marker-memo.ts +105 -0
  140. package/src/cache/cf/index.ts +11 -17
  141. package/src/cache/document-cache.ts +89 -27
  142. package/src/cache/handle-snapshot.ts +70 -0
  143. package/src/cache/index.ts +11 -20
  144. package/src/cache/memory-segment-store.ts +136 -37
  145. package/src/cache/profile-registry.ts +31 -31
  146. package/src/cache/read-through-swr.ts +41 -11
  147. package/src/cache/segment-codec.ts +9 -17
  148. package/src/cache/tag-invalidation.ts +230 -0
  149. package/src/cache/taint.ts +55 -0
  150. package/src/cache/types.ts +37 -100
  151. package/src/client.rsc.tsx +45 -21
  152. package/src/client.tsx +120 -336
  153. package/src/cloudflare/index.ts +11 -0
  154. package/src/cloudflare/tracing.ts +109 -0
  155. package/src/component-utils.ts +19 -0
  156. package/src/components/DefaultDocument.tsx +8 -2
  157. package/src/context-var.ts +84 -2
  158. package/src/debug.ts +2 -2
  159. package/src/decode-loader-results.ts +52 -0
  160. package/src/defer.ts +196 -0
  161. package/src/deps/ssr.ts +0 -1
  162. package/src/encode-kv.ts +49 -0
  163. package/src/errors.ts +30 -4
  164. package/src/escape-script.ts +52 -0
  165. package/src/handle.ts +70 -22
  166. package/src/handles/MetaTags.tsx +56 -19
  167. package/src/handles/Scripts.tsx +183 -0
  168. package/src/handles/breadcrumbs.ts +95 -0
  169. package/src/handles/is-thenable.ts +19 -0
  170. package/src/handles/meta.ts +51 -40
  171. package/src/handles/script.ts +244 -0
  172. package/src/host/cookie-handler.ts +9 -60
  173. package/src/host/errors.ts +0 -24
  174. package/src/host/index.ts +8 -5
  175. package/src/host/pattern-matcher.ts +23 -52
  176. package/src/host/router.ts +107 -99
  177. package/src/host/testing.ts +40 -27
  178. package/src/host/types.ts +37 -4
  179. package/src/host/utils.ts +1 -1
  180. package/src/href-client.ts +137 -22
  181. package/src/index.rsc.ts +79 -29
  182. package/src/index.ts +149 -65
  183. package/src/internal-debug.ts +11 -10
  184. package/src/loader-store.ts +500 -0
  185. package/src/loader.rsc.ts +20 -13
  186. package/src/loader.ts +12 -11
  187. package/src/missing-id-error.ts +68 -0
  188. package/src/outlet-context.ts +1 -1
  189. package/src/outlet-provider.tsx +1 -5
  190. package/src/prerender/param-hash.ts +16 -16
  191. package/src/prerender/store.ts +63 -26
  192. package/src/prerender.ts +198 -82
  193. package/src/redirect-origin.ts +100 -0
  194. package/src/regex-escape.ts +8 -0
  195. package/src/render-error-thrower.tsx +20 -0
  196. package/src/response-utils.ts +62 -0
  197. package/src/reverse.ts +65 -15
  198. package/src/root-error-boundary.tsx +1 -19
  199. package/src/route-content-wrapper.tsx +7 -72
  200. package/src/route-definition/dsl-helpers.ts +469 -276
  201. package/src/route-definition/helper-factories.ts +29 -139
  202. package/src/route-definition/helpers-types.ts +113 -37
  203. package/src/route-definition/index.ts +3 -3
  204. package/src/route-definition/redirect.ts +53 -12
  205. package/src/route-definition/resolve-handler-use.ts +161 -0
  206. package/src/route-definition/use-item-types.ts +32 -0
  207. package/src/route-map-builder.ts +7 -17
  208. package/src/route-types.ts +37 -41
  209. package/src/router/basename.ts +14 -0
  210. package/src/router/content-negotiation.ts +164 -17
  211. package/src/router/error-handling.ts +45 -18
  212. package/src/router/find-match.ts +45 -22
  213. package/src/router/handler-context.ts +110 -39
  214. package/src/router/instrument.ts +350 -0
  215. package/src/router/intercept-resolution.ts +50 -24
  216. package/src/router/lazy-includes.ts +19 -53
  217. package/src/router/loader-resolution.ts +274 -56
  218. package/src/router/logging.ts +5 -8
  219. package/src/router/manifest.ts +49 -45
  220. package/src/router/match-api.ts +121 -205
  221. package/src/router/match-context.ts +0 -22
  222. package/src/router/match-handlers.ts +58 -58
  223. package/src/router/match-middleware/background-revalidation.ts +33 -6
  224. package/src/router/match-middleware/cache-lookup.ts +214 -263
  225. package/src/router/match-middleware/cache-store.ts +73 -33
  226. package/src/router/match-middleware/intercept-resolution.ts +8 -28
  227. package/src/router/match-middleware/segment-resolution.ts +52 -18
  228. package/src/router/match-pipelines.ts +1 -42
  229. package/src/router/match-result.ts +104 -49
  230. package/src/router/metrics.ts +217 -26
  231. package/src/router/middleware-types.ts +24 -110
  232. package/src/router/middleware.ts +384 -197
  233. package/src/router/navigation-snapshot.ts +131 -0
  234. package/src/router/params-util.ts +23 -0
  235. package/src/router/pattern-matching.ts +148 -91
  236. package/src/router/prefetch-cache-ttl.ts +51 -0
  237. package/src/router/prerender-match.ts +199 -56
  238. package/src/router/preview-match.ts +32 -102
  239. package/src/router/request-classification.ts +276 -0
  240. package/src/router/revalidation.ts +144 -74
  241. package/src/router/route-snapshot.ts +244 -0
  242. package/src/router/router-context.ts +8 -28
  243. package/src/router/router-interfaces.ts +129 -36
  244. package/src/router/router-options.ts +185 -23
  245. package/src/router/router-registry.ts +2 -5
  246. package/src/router/segment-resolution/fresh.ts +281 -76
  247. package/src/router/segment-resolution/helpers.ts +116 -31
  248. package/src/router/segment-resolution/loader-cache.ts +63 -37
  249. package/src/router/segment-resolution/revalidation.ts +493 -391
  250. package/src/router/segment-resolution/static-store.ts +19 -5
  251. package/src/router/segment-resolution/streamed-handler-telemetry.ts +52 -0
  252. package/src/router/segment-resolution/view-transition-default.ts +36 -0
  253. package/src/router/segment-resolution.ts +5 -1
  254. package/src/router/segment-wrappers.ts +8 -5
  255. package/src/router/state-cookie-name.ts +33 -0
  256. package/src/router/substitute-pattern-params.ts +56 -0
  257. package/src/router/telemetry-otel.ts +161 -199
  258. package/src/router/telemetry.ts +96 -19
  259. package/src/router/timeout.ts +0 -20
  260. package/src/router/tracing.ts +206 -0
  261. package/src/router/trie-matching.ts +180 -58
  262. package/src/router/types.ts +10 -63
  263. package/src/router/url-params.ts +44 -0
  264. package/src/router.ts +182 -54
  265. package/src/rsc/handler-context.ts +3 -2
  266. package/src/rsc/handler.ts +702 -460
  267. package/src/rsc/helpers.ts +168 -46
  268. package/src/rsc/index.ts +2 -25
  269. package/src/rsc/json-route-result.ts +38 -0
  270. package/src/rsc/loader-fetch.ts +127 -31
  271. package/src/rsc/manifest-init.ts +33 -42
  272. package/src/rsc/origin-guard.ts +39 -25
  273. package/src/rsc/progressive-enhancement.ts +98 -19
  274. package/src/rsc/redirect-guard.ts +99 -0
  275. package/src/rsc/response-cache-serve.ts +238 -0
  276. package/src/rsc/response-error.ts +79 -12
  277. package/src/rsc/response-route-handler.ts +99 -189
  278. package/src/rsc/rsc-rendering.ts +126 -106
  279. package/src/rsc/runtime-warnings.ts +23 -10
  280. package/src/rsc/server-action.ts +269 -114
  281. package/src/rsc/ssr-setup.ts +144 -0
  282. package/src/rsc/types.ts +34 -6
  283. package/src/runtime-env.ts +18 -0
  284. package/src/search-params.ts +49 -41
  285. package/src/segment-content-promise.ts +67 -0
  286. package/src/segment-loader-promise.ts +149 -0
  287. package/src/segment-system.tsx +281 -129
  288. package/src/serialize.ts +243 -0
  289. package/src/server/context.ts +317 -63
  290. package/src/server/cookie-parse.ts +32 -0
  291. package/src/server/cookie-store.ts +80 -5
  292. package/src/server/handle-store.ts +40 -38
  293. package/src/server/loader-registry.ts +26 -46
  294. package/src/server/request-context.ts +425 -177
  295. package/src/server.ts +6 -0
  296. package/src/ssr/index.tsx +25 -16
  297. package/src/static-handler.ts +27 -18
  298. package/src/testing/cache-status.ts +162 -0
  299. package/src/testing/collect-handle.ts +40 -0
  300. package/src/testing/dispatch.ts +701 -0
  301. package/src/testing/dom.entry.ts +22 -0
  302. package/src/testing/e2e/fixture.ts +188 -0
  303. package/src/testing/e2e/index.ts +128 -0
  304. package/src/testing/e2e/matchers.ts +35 -0
  305. package/src/testing/e2e/page-helpers.ts +272 -0
  306. package/src/testing/e2e/parity.ts +387 -0
  307. package/src/testing/e2e/server.ts +195 -0
  308. package/src/testing/flight-matchers.ts +97 -0
  309. package/src/testing/flight-normalize.ts +11 -0
  310. package/src/testing/flight-runtime.d.ts +57 -0
  311. package/src/testing/flight-tree.ts +682 -0
  312. package/src/testing/flight.entry.ts +52 -0
  313. package/src/testing/flight.ts +257 -0
  314. package/src/testing/generated-routes.ts +183 -0
  315. package/src/testing/index.ts +99 -0
  316. package/src/testing/internal/context.ts +371 -0
  317. package/src/testing/internal/flight-client-globals.ts +30 -0
  318. package/src/testing/internal/seed-vars.ts +54 -0
  319. package/src/testing/render-handler.ts +343 -0
  320. package/src/testing/render-route.tsx +581 -0
  321. package/src/testing/run-loader.ts +385 -0
  322. package/src/testing/run-middleware.ts +205 -0
  323. package/src/testing/vitest-stubs/cloudflare-email.ts +9 -0
  324. package/src/testing/vitest-stubs/cloudflare-workers.ts +21 -0
  325. package/src/testing/vitest-stubs/plugin-rsc.ts +16 -0
  326. package/src/testing/vitest-stubs/version.ts +5 -0
  327. package/src/testing/vitest.ts +305 -0
  328. package/src/theme/ThemeProvider.tsx +20 -58
  329. package/src/theme/ThemeScript.tsx +7 -9
  330. package/src/theme/constants.ts +52 -13
  331. package/src/theme/index.ts +3 -19
  332. package/src/theme/theme-context.ts +1 -5
  333. package/src/theme/theme-script.ts +22 -21
  334. package/src/theme/use-theme.ts +0 -3
  335. package/src/types/boundaries.ts +0 -35
  336. package/src/types/cache-types.ts +17 -8
  337. package/src/types/error-types.ts +30 -90
  338. package/src/types/global-namespace.ts +54 -41
  339. package/src/types/handler-context.ts +236 -88
  340. package/src/types/index.ts +1 -10
  341. package/src/types/loader-types.ts +44 -15
  342. package/src/types/request-scope.ts +112 -0
  343. package/src/types/route-config.ts +10 -45
  344. package/src/types/route-entry.ts +19 -7
  345. package/src/types/segments.ts +37 -19
  346. package/src/urls/include-helper.ts +33 -70
  347. package/src/urls/index.ts +1 -11
  348. package/src/urls/path-helper-types.ts +58 -11
  349. package/src/urls/path-helper.ts +57 -111
  350. package/src/urls/pattern-types.ts +48 -19
  351. package/src/urls/response-types.ts +25 -22
  352. package/src/urls/type-extraction.ts +58 -139
  353. package/src/urls/urls-function.ts +1 -18
  354. package/src/use-loader.tsx +346 -89
  355. package/src/vite/debug.ts +185 -0
  356. package/src/vite/discovery/bundle-postprocess.ts +64 -91
  357. package/src/vite/discovery/discover-routers.ts +147 -88
  358. package/src/vite/discovery/discovery-errors.ts +194 -0
  359. package/src/vite/discovery/gate-state.ts +171 -0
  360. package/src/vite/discovery/prerender-collection.ts +247 -145
  361. package/src/vite/discovery/route-types-writer.ts +40 -84
  362. package/src/vite/discovery/self-gen-tracking.ts +27 -1
  363. package/src/vite/discovery/state.ts +61 -13
  364. package/src/vite/discovery/virtual-module-codegen.ts +14 -34
  365. package/src/vite/index.ts +10 -3
  366. package/src/vite/inject-client-debug.ts +36 -0
  367. package/src/vite/plugin-types.ts +155 -65
  368. package/src/vite/plugins/cjs-to-esm.ts +16 -19
  369. package/src/vite/plugins/client-ref-dedup.ts +120 -0
  370. package/src/vite/plugins/client-ref-hashing.ts +28 -15
  371. package/src/vite/plugins/cloudflare-protocol-loader-hook.d.mts +23 -0
  372. package/src/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
  373. package/src/vite/plugins/cloudflare-protocol-stub.ts +194 -0
  374. package/src/vite/plugins/expose-action-id.ts +49 -98
  375. package/src/vite/plugins/expose-id-utils.ts +96 -51
  376. package/src/vite/plugins/expose-ids/export-analysis.ts +101 -34
  377. package/src/vite/plugins/expose-ids/handler-transform.ts +15 -64
  378. package/src/vite/plugins/expose-ids/loader-transform.ts +14 -24
  379. package/src/vite/plugins/expose-ids/router-transform.ts +118 -29
  380. package/src/vite/plugins/expose-internal-ids.ts +553 -317
  381. package/src/vite/plugins/performance-tracks.ts +89 -0
  382. package/src/vite/plugins/refresh-cmd.ts +127 -0
  383. package/src/vite/plugins/use-cache-transform.ts +73 -83
  384. package/src/vite/plugins/version-injector.ts +21 -25
  385. package/src/vite/plugins/version-plugin.ts +46 -37
  386. package/src/vite/plugins/virtual-entries.ts +13 -18
  387. package/src/vite/rango.ts +241 -287
  388. package/src/vite/router-discovery.ts +956 -149
  389. package/src/vite/utils/ast-handler-extract.ts +26 -35
  390. package/src/vite/utils/banner.ts +4 -4
  391. package/src/vite/utils/bundle-analysis.ts +10 -15
  392. package/src/vite/utils/client-chunks.ts +184 -0
  393. package/src/vite/utils/directive-prologue.ts +40 -0
  394. package/src/vite/utils/forward-user-plugins.ts +171 -0
  395. package/src/vite/utils/manifest-utils.ts +4 -59
  396. package/src/vite/utils/package-resolution.ts +20 -52
  397. package/src/vite/utils/prerender-utils.ts +141 -34
  398. package/src/vite/utils/shared-utils.ts +92 -42
  399. package/CLAUDE.md +0 -5
  400. package/src/browser/action-response-classifier.ts +0 -99
  401. package/src/browser/react/use-client-cache.ts +0 -58
  402. package/src/browser/shallow.ts +0 -40
  403. package/src/handles/index.ts +0 -6
  404. package/src/network-error-thrower.tsx +0 -23
  405. package/src/route-definition/route-function.ts +0 -119
  406. package/src/router/middleware-cookies.ts +0 -55
@@ -7,7 +7,11 @@
7
7
 
8
8
  import type { ReactNode } from "react";
9
9
  import { invariant } from "../../errors";
10
- import type { EntryData } from "../../server/context";
10
+ import {
11
+ getParallelEntries,
12
+ getParallelSlotEntries,
13
+ type EntryData,
14
+ } from "../../server/context";
11
15
  import type {
12
16
  HandlerContext,
13
17
  InternalHandlerContext,
@@ -21,52 +25,18 @@ import {
21
25
  tryStaticSlot,
22
26
  resolveLayoutComponent,
23
27
  resolveWithErrorBoundary,
28
+ warnOnStreamedResponse,
29
+ buildLoaderErrorContext,
24
30
  } from "./helpers.js";
31
+ import { applyViewTransitionDefault } from "./view-transition-default.js";
25
32
  import { getRouterContext } from "../router-context.js";
26
- import { resolveSink, safeEmit } from "../telemetry.js";
27
-
28
- // ---------------------------------------------------------------------------
29
- // Streamed handler telemetry
30
- // ---------------------------------------------------------------------------
31
-
32
- /**
33
- * Attach a fire-and-forget rejection observer to a streamed handler promise.
34
- * React catches the actual error via its error boundary; this only emits
35
- * the handler.error telemetry event.
36
- */
37
- function observeStreamedHandler(
38
- promise: Promise<ReactNode>,
39
- segmentId: string,
40
- segmentType: string,
41
- pathname?: string,
42
- routeKey?: string,
43
- params?: Record<string, string>,
44
- ): void {
45
- let routerCtx;
46
- try {
47
- routerCtx = getRouterContext();
48
- } catch {
49
- return;
50
- }
51
- if (!routerCtx?.telemetry) return;
52
- const sink = resolveSink(routerCtx.telemetry);
53
- const reqId = routerCtx.requestId;
54
- promise.catch((err: unknown) => {
55
- const errorObj = err instanceof Error ? err : new Error(String(err));
56
- safeEmit(sink, {
57
- type: "handler.error",
58
- timestamp: performance.now(),
59
- requestId: reqId,
60
- segmentId,
61
- segmentType,
62
- error: errorObj,
63
- handledByBoundary: true,
64
- pathname,
65
- routeKey,
66
- params,
67
- });
68
- });
69
- }
33
+ import { observeStreamedHandler } from "./streamed-handler-telemetry.js";
34
+ import { observeHandler } from "../instrument.js";
35
+ import {
36
+ track,
37
+ RangoContext,
38
+ runInsideLoaderScope,
39
+ } from "../../server/context.js";
70
40
 
71
41
  // ---------------------------------------------------------------------------
72
42
  // Fresh path (full match, no revalidation)
@@ -90,8 +60,16 @@ export async function resolveLoaders<TEnv>(
90
60
  const hasLoading = "loading" in entry && entry.loading !== undefined;
91
61
  const loadingDisabled = hasLoading && entry.loading === false;
92
62
 
63
+ // Error context for wrapLoaderPromise: without it, a throwing DSL loader never
64
+ // fires createRouter({ onError }) (phase "loader") nor emits the loader.error
65
+ // telemetry event — wrapLoaderPromise only builds the onError/telemetry path
66
+ // when errorContext is supplied. Built from ctx so the live render path reports
67
+ // loader failures the same way handlers/actions/routing/fetchable-loaders do.
68
+ const errorContext = buildLoaderErrorContext(ctx);
69
+
93
70
  if (!loadingDisabled) {
94
- return loaderEntries.map((loaderEntry, i) => {
71
+ // Streaming loaders: promises kick off now, settle during RSC serialization.
72
+ const segments = loaderEntries.map((loaderEntry, i) => {
95
73
  const { loader } = loaderEntry;
96
74
  const segmentId = `${shortCode}D${i}.${loader.$$id}`;
97
75
  return {
@@ -103,40 +81,63 @@ export async function resolveLoaders<TEnv>(
103
81
  params: ctx.params,
104
82
  loaderId: loader.$$id,
105
83
  loaderData: deps.wrapLoaderPromise(
106
- resolveLoaderData(loaderEntry, ctx, ctx.pathname),
84
+ runInsideLoaderScope(() =>
85
+ resolveLoaderData(loaderEntry, ctx, ctx.pathname),
86
+ ),
107
87
  entry,
108
88
  segmentId,
109
89
  ctx.pathname,
90
+ errorContext,
110
91
  ),
111
92
  belongsToRoute,
112
93
  };
113
94
  });
95
+
96
+ return segments;
114
97
  }
115
98
 
116
99
  // Loading disabled: still start all loaders in parallel, but only emit
117
100
  // settled promises so handlers don't stream loading placeholders.
118
- const pendingLoaderData = loaderEntries.map((loaderEntry) =>
119
- resolveLoaderData(loaderEntry, ctx, ctx.pathname),
120
- );
121
- await Promise.all(pendingLoaderData);
101
+ //
102
+ // Wrap each loader promise with wrapLoaderPromise BEFORE awaiting. The wrapped
103
+ // promise resolves to a LoaderDataResult and never rejects, routing a failed
104
+ // loader to its own per-loader error boundary. Awaiting the RAW promises here
105
+ // instead would (1) propagate a rejection to the segment-level boundary,
106
+ // collapsing the whole entry and discarding successful sibling data, and
107
+ // (2) leave the other in-flight raw promises without a .catch, producing
108
+ // unhandled rejections. Mirrors the loading path and intercept-resolution.
109
+ const pendingLoaderData = loaderEntries.map((loaderEntry, i) => {
110
+ const { loader } = loaderEntry;
111
+ const segmentId = `${shortCode}D${i}.${loader.$$id}`;
112
+ const wrapped = deps.wrapLoaderPromise(
113
+ runInsideLoaderScope(() =>
114
+ resolveLoaderData(loaderEntry, ctx, ctx.pathname),
115
+ ),
116
+ entry,
117
+ segmentId,
118
+ ctx.pathname,
119
+ errorContext,
120
+ );
121
+ return { wrapped, segmentId };
122
+ });
123
+ await Promise.all(pendingLoaderData.map((p) => p.wrapped));
122
124
 
123
125
  return loaderEntries.map((loaderEntry, i) => {
124
126
  const { loader } = loaderEntry;
125
- const segmentId = `${shortCode}D${i}.${loader.$$id}`;
127
+ const pending = pendingLoaderData[i]!;
128
+ // The "loader:<id>" perf metric is recorded by observePhase at the single
129
+ // loader-metering site (useLoader, reached via ctx.use during
130
+ // resolveLoaderData), with the real per-loader duration rather than a
131
+ // Promise.all batch ceiling.
126
132
  return {
127
- id: segmentId,
133
+ id: pending.segmentId,
128
134
  namespace: entry.id,
129
135
  type: "loader" as const,
130
136
  index: i,
131
137
  component: null,
132
138
  params: ctx.params,
133
139
  loaderId: loader.$$id,
134
- loaderData: deps.wrapLoaderPromise(
135
- pendingLoaderData[i]!,
136
- entry,
137
- segmentId,
138
- ctx.pathname,
139
- ),
140
+ loaderData: pending.wrapped,
140
141
  belongsToRoute,
141
142
  };
142
143
  });
@@ -148,6 +149,15 @@ export async function resolveLoaders<TEnv>(
148
149
  export interface ResolveSegmentOptions {
149
150
  /** When true, skip resolveLoaders() calls (used for pre-rendering) */
150
151
  skipLoaders?: boolean;
152
+ /**
153
+ * When true, a thrown render error is re-thrown instead of being converted
154
+ * into an error-boundary segment. Set only by the pre-render path so a
155
+ * build-time render failure (and a `throw new Skip()` inside a render fn)
156
+ * surfaces to the build instead of being silently baked into a frozen error
157
+ * page served as a 200 (issue #587). The live request path leaves this unset,
158
+ * so error boundaries keep catching at request time.
159
+ */
160
+ throwOnError?: boolean;
151
161
  }
152
162
 
153
163
  /**
@@ -178,7 +188,9 @@ export async function resolveSegment<TEnv>(
178
188
  (context as InternalHandlerContext<any, TEnv>)._currentSegmentId =
179
189
  entry.shortCode;
180
190
 
191
+ const doneLayoutHandler = track(`handler:${entry.id}`, 2);
181
192
  const component = await resolveLayoutComponent(entry, context);
193
+ doneLayoutHandler();
182
194
 
183
195
  segments.push({
184
196
  id: entry.shortCode,
@@ -187,14 +199,20 @@ export async function resolveSegment<TEnv>(
187
199
  index: 0,
188
200
  component,
189
201
  loading: entry.loading === false ? null : entry.loading,
190
- transition: entry.transition,
202
+ transition: applyViewTransitionDefault(
203
+ entry.transition,
204
+ deps.viewTransitionDefault,
205
+ ),
191
206
  params,
192
207
  belongsToRoute: false,
193
208
  layoutName: entry.id,
194
209
  ...(entry.mountPath ? { mountPath: entry.mountPath } : {}),
195
210
  });
196
211
 
197
- for (const parallelEntry of entry.parallel) {
212
+ const resolvedParallelEntries = new Set<string>();
213
+ for (const { slot, entry: parallelEntry } of getParallelSlotEntries(
214
+ entry.parallel,
215
+ )) {
198
216
  const parallelSegments = await resolveParallelEntry(
199
217
  parallelEntry,
200
218
  params,
@@ -204,8 +222,11 @@ export async function resolveSegment<TEnv>(
204
222
  deps,
205
223
  options,
206
224
  routeKey,
225
+ [slot],
226
+ !resolvedParallelEntries.has(parallelEntry.id),
207
227
  );
208
228
  segments.push(...parallelSegments);
229
+ resolvedParallelEntries.add(parallelEntry.id);
209
230
  }
210
231
 
211
232
  for (const orphan of entry.layout) {
@@ -241,9 +262,19 @@ export async function resolveSegment<TEnv>(
241
262
  entry.shortCode,
242
263
  );
243
264
  if (component === undefined) {
265
+ // For Passthrough routes at runtime, use the live handler instead of
266
+ // the build handler. At build time (context.build === true), always
267
+ // use the build handler from entry.handler.
268
+ const handler =
269
+ !context.build && entry.liveHandler ? entry.liveHandler : entry.handler;
270
+ const doneRouteHandler = track(`handler:${entry.id}`, 2);
244
271
  if (entry.loading) {
245
- const result = handleHandlerResult(entry.handler(context));
272
+ const result = handleHandlerResult(
273
+ observeHandler(entry.id, handler, context),
274
+ );
246
275
  if (result instanceof Promise) {
276
+ warnOnStreamedResponse(result, entry.id);
277
+ result.finally(doneRouteHandler).catch(() => {});
247
278
  const tracked = deps.trackHandler(result, {
248
279
  segmentId: entry.shortCode,
249
280
  segmentType: entry.type,
@@ -258,10 +289,14 @@ export async function resolveSegment<TEnv>(
258
289
  );
259
290
  component = tracked;
260
291
  } else {
292
+ doneRouteHandler();
261
293
  component = result;
262
294
  }
263
295
  } else {
264
- component = handleHandlerResult(await entry.handler(context));
296
+ component = handleHandlerResult(
297
+ await observeHandler(entry.id, handler, context),
298
+ );
299
+ doneRouteHandler();
265
300
  }
266
301
  }
267
302
 
@@ -275,11 +310,15 @@ export async function resolveSegment<TEnv>(
275
310
  deps,
276
311
  options,
277
312
  routeKey,
313
+ entry,
278
314
  );
279
315
  segments.push(...orphanSegments);
280
316
  }
281
317
 
282
- for (const parallelEntry of entry.parallel) {
318
+ const resolvedParallelEntries = new Set<string>();
319
+ for (const { slot, entry: parallelEntry } of getParallelSlotEntries(
320
+ entry.parallel,
321
+ )) {
283
322
  const parallelSegments = await resolveParallelEntry(
284
323
  parallelEntry,
285
324
  params,
@@ -289,8 +328,11 @@ export async function resolveSegment<TEnv>(
289
328
  deps,
290
329
  options,
291
330
  routeKey,
331
+ [slot],
332
+ !resolvedParallelEntries.has(parallelEntry.id),
292
333
  );
293
334
  segments.push(...parallelSegments);
335
+ resolvedParallelEntries.add(parallelEntry.id);
294
336
  }
295
337
 
296
338
  segments.push({
@@ -298,9 +340,12 @@ export async function resolveSegment<TEnv>(
298
340
  namespace: entry.id,
299
341
  type: "route",
300
342
  index: 0,
301
- component,
343
+ component: component ?? null,
302
344
  loading: entry.loading === false ? null : entry.loading,
303
- transition: entry.transition,
345
+ transition: applyViewTransitionDefault(
346
+ entry.transition,
347
+ deps.viewTransitionDefault,
348
+ ),
304
349
  params,
305
350
  belongsToRoute: true,
306
351
  ...(entry.mountPath ? { mountPath: entry.mountPath } : {}),
@@ -324,6 +369,9 @@ export async function resolveOrphanLayout<TEnv>(
324
369
  deps: SegmentResolutionDeps<TEnv>,
325
370
  options?: ResolveSegmentOptions,
326
371
  routeKey?: string,
372
+ /** Parent route entry — its loaders are inherited by the layout so
373
+ * parallel slots inside this layout can access them via useLoader(). */
374
+ parentRouteEntry?: EntryData,
327
375
  ): Promise<ResolvedSegment[]> {
328
376
  invariant(
329
377
  orphan.type === "layout" || orphan.type === "cache",
@@ -339,11 +387,37 @@ export async function resolveOrphanLayout<TEnv>(
339
387
  deps,
340
388
  );
341
389
  segments.push(...loaderSegments);
390
+
391
+ // Inherit parent route's loaders so parallel slots inside this layout
392
+ // can access them via useLoader(). Without this, the route's loaders
393
+ // are only in the route's OutletProvider (rendered as <Outlet /> content),
394
+ // which is a child — not a parent — of the layout's context.
395
+ if (
396
+ parentRouteEntry &&
397
+ parentRouteEntry.loader &&
398
+ parentRouteEntry.loader.length > 0 &&
399
+ Object.keys(orphan.parallel).length > 0
400
+ ) {
401
+ const inheritedLoaders = await resolveLoaders(
402
+ parentRouteEntry,
403
+ context,
404
+ belongsToRoute,
405
+ deps,
406
+ orphan.shortCode,
407
+ );
408
+ // Tag as inherited so buildMatchResult can deduplicate when safe
409
+ for (const s of inheritedLoaders) {
410
+ s._inherited = true;
411
+ }
412
+ segments.push(...inheritedLoaders);
413
+ }
342
414
  }
343
415
 
344
416
  // Handler-first: orphan layout handler executes before its parallels
345
417
  // so that ctx.set() values are visible to parallel children.
418
+ const doneOrphanHandler = track(`handler:${orphan.id}`, 2);
346
419
  const component = await resolveLayoutComponent(orphan, context);
420
+ doneOrphanHandler();
347
421
 
348
422
  segments.push({
349
423
  id: orphan.shortCode,
@@ -355,11 +429,17 @@ export async function resolveOrphanLayout<TEnv>(
355
429
  belongsToRoute,
356
430
  layoutName: orphan.id,
357
431
  loading: orphan.loading === false ? null : orphan.loading,
358
- transition: orphan.transition,
432
+ transition: applyViewTransitionDefault(
433
+ orphan.transition,
434
+ deps.viewTransitionDefault,
435
+ ),
359
436
  ...(orphan.mountPath ? { mountPath: orphan.mountPath } : {}),
360
437
  });
361
438
 
362
- for (const parallelEntry of orphan.parallel) {
439
+ const resolvedParallelEntries = new Set<string>();
440
+ for (const { slot, entry: parallelEntry } of getParallelSlotEntries(
441
+ orphan.parallel,
442
+ )) {
363
443
  const parallelSegments = await resolveParallelEntry(
364
444
  parallelEntry,
365
445
  params,
@@ -369,8 +449,11 @@ export async function resolveOrphanLayout<TEnv>(
369
449
  deps,
370
450
  options,
371
451
  routeKey,
452
+ [slot],
453
+ !resolvedParallelEntries.has(parallelEntry.id),
372
454
  );
373
455
  segments.push(...parallelSegments);
456
+ resolvedParallelEntries.add(parallelEntry.id);
374
457
  }
375
458
 
376
459
  return segments;
@@ -388,6 +471,8 @@ export async function resolveParallelEntry<TEnv>(
388
471
  deps: SegmentResolutionDeps<TEnv>,
389
472
  options?: ResolveSegmentOptions,
390
473
  routeKey?: string,
474
+ slotNames?: `@${string}`[],
475
+ includeLoaders: boolean = true,
391
476
  ): Promise<ResolvedSegment[]> {
392
477
  invariant(
393
478
  parallelEntry.type === "parallel",
@@ -402,7 +487,12 @@ export async function resolveParallelEntry<TEnv>(
402
487
  | ReactNode
403
488
  >;
404
489
 
405
- for (const [slot, handler] of Object.entries(slots)) {
490
+ const slotsToResolve = slotNames ?? (Object.keys(slots) as `@${string}`[]);
491
+
492
+ for (const slot of slotsToResolve) {
493
+ // Try static lookup first — in production, handler bodies are evicted
494
+ // and replaced with stubs that have no .handler property (undefined).
495
+ // The static store holds the pre-rendered component for these slots.
406
496
  let component: ReactNode | undefined = await tryStaticSlot(
407
497
  parallelEntry,
408
498
  slot,
@@ -410,12 +500,31 @@ export async function resolveParallelEntry<TEnv>(
410
500
  );
411
501
 
412
502
  if (component === undefined) {
503
+ const handler = slots[slot];
504
+ if (handler === undefined) {
505
+ continue;
506
+ }
507
+ // Pin `_currentSegmentId` to the slot's own id so handle pushes from
508
+ // inside the slot handler get their own bucket in the HandleStore.
509
+ // Parent-keying would collapse them into the parent layout's bucket;
510
+ // the partial-update merge then replaces the parent's bucket on a
511
+ // slot-only revalidation and drops layout-pushed Meta/Breadcrumbs.
512
+ // filterSegmentOrder() retains slot ids so the client preserves them.
513
+ (context as InternalHandlerContext<any, TEnv>)._currentSegmentId =
514
+ `${parentShortCode}.${slot}`;
515
+ const doneParallelHandler = track(
516
+ `handler:${parallelEntry.id}.${slot}`,
517
+ 2,
518
+ );
413
519
  const hasLoadingFallback =
414
520
  parallelEntry.loading !== undefined && parallelEntry.loading !== false;
415
521
  if (hasLoadingFallback) {
416
522
  const result =
417
- typeof handler === "function" ? handler(context) : handler;
523
+ typeof handler === "function"
524
+ ? observeHandler(`${parallelEntry.id}.${slot}`, handler, context)
525
+ : handler;
418
526
  if (result instanceof Promise) {
527
+ result.finally(doneParallelHandler).catch(() => {});
419
528
  const tracked = deps.trackHandler(result, {
420
529
  segmentId: `${parentShortCode}.${slot}`,
421
530
  segmentType: "parallel",
@@ -430,11 +539,19 @@ export async function resolveParallelEntry<TEnv>(
430
539
  );
431
540
  component = tracked as ReactNode;
432
541
  } else {
542
+ doneParallelHandler();
433
543
  component = result as ReactNode;
434
544
  }
435
545
  } else {
436
546
  component =
437
- typeof handler === "function" ? await handler(context) : handler;
547
+ typeof handler === "function"
548
+ ? await observeHandler(
549
+ `${parallelEntry.id}.${slot}`,
550
+ handler,
551
+ context,
552
+ )
553
+ : handler;
554
+ doneParallelHandler();
438
555
  }
439
556
  }
440
557
 
@@ -445,7 +562,10 @@ export async function resolveParallelEntry<TEnv>(
445
562
  index: 0,
446
563
  component,
447
564
  loading: parallelEntry.loading === false ? null : parallelEntry.loading,
448
- transition: parallelEntry.transition,
565
+ transition: applyViewTransitionDefault(
566
+ parallelEntry.transition,
567
+ deps.viewTransitionDefault,
568
+ ),
449
569
  params,
450
570
  slot,
451
571
  belongsToRoute,
@@ -456,7 +576,7 @@ export async function resolveParallelEntry<TEnv>(
456
576
  });
457
577
  }
458
578
 
459
- if (!parallelEntry.loading && !options?.skipLoaders) {
579
+ if (!options?.skipLoaders && includeLoaders) {
460
580
  const loaderSegments = await resolveLoaders(
461
581
  parallelEntry,
462
582
  context,
@@ -464,6 +584,15 @@ export async function resolveParallelEntry<TEnv>(
464
584
  deps,
465
585
  parentShortCode,
466
586
  );
587
+ // Tag parallel-owned loaders so renderSegments can stream them
588
+ // using the parallel's loading() instead of awaiting on the layout
589
+ const parallelLoading =
590
+ parallelEntry.loading === false ? undefined : parallelEntry.loading;
591
+ if (parallelLoading) {
592
+ for (const seg of loaderSegments) {
593
+ seg.parallelLoading = parallelLoading;
594
+ }
595
+ }
467
596
  segments.push(...loaderSegments);
468
597
  }
469
598
 
@@ -499,6 +628,14 @@ export async function resolveAllSegments<TEnv>(
499
628
  } catch {}
500
629
 
501
630
  for (const entry of entries) {
631
+ // Set ALS flag when entering a cache() boundary so that ctx.get()
632
+ // can guard non-cacheable variable reads. Also guards response-level
633
+ // side effects (headers.set). Persists for all descendant entries.
634
+ if (entry.type === "cache") {
635
+ const store = RangoContext.getStore();
636
+ if (store) store.insideCacheScope = true;
637
+ }
638
+ const doneEntry = track(`segment:${entry.id}`, 1);
502
639
  const resolvedSegments = await resolveWithErrorBoundary(
503
640
  entry,
504
641
  params,
@@ -517,7 +654,9 @@ export async function resolveAllSegments<TEnv>(
517
654
  deps,
518
655
  { request: safeRequest, url: context.url, routeKey, telemetry },
519
656
  context.pathname,
657
+ options?.throwOnError,
520
658
  );
659
+ doneEntry();
521
660
  // Deduplicate by segment ID. include() scopes can produce entries that
522
661
  // resolve the same shared layout/loader segment. Duplicates in the segment
523
662
  // array propagate to the client's matched[] and change the React tree depth.
@@ -541,11 +680,77 @@ export async function resolveLoadersOnly<TEnv>(
541
680
  deps: SegmentResolutionDeps<TEnv>,
542
681
  ): Promise<ResolvedSegment[]> {
543
682
  const loaderSegments: ResolvedSegment[] = [];
683
+ const seenIds = new Set<string>();
684
+
685
+ async function collectEntryLoaders(
686
+ entry: EntryData,
687
+ belongsToRoute: boolean,
688
+ shortCodeOverride?: string,
689
+ ): Promise<void> {
690
+ // Skip if all loaders from this entry have already been resolved
691
+ // via a parent (e.g., cache boundary wrapping a layout with shared loaders).
692
+ const entryLoaders = entry.loader ?? [];
693
+ const sc = shortCodeOverride ?? entry.shortCode;
694
+ const allAlreadySeen =
695
+ entryLoaders.length > 0 &&
696
+ entryLoaders.every((le, i) =>
697
+ seenIds.has(`${sc}D${i}.${le.loader.$$id}`),
698
+ );
699
+ if (!allAlreadySeen) {
700
+ const segments = await resolveLoaders(
701
+ entry,
702
+ context,
703
+ belongsToRoute,
704
+ deps,
705
+ shortCodeOverride,
706
+ );
707
+ for (const seg of segments) {
708
+ if (!seenIds.has(seg.id)) {
709
+ seenIds.add(seg.id);
710
+ loaderSegments.push(seg);
711
+ }
712
+ }
713
+ }
714
+
715
+ const seenParallelEntryIds = new Set<string>();
716
+ for (const parallelEntry of getParallelEntries(entry.parallel)) {
717
+ if (seenParallelEntryIds.has(parallelEntry.id)) continue;
718
+ seenParallelEntryIds.add(parallelEntry.id);
719
+ await collectEntryLoaders(parallelEntry, belongsToRoute, entry.shortCode);
720
+ }
721
+
722
+ const childBelongsToRoute = belongsToRoute || entry.type === "route";
723
+ for (const layoutEntry of entry.layout) {
724
+ await collectEntryLoaders(layoutEntry, childBelongsToRoute);
725
+ // Inherit route loaders for orphan layouts with parallels.
726
+ // Resolve directly — do NOT re-enter collectEntryLoaders with the
727
+ // route entry, as that would re-iterate route.layout and loop.
728
+ if (
729
+ entry.type === "route" &&
730
+ entry.loader &&
731
+ entry.loader.length > 0 &&
732
+ Object.keys(layoutEntry.parallel).length > 0
733
+ ) {
734
+ const inherited = await resolveLoaders(
735
+ entry,
736
+ context,
737
+ childBelongsToRoute,
738
+ deps,
739
+ layoutEntry.shortCode,
740
+ );
741
+ for (const seg of inherited) {
742
+ if (!seenIds.has(seg.id)) {
743
+ seenIds.add(seg.id);
744
+ seg._inherited = true;
745
+ loaderSegments.push(seg);
746
+ }
747
+ }
748
+ }
749
+ }
750
+ }
544
751
 
545
752
  for (const entry of entries) {
546
- const belongsToRoute = entry.type === "route";
547
- const segments = await resolveLoaders(entry, context, belongsToRoute, deps);
548
- loaderSegments.push(...segments);
753
+ await collectEntryLoaders(entry, entry.type === "route");
549
754
  }
550
755
 
551
756
  return loaderSegments;