@rangojs/router 0.0.0-experimental.eb0645d3 → 0.0.0-experimental.f1468e3c

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 (392) hide show
  1. package/AGENTS.md +8 -0
  2. package/README.md +126 -16
  3. package/dist/bin/rango.js +319 -95
  4. package/dist/testing/vitest.js +82 -0
  5. package/dist/vite/index.js +2724 -1053
  6. package/package.json +68 -14
  7. package/skills/api-client/SKILL.md +211 -0
  8. package/skills/breadcrumbs/SKILL.md +64 -2
  9. package/skills/bundle-analysis/SKILL.md +159 -0
  10. package/skills/cache-guide/SKILL.md +224 -32
  11. package/skills/caching/SKILL.md +279 -17
  12. package/skills/composability/SKILL.md +27 -3
  13. package/skills/css/SKILL.md +76 -0
  14. package/skills/debug-manifest/SKILL.md +4 -2
  15. package/skills/document-cache/SKILL.md +78 -55
  16. package/skills/handler-use/SKILL.md +11 -9
  17. package/skills/hooks/SKILL.md +243 -29
  18. package/skills/host-router/SKILL.md +83 -23
  19. package/skills/i18n/SKILL.md +276 -0
  20. package/skills/intercept/SKILL.md +68 -19
  21. package/skills/layout/SKILL.md +13 -9
  22. package/skills/links/SKILL.md +190 -23
  23. package/skills/loader/SKILL.md +235 -9
  24. package/skills/middleware/SKILL.md +18 -10
  25. package/skills/migrate-nextjs/SKILL.md +43 -19
  26. package/skills/migrate-react-router/SKILL.md +8 -2
  27. package/skills/mime-routes/SKILL.md +28 -1
  28. package/skills/observability/SKILL.md +172 -0
  29. package/skills/parallel/SKILL.md +18 -7
  30. package/skills/prerender/SKILL.md +65 -60
  31. package/skills/rango/SKILL.md +251 -24
  32. package/skills/react-compiler/SKILL.md +168 -0
  33. package/skills/response-routes/SKILL.md +115 -48
  34. package/skills/route/SKILL.md +46 -5
  35. package/skills/router-setup/SKILL.md +30 -8
  36. package/skills/scripts/SKILL.md +179 -0
  37. package/skills/server-actions/SKILL.md +775 -0
  38. package/skills/tailwind/SKILL.md +27 -3
  39. package/skills/testing/SKILL.md +130 -0
  40. package/skills/testing/bindings.md +103 -0
  41. package/skills/testing/cache-prerender.md +127 -0
  42. package/skills/testing/client-components.md +124 -0
  43. package/skills/testing/e2e-parity.md +125 -0
  44. package/skills/testing/flight.md +91 -0
  45. package/skills/testing/handles.md +129 -0
  46. package/skills/testing/loader.md +128 -0
  47. package/skills/testing/middleware.md +99 -0
  48. package/skills/testing/render-handler.md +122 -0
  49. package/skills/testing/response-routes.md +95 -0
  50. package/skills/testing/reverse-and-types.md +84 -0
  51. package/skills/testing/server-actions.md +107 -0
  52. package/skills/testing/server-tree.md +128 -0
  53. package/skills/testing/setup.md +123 -0
  54. package/skills/typesafety/SKILL.md +322 -29
  55. package/skills/use-cache/SKILL.md +57 -14
  56. package/skills/view-transitions/SKILL.md +337 -0
  57. package/src/__augment-tests__/augment.ts +81 -0
  58. package/src/__augment-tests__/augmented.check.ts +116 -0
  59. package/src/__internal.ts +0 -65
  60. package/src/browser/action-coordinator.ts +53 -36
  61. package/src/browser/action-fence.ts +47 -0
  62. package/src/browser/app-shell.ts +39 -0
  63. package/src/browser/connection-warmup.ts +134 -0
  64. package/src/browser/cookie-name.ts +140 -0
  65. package/src/browser/event-controller.ts +192 -150
  66. package/src/browser/history-state.ts +21 -0
  67. package/src/browser/index.ts +3 -3
  68. package/src/browser/invalidate-client-cache.ts +52 -0
  69. package/src/browser/navigation-bridge.ts +94 -25
  70. package/src/browser/navigation-client.ts +121 -84
  71. package/src/browser/navigation-store-handle.ts +38 -0
  72. package/src/browser/navigation-store.ts +115 -67
  73. package/src/browser/navigation-transaction.ts +9 -59
  74. package/src/browser/network-error-handler.ts +34 -7
  75. package/src/browser/partial-update.ts +147 -128
  76. package/src/browser/prefetch/cache.ts +107 -56
  77. package/src/browser/prefetch/fetch.ts +204 -34
  78. package/src/browser/prefetch/queue.ts +6 -3
  79. package/src/browser/rango-state.ts +158 -76
  80. package/src/browser/react/Link.tsx +30 -7
  81. package/src/browser/react/NavigationProvider.tsx +283 -118
  82. package/src/browser/react/ScrollRestoration.tsx +10 -6
  83. package/src/browser/react/deferred-handle-resolution.ts +75 -0
  84. package/src/browser/react/filter-segment-order.ts +66 -7
  85. package/src/browser/react/index.ts +0 -48
  86. package/src/browser/react/location-state-shared.ts +178 -8
  87. package/src/browser/react/location-state.ts +39 -14
  88. package/src/browser/react/use-action.ts +6 -15
  89. package/src/browser/react/use-handle.ts +17 -14
  90. package/src/browser/react/use-href.tsx +8 -1
  91. package/src/browser/react/use-link-status.ts +33 -8
  92. package/src/browser/react/use-navigation.ts +10 -5
  93. package/src/browser/react/use-params.ts +11 -11
  94. package/src/browser/react/use-reverse.ts +106 -0
  95. package/src/browser/react/use-router.ts +25 -3
  96. package/src/browser/react/use-search-params.ts +0 -5
  97. package/src/browser/react/use-segments.ts +11 -21
  98. package/src/browser/response-adapter.ts +99 -8
  99. package/src/browser/rsc-router.tsx +91 -24
  100. package/src/browser/scroll-restoration.ts +30 -17
  101. package/src/browser/segment-structure-assert.ts +2 -2
  102. package/src/browser/server-action-bridge.ts +214 -55
  103. package/src/browser/types.ts +80 -9
  104. package/src/browser/validate-redirect-origin.ts +43 -16
  105. package/src/build/collect-fallback-refs.ts +107 -0
  106. package/src/build/generate-manifest.ts +60 -35
  107. package/src/build/generate-route-types.ts +2 -1
  108. package/src/build/index.ts +8 -2
  109. package/src/build/prefix-tree-utils.ts +123 -0
  110. package/src/build/route-trie.ts +117 -14
  111. package/src/build/route-types/ast-route-extraction.ts +15 -8
  112. package/src/build/route-types/codegen.ts +16 -5
  113. package/src/build/route-types/include-resolution.ts +117 -23
  114. package/src/build/route-types/param-extraction.ts +6 -3
  115. package/src/build/route-types/per-module-writer.ts +22 -6
  116. package/src/build/route-types/router-processing.ts +55 -28
  117. package/src/build/route-types/scan-filter.ts +1 -1
  118. package/src/build/route-types/source-scan.ts +216 -0
  119. package/src/build/runtime-discovery.ts +9 -20
  120. package/src/cache/cache-error.ts +104 -0
  121. package/src/cache/cache-key-utils.ts +29 -13
  122. package/src/cache/cache-policy.ts +108 -34
  123. package/src/cache/cache-runtime.ts +224 -41
  124. package/src/cache/cache-scope.ts +188 -82
  125. package/src/cache/cache-tag.ts +103 -0
  126. package/src/cache/cf/cf-base64.ts +33 -0
  127. package/src/cache/cf/cf-cache-constants.ts +127 -0
  128. package/src/cache/cf/cf-cache-store.ts +1989 -378
  129. package/src/cache/cf/cf-cache-types.ts +349 -0
  130. package/src/cache/cf/cf-kv-utils.ts +46 -0
  131. package/src/cache/cf/cf-tag-marker-memo.ts +105 -0
  132. package/src/cache/cf/index.ts +6 -16
  133. package/src/cache/document-cache.ts +89 -21
  134. package/src/cache/handle-snapshot.ts +70 -0
  135. package/src/cache/index.ts +10 -20
  136. package/src/cache/memory-segment-store.ts +136 -37
  137. package/src/cache/profile-registry.ts +46 -31
  138. package/src/cache/read-through-swr.ts +56 -12
  139. package/src/cache/segment-codec.ts +9 -17
  140. package/src/cache/tag-invalidation.ts +230 -0
  141. package/src/cache/types.ts +37 -100
  142. package/src/client.rsc.tsx +44 -21
  143. package/src/client.tsx +36 -61
  144. package/src/cloudflare/index.ts +11 -0
  145. package/src/cloudflare/tracing.ts +109 -0
  146. package/src/component-utils.ts +19 -0
  147. package/src/components/DefaultDocument.tsx +8 -2
  148. package/src/context-var.ts +18 -6
  149. package/src/decode-loader-results.ts +52 -0
  150. package/src/defer.ts +196 -0
  151. package/src/deps/ssr.ts +0 -1
  152. package/src/encode-kv.ts +49 -0
  153. package/src/errors.ts +30 -4
  154. package/src/escape-script.ts +52 -0
  155. package/src/handle.ts +31 -23
  156. package/src/handles/MetaTags.tsx +62 -19
  157. package/src/handles/Scripts.tsx +183 -0
  158. package/src/handles/breadcrumbs.ts +37 -8
  159. package/src/handles/is-thenable.ts +19 -0
  160. package/src/handles/meta.ts +51 -40
  161. package/src/handles/script.ts +244 -0
  162. package/src/host/cookie-handler.ts +9 -60
  163. package/src/host/errors.ts +0 -24
  164. package/src/host/index.ts +8 -2
  165. package/src/host/pattern-matcher.ts +23 -52
  166. package/src/host/router.ts +107 -99
  167. package/src/host/testing.ts +40 -27
  168. package/src/host/types.ts +37 -4
  169. package/src/host/utils.ts +1 -1
  170. package/src/href-client.ts +137 -22
  171. package/src/index.rsc.ts +96 -12
  172. package/src/index.ts +94 -14
  173. package/src/internal-debug.ts +11 -10
  174. package/src/loader-store.ts +500 -0
  175. package/src/loader.rsc.ts +20 -13
  176. package/src/loader.ts +12 -11
  177. package/src/missing-id-error.ts +68 -0
  178. package/src/outlet-context.ts +1 -1
  179. package/src/outlet-provider.tsx +1 -5
  180. package/src/prerender/param-hash.ts +16 -16
  181. package/src/prerender/store.ts +32 -37
  182. package/src/prerender.ts +61 -6
  183. package/src/redirect-origin.ts +100 -0
  184. package/src/regex-escape.ts +8 -0
  185. package/src/render-error-thrower.tsx +20 -0
  186. package/src/response-utils.ts +34 -0
  187. package/src/reverse.ts +65 -40
  188. package/src/root-error-boundary.tsx +1 -19
  189. package/src/route-content-wrapper.tsx +19 -77
  190. package/src/route-definition/dsl-helpers.ts +304 -309
  191. package/src/route-definition/helper-factories.ts +28 -140
  192. package/src/route-definition/helpers-types.ts +82 -55
  193. package/src/route-definition/index.ts +1 -2
  194. package/src/route-definition/redirect.ts +44 -11
  195. package/src/route-definition/resolve-handler-use.ts +12 -1
  196. package/src/route-definition/use-item-types.ts +29 -0
  197. package/src/route-map-builder.ts +0 -16
  198. package/src/route-types.ts +19 -46
  199. package/src/router/basename.ts +14 -0
  200. package/src/router/content-negotiation.ts +73 -25
  201. package/src/router/error-handling.ts +45 -18
  202. package/src/router/find-match.ts +44 -23
  203. package/src/router/handler-context.ts +27 -43
  204. package/src/router/instrument.ts +350 -0
  205. package/src/router/intercept-resolution.ts +39 -20
  206. package/src/router/lazy-includes.ts +10 -47
  207. package/src/router/loader-resolution.ts +155 -72
  208. package/src/router/logging.ts +0 -6
  209. package/src/router/manifest.ts +18 -29
  210. package/src/router/match-api.ts +9 -24
  211. package/src/router/match-context.ts +0 -22
  212. package/src/router/match-handlers.ts +58 -58
  213. package/src/router/match-middleware/background-revalidation.ts +40 -24
  214. package/src/router/match-middleware/cache-lookup.ts +159 -285
  215. package/src/router/match-middleware/cache-store.ts +64 -52
  216. package/src/router/match-middleware/intercept-resolution.ts +0 -22
  217. package/src/router/match-middleware/segment-resolution.ts +0 -22
  218. package/src/router/match-pipelines.ts +1 -42
  219. package/src/router/match-result.ts +44 -74
  220. package/src/router/metrics.ts +0 -34
  221. package/src/router/middleware-types.ts +7 -134
  222. package/src/router/middleware.ts +247 -166
  223. package/src/router/navigation-snapshot.ts +0 -51
  224. package/src/router/params-util.ts +23 -0
  225. package/src/router/pattern-matching.ts +85 -94
  226. package/src/router/prefetch-cache-ttl.ts +51 -0
  227. package/src/router/prerender-match.ts +104 -65
  228. package/src/router/preview-match.ts +3 -1
  229. package/src/router/request-classification.ts +28 -62
  230. package/src/router/revalidation.ts +123 -73
  231. package/src/router/route-snapshot.ts +0 -1
  232. package/src/router/router-context.ts +3 -28
  233. package/src/router/router-interfaces.ts +83 -35
  234. package/src/router/router-options.ts +136 -5
  235. package/src/router/router-registry.ts +2 -5
  236. package/src/router/segment-resolution/fresh.ts +97 -84
  237. package/src/router/segment-resolution/helpers.ts +86 -6
  238. package/src/router/segment-resolution/loader-cache.ts +76 -39
  239. package/src/router/segment-resolution/revalidation.ts +272 -320
  240. package/src/router/segment-resolution/static-store.ts +19 -5
  241. package/src/router/segment-resolution/streamed-handler-telemetry.ts +52 -0
  242. package/src/router/segment-resolution/view-transition-default.ts +56 -0
  243. package/src/router/segment-resolution.ts +5 -1
  244. package/src/router/segment-wrappers.ts +6 -5
  245. package/src/router/state-cookie-name.ts +33 -0
  246. package/src/router/substitute-pattern-params.ts +56 -0
  247. package/src/router/telemetry-otel.ts +161 -199
  248. package/src/router/telemetry.ts +96 -19
  249. package/src/router/timeout.ts +0 -20
  250. package/src/router/tracing.ts +206 -0
  251. package/src/router/trie-matching.ts +162 -64
  252. package/src/router/types.ts +9 -63
  253. package/src/router/url-params.ts +0 -5
  254. package/src/router.ts +110 -55
  255. package/src/rsc/handler-context.ts +3 -2
  256. package/src/rsc/handler.ts +264 -220
  257. package/src/rsc/helpers.ts +100 -6
  258. package/src/rsc/index.ts +2 -5
  259. package/src/rsc/json-route-result.ts +38 -0
  260. package/src/rsc/loader-fetch.ts +114 -38
  261. package/src/rsc/manifest-init.ts +28 -41
  262. package/src/rsc/origin-guard.ts +39 -25
  263. package/src/rsc/progressive-enhancement.ts +117 -11
  264. package/src/rsc/redirect-guard.ts +99 -0
  265. package/src/rsc/response-cache-serve.ts +238 -0
  266. package/src/rsc/response-error.ts +79 -12
  267. package/src/rsc/response-route-handler.ts +88 -188
  268. package/src/rsc/rsc-rendering.ts +98 -76
  269. package/src/rsc/runtime-warnings.ts +23 -10
  270. package/src/rsc/server-action.ts +281 -117
  271. package/src/rsc/ssr-setup.ts +16 -0
  272. package/src/rsc/transition-gate.ts +89 -0
  273. package/src/rsc/types.ts +23 -5
  274. package/src/runtime-env.ts +18 -0
  275. package/src/search-params.ts +35 -30
  276. package/src/segment-loader-promise.ts +31 -4
  277. package/src/segment-system.tsx +254 -143
  278. package/src/serialize.ts +243 -0
  279. package/src/server/context.ts +163 -51
  280. package/src/server/cookie-parse.ts +32 -0
  281. package/src/server/cookie-store.ts +80 -5
  282. package/src/server/handle-store.ts +21 -38
  283. package/src/server/loader-registry.ts +33 -42
  284. package/src/server/request-context.ts +287 -178
  285. package/src/ssr/index.tsx +21 -16
  286. package/src/static-handler.ts +10 -13
  287. package/src/testing/cache-status.ts +162 -0
  288. package/src/testing/collect-handle.ts +40 -0
  289. package/src/testing/dispatch.ts +701 -0
  290. package/src/testing/dom.entry.ts +22 -0
  291. package/src/testing/e2e/fixture.ts +188 -0
  292. package/src/testing/e2e/index.ts +128 -0
  293. package/src/testing/e2e/matchers.ts +35 -0
  294. package/src/testing/e2e/page-helpers.ts +272 -0
  295. package/src/testing/e2e/parity.ts +387 -0
  296. package/src/testing/e2e/server.ts +195 -0
  297. package/src/testing/flight-matchers.ts +97 -0
  298. package/src/testing/flight-normalize.ts +11 -0
  299. package/src/testing/flight-runtime.d.ts +57 -0
  300. package/src/testing/flight-tree.ts +682 -0
  301. package/src/testing/flight.entry.ts +52 -0
  302. package/src/testing/flight.ts +257 -0
  303. package/src/testing/generated-routes.ts +183 -0
  304. package/src/testing/index.ts +105 -0
  305. package/src/testing/internal/context.ts +371 -0
  306. package/src/testing/internal/flight-client-globals.ts +30 -0
  307. package/src/testing/internal/seed-vars.ts +54 -0
  308. package/src/testing/render-handler.ts +357 -0
  309. package/src/testing/render-route.tsx +581 -0
  310. package/src/testing/run-loader.ts +385 -0
  311. package/src/testing/run-middleware.ts +205 -0
  312. package/src/testing/run-transition-when.ts +164 -0
  313. package/src/testing/vitest-stubs/cloudflare-email.ts +9 -0
  314. package/src/testing/vitest-stubs/cloudflare-workers.ts +21 -0
  315. package/src/testing/vitest-stubs/plugin-rsc.ts +16 -0
  316. package/src/testing/vitest-stubs/version.ts +5 -0
  317. package/src/testing/vitest.ts +305 -0
  318. package/src/theme/ThemeProvider.tsx +20 -58
  319. package/src/theme/ThemeScript.tsx +7 -9
  320. package/src/theme/constants.ts +52 -13
  321. package/src/theme/index.ts +0 -7
  322. package/src/theme/theme-context.ts +1 -5
  323. package/src/theme/theme-script.ts +22 -21
  324. package/src/theme/use-theme.ts +0 -3
  325. package/src/types/boundaries.ts +0 -35
  326. package/src/types/cache-types.ts +13 -4
  327. package/src/types/error-types.ts +30 -90
  328. package/src/types/global-namespace.ts +54 -41
  329. package/src/types/handler-context.ts +110 -62
  330. package/src/types/index.ts +3 -10
  331. package/src/types/loader-types.ts +11 -9
  332. package/src/types/request-scope.ts +112 -0
  333. package/src/types/route-config.ts +6 -50
  334. package/src/types/route-entry.ts +0 -6
  335. package/src/types/segments.ts +135 -14
  336. package/src/urls/include-helper.ts +9 -56
  337. package/src/urls/index.ts +1 -11
  338. package/src/urls/path-helper-types.ts +29 -12
  339. package/src/urls/path-helper.ts +17 -106
  340. package/src/urls/pattern-types.ts +36 -19
  341. package/src/urls/response-types.ts +22 -29
  342. package/src/urls/type-extraction.ts +58 -139
  343. package/src/urls/urls-function.ts +1 -19
  344. package/src/use-loader.tsx +292 -107
  345. package/src/vite/debug.ts +185 -0
  346. package/src/vite/discovery/bundle-postprocess.ts +8 -7
  347. package/src/vite/discovery/discover-routers.ts +126 -85
  348. package/src/vite/discovery/discovery-errors.ts +194 -0
  349. package/src/vite/discovery/gate-state.ts +171 -0
  350. package/src/vite/discovery/prerender-collection.ts +96 -68
  351. package/src/vite/discovery/route-types-writer.ts +40 -84
  352. package/src/vite/discovery/self-gen-tracking.ts +27 -1
  353. package/src/vite/discovery/state.ts +44 -0
  354. package/src/vite/discovery/virtual-module-codegen.ts +14 -34
  355. package/src/vite/index.ts +2 -0
  356. package/src/vite/inject-client-debug.ts +36 -0
  357. package/src/vite/plugin-types.ts +126 -8
  358. package/src/vite/plugins/cjs-to-esm.ts +16 -19
  359. package/src/vite/plugins/client-ref-dedup.ts +16 -11
  360. package/src/vite/plugins/client-ref-hashing.ts +28 -15
  361. package/src/vite/plugins/cloudflare-protocol-stub.ts +1 -21
  362. package/src/vite/plugins/expose-action-id.ts +48 -95
  363. package/src/vite/plugins/expose-id-utils.ts +88 -55
  364. package/src/vite/plugins/expose-ids/export-analysis.ts +101 -34
  365. package/src/vite/plugins/expose-ids/handler-transform.ts +11 -90
  366. package/src/vite/plugins/expose-ids/loader-transform.ts +14 -24
  367. package/src/vite/plugins/expose-ids/router-transform.ts +118 -29
  368. package/src/vite/plugins/expose-internal-ids.ts +505 -486
  369. package/src/vite/plugins/performance-tracks.ts +26 -25
  370. package/src/vite/plugins/refresh-cmd.ts +1 -1
  371. package/src/vite/plugins/use-cache-transform.ts +73 -83
  372. package/src/vite/plugins/version-injector.ts +40 -29
  373. package/src/vite/plugins/version-plugin.ts +37 -40
  374. package/src/vite/plugins/virtual-entries.ts +39 -25
  375. package/src/vite/rango.ts +109 -118
  376. package/src/vite/router-discovery.ts +718 -119
  377. package/src/vite/utils/ast-handler-extract.ts +26 -35
  378. package/src/vite/utils/banner.ts +1 -1
  379. package/src/vite/utils/bundle-analysis.ts +10 -15
  380. package/src/vite/utils/client-chunks.ts +184 -0
  381. package/src/vite/utils/directive-prologue.ts +40 -0
  382. package/src/vite/utils/forward-user-plugins.ts +171 -0
  383. package/src/vite/utils/manifest-utils.ts +4 -59
  384. package/src/vite/utils/package-resolution.ts +20 -52
  385. package/src/vite/utils/prerender-utils.ts +54 -39
  386. package/src/vite/utils/shared-utils.ts +90 -41
  387. package/src/browser/action-response-classifier.ts +0 -99
  388. package/src/browser/react/use-client-cache.ts +0 -58
  389. package/src/browser/shallow.ts +0 -40
  390. package/src/handles/index.ts +0 -7
  391. package/src/network-error-thrower.tsx +0 -23
  392. package/src/router/middleware-cookies.ts +0 -55
@@ -14,6 +14,7 @@ import {
14
14
  getParallelEntries,
15
15
  getParallelSlotEntries,
16
16
  type EntryData,
17
+ type ParallelEntryData,
17
18
  } from "../../server/context";
18
19
  import type {
19
20
  HandlerContext,
@@ -34,58 +35,41 @@ import {
34
35
  import { resolveLoaderData } from "./loader-cache.js";
35
36
  import {
36
37
  handleHandlerResult,
38
+ warnOnStreamedResponse,
37
39
  tryStaticHandler,
38
40
  tryStaticSlot,
39
41
  resolveLayoutComponent,
40
42
  resolveWithErrorBoundary,
43
+ buildLoaderErrorContext,
41
44
  } from "./helpers.js";
45
+ import { applyViewTransitionDefault } from "./view-transition-default.js";
42
46
  import { getRouterContext } from "../router-context.js";
43
- import { resolveSink, safeEmit } from "../telemetry.js";
47
+ import { observeEvent, observeHandler } from "../instrument.js";
48
+ import { observeStreamedHandler } from "./streamed-handler-telemetry.js";
44
49
  import {
45
50
  track,
46
- RSCRouterContext,
51
+ RangoContext,
47
52
  runInsideLoaderScope,
48
53
  } from "../../server/context.js";
49
54
 
50
- // ---------------------------------------------------------------------------
51
- // Telemetry helpers
52
- // ---------------------------------------------------------------------------
53
-
54
55
  /**
55
- * Attach a fire-and-forget rejection observer to a streamed handler promise.
56
- * Silently no-ops when called outside RouterContext (e.g. in unit tests).
56
+ * Trace a parallel slot that's being force-rendered on a full refetch (client
57
+ * has no cached state). User revalidate fns are bypassed in this case — see
58
+ * the call sites for the load-bearing rationale.
57
59
  */
58
- function observeStreamedHandler(
59
- promise: Promise<ReactNode>,
60
- segmentId: string,
61
- segmentType: string,
62
- pathname?: string,
63
- routeKey?: string,
64
- params?: Record<string, string>,
60
+ function traceFullRefetchedParallelSlot(
61
+ parallelId: string,
62
+ belongsToRoute: boolean,
65
63
  ): void {
66
- let routerCtx;
67
- try {
68
- routerCtx = getRouterContext();
69
- } catch {
70
- return;
71
- }
72
- if (!routerCtx?.telemetry) return;
73
- const sink = resolveSink(routerCtx.telemetry);
74
- const reqId = routerCtx.requestId;
75
- promise.catch((err: unknown) => {
76
- const errorObj = err instanceof Error ? err : new Error(String(err));
77
- safeEmit(sink, {
78
- type: "handler.error",
79
- timestamp: performance.now(),
80
- requestId: reqId,
81
- segmentId,
82
- segmentType,
83
- error: errorObj,
84
- handledByBoundary: true,
85
- pathname,
86
- routeKey,
87
- params,
88
- });
64
+ if (!isTraceActive()) return;
65
+ pushRevalidationTraceEntry({
66
+ segmentId: parallelId,
67
+ segmentType: "parallel",
68
+ belongsToRoute,
69
+ source: "parallel",
70
+ defaultShouldRevalidate: true,
71
+ finalShouldRevalidate: true,
72
+ reason: "full-refetch",
89
73
  });
90
74
  }
91
75
 
@@ -104,23 +88,14 @@ function emitRevalidationDecision(
104
88
  routeKey: string,
105
89
  shouldRevalidate: boolean,
106
90
  ): void {
107
- let routerCtx;
108
- try {
109
- routerCtx = getRouterContext();
110
- } catch {
111
- return;
112
- }
113
- if (routerCtx?.telemetry) {
114
- safeEmit(resolveSink(routerCtx.telemetry), {
115
- type: "revalidation.decision",
116
- timestamp: performance.now(),
117
- requestId: routerCtx.requestId,
118
- segmentId,
119
- pathname,
120
- routeKey,
121
- shouldRevalidate,
122
- });
123
- }
91
+ observeEvent({
92
+ type: "revalidation.decision",
93
+ timestamp: performance.now(),
94
+ segmentId,
95
+ pathname,
96
+ routeKey,
97
+ shouldRevalidate,
98
+ });
124
99
  }
125
100
 
126
101
  // ---------------------------------------------------------------------------
@@ -225,6 +200,10 @@ export async function resolveLoadersWithRevalidation<TEnv>(
225
200
  ),
226
201
  );
227
202
 
203
+ // Partial (revalidation) render path: a throwing DSL loader must still fire
204
+ // onError/loader.error. isPartial flags the reporting phase accordingly.
205
+ const errorContext = { ...buildLoaderErrorContext(ctx), isPartial: true };
206
+
228
207
  const loadersToRun = revalidationChecks.filter((c) => c.shouldRun);
229
208
  const segments: ResolvedSegment[] = loadersToRun.map(
230
209
  ({ loaderEntry, loader, segmentId, index }) => ({
@@ -242,6 +221,7 @@ export async function resolveLoadersWithRevalidation<TEnv>(
242
221
  entry,
243
222
  segmentId,
244
223
  ctx.pathname,
224
+ errorContext,
245
225
  ),
246
226
  belongsToRoute,
247
227
  }),
@@ -404,6 +384,97 @@ export function buildEntryRevalidateMap(
404
384
  return map;
405
385
  }
406
386
 
387
+ /**
388
+ * Resolve the component for a single parallel slot on the revalidation path.
389
+ * Pure component resolution shared verbatim by
390
+ * resolveParallelSegmentsWithRevalidation and the orphan-inlined loop in
391
+ * resolveOrphanLayoutWithRevalidation: try the static slot cache, else run the
392
+ * slot handler (pinning _currentSegmentId to the slot id so handle pushes land
393
+ * in the slot's own bucket, and wrapping a streamed handler). Returns the
394
+ * resolved component and whether the handler actually ran. Does NOT touch the
395
+ * revalidate-default policy (the caller decides shouldResolve, including the
396
+ * orphan-vs-main defaultOverride divergence) or loader-resolution ordering.
397
+ */
398
+ async function resolveParallelSlotComponent<TEnv>(args: {
399
+ shouldResolve: boolean;
400
+ parallelEntry: ParallelEntryData;
401
+ slot: string;
402
+ parallelId: string;
403
+ handler:
404
+ | ((ctx: HandlerContext<any, TEnv>) => ReactNode | Promise<ReactNode>)
405
+ | ReactNode
406
+ | undefined;
407
+ context: HandlerContext<any, TEnv>;
408
+ deps: SegmentResolutionDeps<TEnv>;
409
+ routeKey: string;
410
+ params: Record<string, string>;
411
+ }): Promise<{ component: ReactNode | undefined; handlerRan: boolean }> {
412
+ const {
413
+ shouldResolve,
414
+ parallelEntry,
415
+ slot,
416
+ parallelId,
417
+ handler,
418
+ context,
419
+ deps,
420
+ routeKey,
421
+ params,
422
+ } = args;
423
+
424
+ let component: ReactNode | undefined;
425
+ let handlerRan = false;
426
+ if (shouldResolve) {
427
+ component = await tryStaticSlot(parallelEntry, slot, parallelId);
428
+ // tryStaticSlot returning a value means the static cache supplied the
429
+ // component — handler did NOT run. handlerRan stays false.
430
+ }
431
+ if (component === undefined) {
432
+ const hasLoadingFallback =
433
+ parallelEntry.loading !== undefined && parallelEntry.loading !== false;
434
+ if (!shouldResolve) {
435
+ component = null;
436
+ } else if (handler === undefined) {
437
+ // Handler evicted (production static slot) but static lookup missed.
438
+ // Nothing to render — use null so the client keeps its cached version.
439
+ component = null;
440
+ } else {
441
+ // Slot-keyed pushes — slot owns its own bucket, parent layout owns its
442
+ // own. On slot-only revalidations the partial merge updates only the
443
+ // slot's bucket; the parent's bucket stays intact.
444
+ (context as InternalHandlerContext<any, TEnv>)._currentSegmentId =
445
+ parallelId;
446
+ handlerRan = true;
447
+ if (hasLoadingFallback) {
448
+ const result =
449
+ typeof handler === "function" ? handler(context) : handler;
450
+ if (result instanceof Promise) {
451
+ warnOnStreamedResponse(result, parallelId);
452
+ const tracked = deps.trackHandler(result, {
453
+ segmentId: parallelId,
454
+ segmentType: "parallel",
455
+ });
456
+ observeStreamedHandler(
457
+ tracked,
458
+ parallelId,
459
+ "parallel",
460
+ context.pathname,
461
+ routeKey,
462
+ params,
463
+ );
464
+ component = tracked as ReactNode;
465
+ } else {
466
+ component = result as ReactNode;
467
+ }
468
+ } else {
469
+ component =
470
+ typeof handler === "function" ? await handler(context) : handler;
471
+ }
472
+ }
473
+ }
474
+
475
+ return { component, handlerRan };
476
+ }
477
+
407
478
  /**
408
479
  * Resolve parallel segments with revalidation.
409
480
  */
@@ -421,9 +492,30 @@ export async function resolveParallelSegmentsWithRevalidation<TEnv>(
421
492
  deps: SegmentResolutionDeps<TEnv>,
422
493
  actionContext?: ActionContext,
423
494
  stale?: boolean,
495
+ options?: {
496
+ /**
497
+ * Seed for an unknown parent-chain slot (slot not in clientSegmentIds) when
498
+ * there are no deciding revalidate fns. "type-derived" (default, main path):
499
+ * `belongsToRoute || isNewParent`. "force-render" (orphan path): always
500
+ * `true` — orphan parallels always belong to the route and must render
501
+ * unless the user opts out via revalidate(); the #482 blank-parent-chain-
502
+ * slot guard.
503
+ */
504
+ parentChainDefault?: "type-derived" | "force-render";
505
+ /**
506
+ * When a slot's loaders are resolved relative to the slot segment push.
507
+ * "after" (default, main path) pushes the slot segment first; "before"
508
+ * (orphan path) resolves loaders first. This only changes the
509
+ * segments/matchedIds emission ORDER (the client reconciler is insensitive
510
+ * to it: loader sub-ids are filtered out and slots are re-grouped by parent).
511
+ */
512
+ loaderOrder?: "after" | "before";
513
+ },
424
514
  ): Promise<SegmentRevalidationResult> {
425
515
  const segments: ResolvedSegment[] = [];
426
516
  const matchedIds: string[] = [];
517
+ const parentChainDefault = options?.parentChainDefault ?? "type-derived";
518
+ const loaderOrder = options?.loaderOrder ?? "after";
427
519
 
428
520
  const resolvedParallelEntries = new Set<string>();
429
521
  for (const { slot, entry: parallelEntry } of getParallelSlotEntries(
@@ -448,44 +540,61 @@ export async function resolveParallelSegmentsWithRevalidation<TEnv>(
448
540
 
449
541
  const isFullRefetch = clientSegmentIds.size === 0;
450
542
  const isNewParent = !clientSegmentIds.has(entry.shortCode);
451
- if (
452
- isFullRefetch ||
453
- clientSegmentIds.has(parallelId) ||
454
- belongsToRoute ||
455
- isNewParent
456
- ) {
457
- matchedIds.push(parallelId);
543
+
544
+ // A slot's loaders (never cached) are deduped per parallel entry and
545
+ // emitted either before or after the slot segment per loaderOrder.
546
+ const resolveSlotLoaders = async () => {
547
+ if (resolvedParallelEntries.has(parallelEntry.id)) return;
548
+ const loaderResult = await resolveLoadersWithRevalidation(
549
+ parallelEntry,
550
+ context,
551
+ belongsToRoute,
552
+ clientSegmentIds,
553
+ prevParams,
554
+ request,
555
+ prevUrl,
556
+ nextUrl,
557
+ routeKey,
558
+ deps,
559
+ actionContext,
560
+ entry.shortCode,
561
+ stale,
562
+ );
563
+ segments.push(...loaderResult.segments);
564
+ matchedIds.push(...loaderResult.matchedIds);
565
+ resolvedParallelEntries.add(parallelEntry.id);
566
+ };
567
+
568
+ if (loaderOrder === "before") {
569
+ await resolveSlotLoaders();
458
570
  }
571
+ // Always announce the slot in matchedIds — it's unconditionally appended
572
+ // to `segments` below, and a segment present in segments but missing from
573
+ // matched lets the client prune it (then it's missing from clientSegmentIds
574
+ // on the next request, perpetuating the staleness).
575
+ matchedIds.push(parallelId);
459
576
 
460
- const shouldResolve = await (async () => {
461
- if (isFullRefetch) {
462
- if (isTraceActive()) {
463
- pushRevalidationTraceEntry({
464
- segmentId: parallelId,
465
- segmentType: "parallel",
466
- belongsToRoute,
467
- source: "parallel",
468
- defaultShouldRevalidate: true,
469
- finalShouldRevalidate: true,
470
- reason: "full-refetch",
471
- });
472
- }
473
- return true;
474
- }
577
+ let shouldResolve: boolean;
578
+ if (isFullRefetch) {
579
+ // Client has nothing cached — slot MUST render. User revalidate fns are
580
+ // bypassed here because returning false would leave the segment blank
581
+ // with no client-side fallback.
582
+ traceFullRefetchedParallelSlot(parallelId, belongsToRoute);
583
+ shouldResolve = true;
584
+ } else {
585
+ // For non-empty client sets, consult user revalidate fns. When the slot
586
+ // is unknown to the client, override the type-derived default so the
587
+ // soft chain seeds with the right "new segment" / "parent-chain" value.
588
+ let defaultOverride: { value: boolean; reason: string } | undefined;
475
589
  if (!clientSegmentIds.has(parallelId)) {
476
- const result = belongsToRoute || isNewParent;
477
- if (isTraceActive()) {
478
- pushRevalidationTraceEntry({
479
- segmentId: parallelId,
480
- segmentType: "parallel",
481
- belongsToRoute,
482
- source: "parallel",
483
- defaultShouldRevalidate: result,
484
- finalShouldRevalidate: result,
485
- reason: result ? "new-segment" : "skip-parent-chain",
486
- });
487
- }
488
- return result;
590
+ const value =
591
+ parentChainDefault === "force-render"
592
+ ? true
593
+ : belongsToRoute || isNewParent;
594
+ defaultOverride = {
595
+ value,
596
+ reason: value ? "new-segment" : "skip-parent-chain",
597
+ };
489
598
  }
490
599
 
491
600
  const dummySegment: ResolvedSegment = {
@@ -503,7 +612,7 @@ export async function resolveParallelSegmentsWithRevalidation<TEnv>(
503
612
  : {}),
504
613
  };
505
614
 
506
- return await evaluateRevalidation({
615
+ shouldResolve = await evaluateRevalidation({
507
616
  segment: dummySegment,
508
617
  prevParams,
509
618
  getPrevSegment: null,
@@ -519,8 +628,9 @@ export async function resolveParallelSegmentsWithRevalidation<TEnv>(
519
628
  actionContext,
520
629
  stale,
521
630
  traceSource: "parallel",
631
+ defaultOverride,
522
632
  });
523
- })();
633
+ }
524
634
  emitRevalidationDecision(
525
635
  parallelId,
526
636
  context.pathname,
@@ -528,44 +638,17 @@ export async function resolveParallelSegmentsWithRevalidation<TEnv>(
528
638
  shouldResolve,
529
639
  );
530
640
 
531
- let component: ReactNode | undefined;
532
- if (shouldResolve) {
533
- component = await tryStaticSlot(parallelEntry, slot, parallelId);
534
- }
535
- if (component === undefined) {
536
- const hasLoadingFallback =
537
- parallelEntry.loading !== undefined && parallelEntry.loading !== false;
538
- if (!shouldResolve) {
539
- component = null;
540
- } else if (handler === undefined) {
541
- // Handler evicted (production static slot) but static lookup missed.
542
- // Nothing to render — use null so the client keeps its cached version.
543
- component = null;
544
- } else if (hasLoadingFallback) {
545
- const result =
546
- typeof handler === "function" ? handler(context) : handler;
547
- if (result instanceof Promise) {
548
- const tracked = deps.trackHandler(result, {
549
- segmentId: parallelId,
550
- segmentType: "parallel",
551
- });
552
- observeStreamedHandler(
553
- tracked,
554
- parallelId,
555
- "parallel",
556
- context.pathname,
557
- routeKey,
558
- params,
559
- );
560
- component = tracked as ReactNode;
561
- } else {
562
- component = result as ReactNode;
563
- }
564
- } else {
565
- component =
566
- typeof handler === "function" ? await handler(context) : handler;
567
- }
568
- }
641
+ const { component, handlerRan } = await resolveParallelSlotComponent({
642
+ shouldResolve,
643
+ parallelEntry,
644
+ slot,
645
+ parallelId,
646
+ handler,
647
+ context,
648
+ deps,
649
+ routeKey,
650
+ params,
651
+ });
569
652
 
570
653
  segments.push({
571
654
  id: parallelId,
@@ -574,9 +657,14 @@ export async function resolveParallelSegmentsWithRevalidation<TEnv>(
574
657
  index: 0,
575
658
  component,
576
659
  loading: parallelEntry.loading === false ? null : parallelEntry.loading,
577
- transition: parallelEntry.transition,
660
+ transition: applyViewTransitionDefault(
661
+ parallelEntry.transition,
662
+ deps.viewTransitionDefault,
663
+ parallelId,
664
+ ),
578
665
  params,
579
666
  slot,
667
+ _handlerRan: handlerRan,
580
668
  belongsToRoute,
581
669
  parallelName: `${parallelEntry.id}.${slot}`,
582
670
  ...(parallelEntry.mountPath
@@ -584,28 +672,9 @@ export async function resolveParallelSegmentsWithRevalidation<TEnv>(
584
672
  : {}),
585
673
  });
586
674
 
587
- if (resolvedParallelEntries.has(parallelEntry.id)) {
588
- continue;
675
+ if (loaderOrder === "after") {
676
+ await resolveSlotLoaders();
589
677
  }
590
-
591
- const loaderResult = await resolveLoadersWithRevalidation(
592
- parallelEntry,
593
- context,
594
- belongsToRoute,
595
- clientSegmentIds,
596
- prevParams,
597
- request,
598
- prevUrl,
599
- nextUrl,
600
- routeKey,
601
- deps,
602
- actionContext,
603
- entry.shortCode,
604
- stale,
605
- );
606
- segments.push(...loaderResult.segments);
607
- matchedIds.push(...loaderResult.matchedIds);
608
- resolvedParallelEntries.add(parallelEntry.id);
609
678
  }
610
679
 
611
680
  return { segments, matchedIds };
@@ -631,6 +700,7 @@ export async function resolveEntryHandlerWithRevalidation<TEnv>(
631
700
  ): Promise<{ segment: ResolvedSegment; matchedId: string }> {
632
701
  const matchedId = entry.shortCode;
633
702
 
703
+ let handlerRan = false;
634
704
  const component = await revalidate(
635
705
  async () => {
636
706
  const hasSegment = clientSegmentIds.has(entry.shortCode);
@@ -707,6 +777,7 @@ export async function resolveEntryHandlerWithRevalidation<TEnv>(
707
777
  return shouldRevalidate;
708
778
  },
709
779
  async () => {
780
+ handlerRan = true;
710
781
  const doneHandler = track(`handler:${entry.id}`, 2);
711
782
  (context as InternalHandlerContext<any, TEnv>)._currentSegmentId =
712
783
  entry.shortCode;
@@ -729,13 +800,18 @@ export async function resolveEntryHandlerWithRevalidation<TEnv>(
729
800
  ? routeEntry.liveHandler
730
801
  : routeEntry.handler;
731
802
  if (!routeEntry.loading) {
732
- const result = handleHandlerResult(await handler(context));
803
+ const result = handleHandlerResult(
804
+ await observeHandler(entry.id, handler, context),
805
+ );
733
806
  doneHandler();
734
807
  return result;
735
808
  }
736
809
  if (!actionContext) {
737
- const result = handleHandlerResult(handler(context));
810
+ const result = handleHandlerResult(
811
+ observeHandler(entry.id, handler, context),
812
+ );
738
813
  if (result instanceof Promise) {
814
+ warnOnStreamedResponse(result, routeEntry.id);
739
815
  result.finally(doneHandler).catch(() => {});
740
816
  const tracked = deps.trackHandler(result, {
741
817
  segmentId: entry.shortCode,
@@ -757,7 +833,9 @@ export async function resolveEntryHandlerWithRevalidation<TEnv>(
757
833
  debugLog("segment.action", "resolving action route with awaited value", {
758
834
  entryId: entry.id,
759
835
  });
760
- const actionResult = handleHandlerResult(await handler(context));
836
+ const actionResult = handleHandlerResult(
837
+ await observeHandler(entry.id, handler, context),
838
+ );
761
839
  doneHandler();
762
840
  return {
763
841
  content: Promise.resolve(actionResult),
@@ -781,13 +859,18 @@ export async function resolveEntryHandlerWithRevalidation<TEnv>(
781
859
  index: 0,
782
860
  component: resolvedComponent,
783
861
  loading: entry.loading === false ? null : entry.loading,
784
- transition: entry.transition,
862
+ transition: applyViewTransitionDefault(
863
+ entry.transition,
864
+ deps.viewTransitionDefault,
865
+ entry.shortCode,
866
+ ),
785
867
  params,
786
868
  belongsToRoute,
787
869
  ...(entry.type === "layout" || entry.type === "cache"
788
870
  ? { layoutName: entry.id }
789
871
  : {}),
790
872
  ...(entry.mountPath ? { mountPath: entry.mountPath } : {}),
873
+ _handlerRan: handlerRan,
791
874
  };
792
875
 
793
876
  return { segment, matchedId };
@@ -806,7 +889,6 @@ export async function resolveSegmentWithRevalidation<TEnv>(
806
889
  request: Request,
807
890
  prevUrl: URL,
808
891
  nextUrl: URL,
809
- loaderPromises: Map<string, Promise<any>>,
810
892
  deps: SegmentResolutionDeps<TEnv>,
811
893
  actionContext?: ActionContext,
812
894
  stale?: boolean,
@@ -868,7 +950,6 @@ export async function resolveSegmentWithRevalidation<TEnv>(
868
950
  prevUrl,
869
951
  nextUrl,
870
952
  routeKey,
871
- loaderPromises,
872
953
  true,
873
954
  deps,
874
955
  actionContext,
@@ -953,7 +1034,6 @@ export async function resolveSegmentWithRevalidation<TEnv>(
953
1034
  prevUrl,
954
1035
  nextUrl,
955
1036
  routeKey,
956
- loaderPromises,
957
1037
  false,
958
1038
  deps,
959
1039
  actionContext,
@@ -980,7 +1060,6 @@ export async function resolveOrphanLayoutWithRevalidation<TEnv>(
980
1060
  prevUrl: URL,
981
1061
  nextUrl: URL,
982
1062
  routeKey: string,
983
- loaderPromises: Map<string, Promise<any>>,
984
1063
  belongsToRoute: boolean,
985
1064
  deps: SegmentResolutionDeps<TEnv>,
986
1065
  actionContext?: ActionContext,
@@ -1117,166 +1196,41 @@ export async function resolveOrphanLayoutWithRevalidation<TEnv>(
1117
1196
  belongsToRoute,
1118
1197
  layoutName: orphan.id,
1119
1198
  loading: orphan.loading === false ? null : orphan.loading,
1120
- transition: orphan.transition,
1199
+ transition: applyViewTransitionDefault(
1200
+ orphan.transition,
1201
+ deps.viewTransitionDefault,
1202
+ orphan.shortCode,
1203
+ ),
1121
1204
  ...(orphan.mountPath ? { mountPath: orphan.mountPath } : {}),
1122
1205
  });
1123
1206
 
1124
- const resolvedParallelEntries = new Set<string>();
1125
- for (const { slot, entry: parallelEntry } of getParallelSlotEntries(
1126
- orphan.parallel,
1127
- )) {
1128
- invariant(
1129
- parallelEntry.type === "parallel",
1130
- `Expected parallel entry, got: ${parallelEntry.type}`,
1131
- );
1132
-
1133
- if (!resolvedParallelEntries.has(parallelEntry.id)) {
1134
- // shortCodeOverride must match the parent layout, not the parallel entry.
1135
- const loaderResult = await resolveLoadersWithRevalidation(
1136
- parallelEntry,
1137
- context,
1138
- belongsToRoute,
1139
- clientSegmentIds,
1140
- prevParams,
1141
- request,
1142
- prevUrl,
1143
- nextUrl,
1144
- routeKey,
1145
- deps,
1146
- actionContext,
1147
- orphan.shortCode,
1148
- stale,
1149
- );
1150
- segments.push(...loaderResult.segments);
1151
- matchedIds.push(...loaderResult.matchedIds);
1152
- resolvedParallelEntries.add(parallelEntry.id);
1153
- }
1154
-
1155
- const slots = parallelEntry.handler as Record<
1156
- `@${string}`,
1157
- | ((ctx: HandlerContext<any, TEnv>) => ReactNode | Promise<ReactNode>)
1158
- | ReactNode
1159
- >;
1160
- // Handler may be undefined in production after static handler eviction.
1161
- const handler = slots[slot];
1162
-
1163
- // Use orphan.shortCode (the parent layout) to match the SSR path
1164
- // (resolveParallelEntry receives parentShortCode = orphan.shortCode).
1165
- // Using parallelEntry.shortCode would generate IDs the client doesn't know about.
1166
- const parallelId = `${orphan.shortCode}.${slot}`;
1167
- matchedIds.push(parallelId);
1168
-
1169
- const shouldResolve = await (async () => {
1170
- if (!clientSegmentIds.has(parallelId)) {
1171
- if (isTraceActive()) {
1172
- pushRevalidationTraceEntry({
1173
- segmentId: parallelId,
1174
- segmentType: "parallel",
1175
- belongsToRoute,
1176
- source: "parallel",
1177
- defaultShouldRevalidate: true,
1178
- finalShouldRevalidate: true,
1179
- reason: "new-segment",
1180
- });
1181
- }
1182
- return true;
1183
- }
1184
-
1185
- const dummySegment: ResolvedSegment = {
1186
- id: parallelId,
1187
- namespace: parallelEntry.id,
1188
- type: "parallel",
1189
- index: 0,
1190
- component: null as any,
1191
- params,
1192
- slot,
1193
- belongsToRoute,
1194
- parallelName: `${parallelEntry.id}.${slot}`,
1195
- ...(parallelEntry.mountPath
1196
- ? { mountPath: parallelEntry.mountPath }
1197
- : {}),
1198
- };
1199
-
1200
- return await evaluateRevalidation({
1201
- segment: dummySegment,
1202
- prevParams,
1203
- getPrevSegment: null,
1204
- request,
1205
- prevUrl,
1206
- nextUrl,
1207
- revalidations: parallelEntry.revalidate.map((fn, i) => ({
1208
- name: `revalidate${i}`,
1209
- fn,
1210
- })),
1211
- routeKey,
1212
- context,
1213
- actionContext,
1214
- stale,
1215
- traceSource: "parallel",
1216
- });
1217
- })();
1218
- emitRevalidationDecision(
1219
- parallelId,
1220
- context.pathname,
1221
- routeKey,
1222
- shouldResolve,
1223
- );
1224
-
1225
- let component: ReactNode | undefined;
1226
- if (shouldResolve) {
1227
- component = await tryStaticSlot(parallelEntry, slot, parallelId);
1228
- }
1229
- if (component === undefined) {
1230
- const hasLoadingFallback =
1231
- parallelEntry.loading !== undefined && parallelEntry.loading !== false;
1232
- if (!shouldResolve) {
1233
- component = null;
1234
- } else if (handler === undefined) {
1235
- // Handler evicted (production static slot) but static lookup missed.
1236
- component = null;
1237
- } else if (hasLoadingFallback) {
1238
- const result =
1239
- typeof handler === "function" ? handler(context) : handler;
1240
- if (result instanceof Promise) {
1241
- const tracked = deps.trackHandler(result, {
1242
- segmentId: parallelId,
1243
- segmentType: "parallel",
1244
- });
1245
- observeStreamedHandler(
1246
- tracked,
1247
- parallelId,
1248
- "parallel",
1249
- context.pathname,
1250
- routeKey,
1251
- params,
1252
- );
1253
- component = tracked as ReactNode;
1254
- } else {
1255
- component = result as ReactNode;
1256
- }
1257
- } else {
1258
- component =
1259
- typeof handler === "function" ? await handler(context) : handler;
1260
- }
1261
- }
1262
-
1263
- segments.push({
1264
- id: parallelId,
1265
- namespace: parallelEntry.id,
1266
- type: "parallel",
1267
- index: 0,
1268
- component,
1269
- loading: parallelEntry.loading === false ? null : parallelEntry.loading,
1270
- transition: parallelEntry.transition,
1271
- params,
1272
- slot,
1273
- belongsToRoute,
1274
- parallelName: `${parallelEntry.id}.${slot}`,
1275
- ...(parallelEntry.mountPath
1276
- ? { mountPath: parallelEntry.mountPath }
1277
- : {}),
1278
- });
1279
- }
1207
+ // Resolve the orphan layout's parallel slots through the shared main-path
1208
+ // helper. The orphan policy is carried by explicit args, byte-for-byte:
1209
+ // - parentChainDefault "force-render": an unknown parent-chain slot seeds
1210
+ // `true` (orphan parallels always belong to the route — the #482 guard),
1211
+ // where the main path would seed `belongsToRoute || isNewParent`.
1212
+ // - loaderOrder "before": a slot's loaders are emitted before the slot
1213
+ // segment, matching the prior inlined order.
1214
+ // `entry.shortCode` inside the helper is `orphan.shortCode` (orphan is passed
1215
+ // as `entry`), so the parallel ids + loader shortCodeOverride are unchanged.
1216
+ const parallelResult = await resolveParallelSegmentsWithRevalidation(
1217
+ orphan,
1218
+ params,
1219
+ context,
1220
+ belongsToRoute,
1221
+ clientSegmentIds,
1222
+ prevParams,
1223
+ request,
1224
+ prevUrl,
1225
+ nextUrl,
1226
+ routeKey,
1227
+ deps,
1228
+ actionContext,
1229
+ stale,
1230
+ { parentChainDefault: "force-render", loaderOrder: "before" },
1231
+ );
1232
+ segments.push(...parallelResult.segments);
1233
+ matchedIds.push(...parallelResult.matchedIds);
1280
1234
 
1281
1235
  return { segments, matchedIds };
1282
1236
  }
@@ -1294,7 +1248,6 @@ export async function resolveAllSegmentsWithRevalidation<TEnv>(
1294
1248
  request: Request,
1295
1249
  prevUrl: URL,
1296
1250
  nextUrl: URL,
1297
- loaderPromises: Map<string, Promise<any>>,
1298
1251
  actionContext: ActionContext | undefined,
1299
1252
  interceptResult: { intercept: any; entry: EntryData } | null,
1300
1253
  localRouteName: string,
@@ -1328,7 +1281,7 @@ export async function resolveAllSegmentsWithRevalidation<TEnv>(
1328
1281
 
1329
1282
  const nonParallelEntry = entry as Exclude<EntryData, { type: "parallel" }>;
1330
1283
  if (entry.type === "cache") {
1331
- const store = RSCRouterContext.getStore();
1284
+ const store = RangoContext.getStore();
1332
1285
  if (store) store.insideCacheScope = true;
1333
1286
  }
1334
1287
  const doneEntry = track(`segment:${entry.id}`, 1);
@@ -1346,7 +1299,6 @@ export async function resolveAllSegmentsWithRevalidation<TEnv>(
1346
1299
  request,
1347
1300
  prevUrl,
1348
1301
  nextUrl,
1349
- loaderPromises,
1350
1302
  deps,
1351
1303
  actionContext,
1352
1304
  stale,