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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (460) hide show
  1. package/AGENTS.md +17 -0
  2. package/README.md +432 -7
  3. package/dist/bin/rango.js +2073 -213
  4. package/dist/testing/vitest.js +82 -0
  5. package/dist/vite/index.js +7258 -2714
  6. package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
  7. package/package.json +140 -67
  8. package/skills/api-client/SKILL.md +211 -0
  9. package/skills/breadcrumbs/SKILL.md +329 -0
  10. package/skills/bundle-analysis/SKILL.md +159 -0
  11. package/skills/cache-guide/SKILL.md +487 -0
  12. package/skills/caching/SKILL.md +357 -25
  13. package/skills/comparison/SKILL.md +50 -0
  14. package/skills/comparison/agents/openai.yaml +4 -0
  15. package/skills/comparison/references/framework-comparison.md +837 -0
  16. package/skills/composability/SKILL.md +246 -0
  17. package/skills/css/SKILL.md +76 -0
  18. package/skills/debug-manifest/SKILL.md +16 -10
  19. package/skills/document-cache/SKILL.md +87 -62
  20. package/skills/fonts/SKILL.md +6 -4
  21. package/skills/handler-use/SKILL.md +364 -0
  22. package/skills/hooks/SKILL.md +557 -79
  23. package/skills/host-router/SKILL.md +320 -0
  24. package/skills/i18n/SKILL.md +276 -0
  25. package/skills/intercept/SKILL.md +207 -15
  26. package/skills/layout/SKILL.md +146 -6
  27. package/skills/links/SKILL.md +304 -25
  28. package/skills/loader/SKILL.md +616 -54
  29. package/skills/middleware/SKILL.md +217 -37
  30. package/skills/migrate-nextjs/SKILL.md +611 -0
  31. package/skills/migrate-react-router/SKILL.md +927 -0
  32. package/skills/mime-routes/SKILL.md +42 -11
  33. package/skills/observability/SKILL.md +194 -0
  34. package/skills/parallel/SKILL.md +284 -3
  35. package/skills/ppr/SKILL.md +426 -0
  36. package/skills/prerender/SKILL.md +437 -52
  37. package/skills/rango/SKILL.md +369 -22
  38. package/skills/react-compiler/SKILL.md +168 -0
  39. package/skills/response-routes/SKILL.md +263 -121
  40. package/skills/route/SKILL.md +350 -21
  41. package/skills/router-setup/SKILL.md +246 -33
  42. package/skills/scripts/SKILL.md +179 -0
  43. package/skills/server-actions/SKILL.md +775 -0
  44. package/skills/shell-manifest/SKILL.md +185 -0
  45. package/skills/streams-and-websockets/SKILL.md +283 -0
  46. package/skills/tailwind/SKILL.md +27 -3
  47. package/skills/testing/SKILL.md +126 -222
  48. package/skills/testing/bindings.md +103 -0
  49. package/skills/testing/cache-prerender.md +127 -0
  50. package/skills/testing/client-components.md +124 -0
  51. package/skills/testing/e2e-parity.md +125 -0
  52. package/skills/testing/flight.md +91 -0
  53. package/skills/testing/handles.md +131 -0
  54. package/skills/testing/loader.md +128 -0
  55. package/skills/testing/middleware.md +99 -0
  56. package/skills/testing/render-handler.md +122 -0
  57. package/skills/testing/response-routes.md +95 -0
  58. package/skills/testing/reverse-and-types.md +85 -0
  59. package/skills/testing/server-actions.md +107 -0
  60. package/skills/testing/server-tree.md +128 -0
  61. package/skills/testing/setup.md +123 -0
  62. package/skills/theme/SKILL.md +9 -8
  63. package/skills/typesafety/SKILL.md +532 -103
  64. package/skills/use-cache/SKILL.md +367 -0
  65. package/skills/vercel/SKILL.md +128 -0
  66. package/skills/view-transitions/SKILL.md +337 -0
  67. package/src/__augment-tests__/augment.ts +81 -0
  68. package/src/__augment-tests__/augmented.check.ts +116 -0
  69. package/src/__internal.ts +77 -44
  70. package/src/bin/rango.ts +312 -15
  71. package/src/browser/action-coordinator.ts +114 -0
  72. package/src/browser/action-fence.ts +47 -0
  73. package/src/browser/app-shell.ts +39 -0
  74. package/src/browser/app-version.ts +14 -0
  75. package/src/browser/connection-warmup.ts +134 -0
  76. package/src/browser/cookie-name.ts +140 -0
  77. package/src/browser/event-controller.ts +293 -202
  78. package/src/browser/history-state.ts +101 -0
  79. package/src/browser/index.ts +3 -3
  80. package/src/browser/intercept-utils.ts +52 -0
  81. package/src/browser/invalidate-client-cache.ts +52 -0
  82. package/src/browser/link-interceptor.ts +24 -4
  83. package/src/browser/logging.ts +11 -0
  84. package/src/browser/merge-segment-loaders.ts +20 -12
  85. package/src/browser/navigation-bridge.ts +385 -576
  86. package/src/browser/navigation-client.ts +245 -75
  87. package/src/browser/navigation-store-handle.ts +38 -0
  88. package/src/browser/navigation-store.ts +184 -118
  89. package/src/browser/navigation-transaction.ts +247 -0
  90. package/src/browser/network-error-handler.ts +88 -0
  91. package/src/browser/partial-update.ts +412 -364
  92. package/src/browser/prefetch/cache.ts +359 -0
  93. package/src/browser/prefetch/fetch.ts +452 -0
  94. package/src/browser/prefetch/observer.ts +65 -0
  95. package/src/browser/prefetch/policy.ts +48 -0
  96. package/src/browser/prefetch/queue.ts +209 -0
  97. package/src/browser/prefetch/resource-ready.ts +77 -0
  98. package/src/browser/rango-state.ts +194 -0
  99. package/src/browser/react/Link.tsx +275 -68
  100. package/src/browser/react/NavigationProvider.tsx +265 -109
  101. package/src/browser/react/ScrollRestoration.tsx +10 -6
  102. package/src/browser/react/context.ts +11 -0
  103. package/src/browser/react/filter-segment-order.ts +70 -0
  104. package/src/browser/react/index.ts +0 -48
  105. package/src/browser/react/location-state-shared.ts +272 -60
  106. package/src/browser/react/location-state.ts +90 -20
  107. package/src/browser/react/mount-context.ts +6 -1
  108. package/src/browser/react/nonce-context.ts +23 -0
  109. package/src/browser/react/shallow-equal.ts +27 -0
  110. package/src/browser/react/use-action.ts +35 -66
  111. package/src/browser/react/use-handle.ts +39 -126
  112. package/src/browser/react/use-href.tsx +8 -1
  113. package/src/browser/react/use-link-status.ts +39 -13
  114. package/src/browser/react/use-navigation.ts +53 -69
  115. package/src/browser/react/use-params.ts +75 -0
  116. package/src/browser/react/use-pathname.ts +47 -0
  117. package/src/browser/react/use-reverse.ts +106 -0
  118. package/src/browser/react/use-router.ts +98 -0
  119. package/src/browser/react/use-search-params.ts +51 -0
  120. package/src/browser/react/use-segments.ts +72 -99
  121. package/src/browser/response-adapter.ts +164 -0
  122. package/src/browser/rsc-router.tsx +300 -72
  123. package/src/browser/scroll-restoration.ts +138 -50
  124. package/src/browser/segment-reconciler.ts +243 -0
  125. package/src/browser/segment-structure-assert.ts +17 -1
  126. package/src/browser/server-action-bridge.ts +668 -613
  127. package/src/browser/types.ts +223 -51
  128. package/src/browser/validate-redirect-origin.ts +56 -0
  129. package/src/build/collect-fallback-refs.ts +107 -0
  130. package/src/build/generate-manifest.ts +252 -161
  131. package/src/build/generate-route-types.ts +41 -1038
  132. package/src/build/index.ts +12 -7
  133. package/src/build/prefix-tree-utils.ts +123 -0
  134. package/src/build/route-trie.ts +225 -42
  135. package/src/build/route-types/ast-helpers.ts +25 -0
  136. package/src/build/route-types/ast-route-extraction.ts +105 -0
  137. package/src/build/route-types/codegen.ts +113 -0
  138. package/src/build/route-types/include-resolution.ts +812 -0
  139. package/src/build/route-types/param-extraction.ts +51 -0
  140. package/src/build/route-types/per-module-writer.ts +144 -0
  141. package/src/build/route-types/router-processing.ts +695 -0
  142. package/src/build/route-types/scan-filter.ts +85 -0
  143. package/src/build/route-types/source-scan.ts +216 -0
  144. package/src/build/runtime-discovery.ts +223 -0
  145. package/src/cache/background-task.ts +34 -0
  146. package/src/cache/cache-error.ts +104 -0
  147. package/src/cache/cache-key-utils.ts +60 -0
  148. package/src/cache/cache-policy.ts +199 -0
  149. package/src/cache/cache-runtime.ts +525 -0
  150. package/src/cache/cache-scope.ts +298 -332
  151. package/src/cache/cache-tag.ts +103 -0
  152. package/src/cache/cf/cf-base64.ts +33 -0
  153. package/src/cache/cf/cf-cache-constants.ts +127 -0
  154. package/src/cache/cf/cf-cache-store.ts +2500 -158
  155. package/src/cache/cf/cf-cache-types.ts +349 -0
  156. package/src/cache/cf/cf-kv-utils.ts +46 -0
  157. package/src/cache/cf/cf-tag-marker-memo.ts +105 -0
  158. package/src/cache/cf/index.ts +17 -17
  159. package/src/cache/document-cache.ts +199 -92
  160. package/src/cache/handle-capture.ts +81 -0
  161. package/src/cache/handle-snapshot.ts +111 -0
  162. package/src/cache/index.ts +29 -35
  163. package/src/cache/memory-segment-store.ts +363 -30
  164. package/src/cache/profile-registry.ts +88 -0
  165. package/src/cache/read-through-swr.ts +178 -0
  166. package/src/cache/segment-codec.ts +248 -0
  167. package/src/cache/shell-cache.ts +386 -0
  168. package/src/cache/tag-invalidation.ts +230 -0
  169. package/src/cache/taint.ts +153 -0
  170. package/src/cache/types.ts +156 -211
  171. package/src/cache/vercel/index.ts +11 -0
  172. package/src/cache/vercel/vercel-cache-store.ts +1102 -0
  173. package/src/client.rsc.tsx +43 -21
  174. package/src/client.tsx +131 -347
  175. package/src/cloudflare/index.ts +11 -0
  176. package/src/cloudflare/tracing.ts +109 -0
  177. package/src/component-utils.ts +23 -4
  178. package/src/components/DefaultDocument.tsx +13 -3
  179. package/src/context-var.ts +168 -0
  180. package/src/debug.ts +19 -9
  181. package/src/decode-loader-results.ts +52 -0
  182. package/src/defer.ts +185 -0
  183. package/src/deps/ssr.ts +0 -1
  184. package/src/encode-kv.ts +49 -0
  185. package/src/errors.ts +106 -10
  186. package/src/escape-script.ts +52 -0
  187. package/src/handle.ts +110 -35
  188. package/src/handles/MetaTags.tsx +83 -59
  189. package/src/handles/Scripts.tsx +183 -0
  190. package/src/handles/breadcrumbs.ts +93 -0
  191. package/src/handles/deferred-resolution.ts +127 -0
  192. package/src/handles/is-thenable.ts +18 -0
  193. package/src/handles/meta.ts +44 -53
  194. package/src/handles/script.ts +244 -0
  195. package/src/host/cookie-handler.ts +20 -65
  196. package/src/host/errors.ts +21 -30
  197. package/src/host/index.ts +13 -9
  198. package/src/host/pattern-matcher.ts +50 -79
  199. package/src/host/router.ts +151 -121
  200. package/src/host/testing.ts +45 -32
  201. package/src/host/types.ts +52 -11
  202. package/src/host/utils.ts +2 -2
  203. package/src/href-client.ts +192 -57
  204. package/src/index.rsc.ts +177 -35
  205. package/src/index.ts +255 -71
  206. package/src/internal-debug.ts +9 -2
  207. package/src/loader-store.ts +500 -0
  208. package/src/loader.rsc.ts +31 -99
  209. package/src/loader.ts +30 -12
  210. package/src/missing-id-error.ts +68 -0
  211. package/src/outlet-context.ts +1 -1
  212. package/src/outlet-provider.tsx +41 -0
  213. package/src/prerender/param-hash.ts +16 -14
  214. package/src/prerender/store.ts +121 -21
  215. package/src/prerender.ts +460 -26
  216. package/src/redirect-origin.ts +100 -0
  217. package/src/regex-escape.ts +8 -0
  218. package/src/render-error-thrower.tsx +20 -0
  219. package/src/response-utils.ts +62 -0
  220. package/src/reverse.ts +198 -128
  221. package/src/root-error-boundary.tsx +42 -48
  222. package/src/route-content-wrapper.tsx +22 -77
  223. package/src/route-definition/dsl-helpers.ts +1116 -0
  224. package/src/route-definition/helper-factories.ts +88 -0
  225. package/src/route-definition/helpers-types.ts +505 -0
  226. package/src/route-definition/index.ts +54 -0
  227. package/src/route-definition/redirect.ts +134 -0
  228. package/src/route-definition/resolve-handler-use.ts +160 -0
  229. package/src/route-definition/use-item-types.ts +29 -0
  230. package/src/route-definition.ts +1 -1481
  231. package/src/route-map-builder.ts +82 -144
  232. package/src/route-name.ts +53 -0
  233. package/src/route-types.ts +71 -45
  234. package/src/router/basename.ts +14 -0
  235. package/src/router/content-negotiation.ts +263 -0
  236. package/src/router/debug-manifest.ts +72 -0
  237. package/src/router/error-handling.ts +54 -27
  238. package/src/router/find-match.ts +245 -0
  239. package/src/router/handler-context.ts +377 -125
  240. package/src/router/instrument.ts +350 -0
  241. package/src/router/intercept-resolution.ts +59 -28
  242. package/src/router/lazy-includes.ts +254 -0
  243. package/src/router/loader-resolution.ts +421 -157
  244. package/src/router/logging.ts +106 -6
  245. package/src/router/manifest.ts +131 -57
  246. package/src/router/match-api.ts +167 -246
  247. package/src/router/match-context.ts +4 -24
  248. package/src/router/match-handlers.ts +440 -0
  249. package/src/router/match-middleware/background-revalidation.ts +117 -93
  250. package/src/router/match-middleware/cache-lookup.ts +297 -150
  251. package/src/router/match-middleware/cache-store.ts +123 -51
  252. package/src/router/match-middleware/intercept-resolution.ts +44 -43
  253. package/src/router/match-middleware/segment-resolution.ts +64 -22
  254. package/src/router/match-pipelines.ts +11 -87
  255. package/src/router/match-result.ts +121 -50
  256. package/src/router/metrics.ts +219 -28
  257. package/src/router/middleware-types.ts +93 -0
  258. package/src/router/middleware.ts +505 -441
  259. package/src/router/navigation-snapshot.ts +133 -0
  260. package/src/router/params-util.ts +23 -0
  261. package/src/router/parse-pattern.ts +115 -0
  262. package/src/router/pattern-matching.ts +311 -142
  263. package/src/router/prefetch-cache-ttl.ts +51 -0
  264. package/src/router/prefetch-limits.ts +37 -0
  265. package/src/router/prerender-match.ts +547 -0
  266. package/src/router/preview-match.ts +102 -0
  267. package/src/router/request-classification.ts +278 -0
  268. package/src/router/revalidation.ts +203 -62
  269. package/src/router/route-snapshot.ts +246 -0
  270. package/src/router/router-context.ts +45 -48
  271. package/src/router/router-interfaces.ts +554 -0
  272. package/src/router/router-options.ts +779 -0
  273. package/src/router/router-registry.ts +21 -0
  274. package/src/router/segment-resolution/fresh.ts +772 -0
  275. package/src/router/segment-resolution/helpers.ts +348 -0
  276. package/src/router/segment-resolution/loader-cache.ts +250 -0
  277. package/src/router/segment-resolution/loader-mask.ts +44 -0
  278. package/src/router/segment-resolution/revalidation.ts +1331 -0
  279. package/src/router/segment-resolution/static-store.ts +81 -0
  280. package/src/router/segment-resolution/streamed-handler-telemetry.ts +52 -0
  281. package/src/router/segment-resolution/view-transition-default.ts +56 -0
  282. package/src/router/segment-resolution.ts +25 -1354
  283. package/src/router/segment-wrappers.ts +292 -0
  284. package/src/router/state-cookie-name.ts +33 -0
  285. package/src/router/substitute-pattern-params.ts +75 -0
  286. package/src/router/telemetry-otel.ts +261 -0
  287. package/src/router/telemetry.ts +377 -0
  288. package/src/router/timeout.ts +128 -0
  289. package/src/router/tracing.ts +206 -0
  290. package/src/router/trie-matching.ts +240 -61
  291. package/src/router/types.ts +23 -70
  292. package/src/router/url-params.ts +57 -0
  293. package/src/router.ts +781 -2378
  294. package/src/rsc/full-payload.ts +70 -0
  295. package/src/rsc/handler-context.ts +46 -0
  296. package/src/rsc/handler.ts +905 -1142
  297. package/src/rsc/helpers.ts +275 -19
  298. package/src/rsc/index.ts +2 -25
  299. package/src/rsc/json-route-result.ts +38 -0
  300. package/src/rsc/loader-fetch.ts +305 -0
  301. package/src/rsc/manifest-init.ts +77 -0
  302. package/src/rsc/nonce.ts +14 -0
  303. package/src/rsc/origin-guard.ts +155 -0
  304. package/src/rsc/progressive-enhancement.ts +502 -0
  305. package/src/rsc/redirect-guard.ts +99 -0
  306. package/src/rsc/response-cache-serve.ts +238 -0
  307. package/src/rsc/response-error.ts +104 -0
  308. package/src/rsc/response-route-handler.ts +257 -0
  309. package/src/rsc/rsc-rendering.ts +337 -0
  310. package/src/rsc/runtime-warnings.ts +55 -0
  311. package/src/rsc/server-action.ts +522 -0
  312. package/src/rsc/shell-capture.ts +439 -0
  313. package/src/rsc/ssr-setup.ts +144 -0
  314. package/src/rsc/transition-gate.ts +89 -0
  315. package/src/rsc/types.ts +95 -12
  316. package/src/runtime-env.ts +18 -0
  317. package/src/search-params.ts +99 -82
  318. package/src/segment-content-promise.ts +67 -0
  319. package/src/segment-loader-promise.ts +149 -0
  320. package/src/segment-system.tsx +349 -134
  321. package/src/serialize.ts +243 -0
  322. package/src/server/context.ts +452 -85
  323. package/src/server/cookie-parse.ts +32 -0
  324. package/src/server/cookie-store.ts +310 -0
  325. package/src/server/fetchable-loader-store.ts +11 -6
  326. package/src/server/handle-store.ts +123 -42
  327. package/src/server/live.ts +130 -0
  328. package/src/server/loader-registry.ts +51 -100
  329. package/src/server/request-context.ts +842 -157
  330. package/src/server.ts +15 -8
  331. package/src/ssr/index.tsx +412 -136
  332. package/src/ssr/ssr-root.tsx +228 -0
  333. package/src/static-handler.ts +45 -18
  334. package/src/testing/cache-status.ts +162 -0
  335. package/src/testing/collect-handle.ts +46 -0
  336. package/src/testing/dispatch.ts +701 -0
  337. package/src/testing/dom.entry.ts +22 -0
  338. package/src/testing/e2e/fixture.ts +188 -0
  339. package/src/testing/e2e/index.ts +128 -0
  340. package/src/testing/e2e/matchers.ts +35 -0
  341. package/src/testing/e2e/page-helpers.ts +272 -0
  342. package/src/testing/e2e/parity.ts +387 -0
  343. package/src/testing/e2e/server.ts +195 -0
  344. package/src/testing/flight-matchers.ts +97 -0
  345. package/src/testing/flight-normalize.ts +11 -0
  346. package/src/testing/flight-runtime.d.ts +57 -0
  347. package/src/testing/flight-tree.ts +682 -0
  348. package/src/testing/flight.entry.ts +52 -0
  349. package/src/testing/flight.ts +257 -0
  350. package/src/testing/generated-routes.ts +199 -0
  351. package/src/testing/index.ts +105 -0
  352. package/src/testing/internal/context.ts +371 -0
  353. package/src/testing/internal/flight-client-globals.ts +30 -0
  354. package/src/testing/internal/seed-vars.ts +54 -0
  355. package/src/testing/render-handler.ts +357 -0
  356. package/src/testing/render-route.tsx +584 -0
  357. package/src/testing/run-loader.ts +385 -0
  358. package/src/testing/run-middleware.ts +205 -0
  359. package/src/testing/run-transition-when.ts +164 -0
  360. package/src/testing/vitest-stubs/cloudflare-email.ts +9 -0
  361. package/src/testing/vitest-stubs/cloudflare-workers.ts +21 -0
  362. package/src/testing/vitest-stubs/plugin-rsc.ts +16 -0
  363. package/src/testing/vitest-stubs/version.ts +5 -0
  364. package/src/testing/vitest.ts +305 -0
  365. package/src/theme/ThemeProvider.tsx +40 -72
  366. package/src/theme/ThemeScript.tsx +12 -14
  367. package/src/theme/constants.ts +57 -15
  368. package/src/theme/index.ts +3 -20
  369. package/src/theme/theme-context.ts +5 -35
  370. package/src/theme/theme-script.ts +43 -39
  371. package/src/theme/use-theme.ts +0 -3
  372. package/src/types/boundaries.ts +123 -0
  373. package/src/types/cache-types.ts +207 -0
  374. package/src/types/error-types.ts +132 -0
  375. package/src/types/global-namespace.ts +113 -0
  376. package/src/types/handler-context.ts +839 -0
  377. package/src/types/index.ts +81 -0
  378. package/src/types/loader-types.ts +212 -0
  379. package/src/types/request-scope.ts +112 -0
  380. package/src/types/route-config.ts +138 -0
  381. package/src/types/route-entry.ts +114 -0
  382. package/src/types/segments.ts +271 -0
  383. package/src/types.ts +1 -1795
  384. package/src/urls/include-helper.ts +162 -0
  385. package/src/urls/include-provider.ts +71 -0
  386. package/src/urls/index.ts +43 -0
  387. package/src/urls/path-helper-types.ts +413 -0
  388. package/src/urls/path-helper.ts +275 -0
  389. package/src/urls/pattern-types.ts +124 -0
  390. package/src/urls/response-types.ts +109 -0
  391. package/src/urls/type-extraction.ts +316 -0
  392. package/src/urls/urls-function.ts +80 -0
  393. package/src/urls.ts +1 -1341
  394. package/src/use-loader.tsx +406 -141
  395. package/src/vercel/index.ts +11 -0
  396. package/src/vercel/tracing.ts +88 -0
  397. package/src/vite/debug.ts +185 -0
  398. package/src/vite/discovery/bundle-postprocess.ts +182 -0
  399. package/src/vite/discovery/discover-routers.ts +389 -0
  400. package/src/vite/discovery/discovery-errors.ts +255 -0
  401. package/src/vite/discovery/gate-state.ts +171 -0
  402. package/src/vite/discovery/prerender-collection.ts +467 -0
  403. package/src/vite/discovery/route-types-writer.ts +214 -0
  404. package/src/vite/discovery/self-gen-tracking.ts +73 -0
  405. package/src/vite/discovery/state.ts +161 -0
  406. package/src/vite/discovery/virtual-module-codegen.ts +183 -0
  407. package/src/vite/index.ts +23 -2255
  408. package/src/vite/inject-client-debug.ts +36 -0
  409. package/src/vite/plugin-types.ts +303 -0
  410. package/src/vite/plugins/cjs-to-esm.ts +90 -0
  411. package/src/vite/plugins/client-ref-dedup.ts +120 -0
  412. package/src/vite/plugins/client-ref-hashing.ts +118 -0
  413. package/src/vite/plugins/cloudflare-protocol-loader-hook.d.mts +23 -0
  414. package/src/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
  415. package/src/vite/plugins/cloudflare-protocol-stub.ts +194 -0
  416. package/src/vite/{expose-action-id.ts → plugins/expose-action-id.ts} +88 -110
  417. package/src/vite/{expose-id-utils.ts → plugins/expose-id-utils.ts} +89 -79
  418. package/src/vite/plugins/expose-ids/export-analysis.ts +363 -0
  419. package/src/vite/plugins/expose-ids/handler-transform.ts +130 -0
  420. package/src/vite/plugins/expose-ids/loader-transform.ts +64 -0
  421. package/src/vite/plugins/expose-ids/router-transform.ts +199 -0
  422. package/src/vite/plugins/expose-ids/types.ts +45 -0
  423. package/src/vite/plugins/expose-internal-ids.ts +805 -0
  424. package/src/vite/plugins/performance-tracks.ts +89 -0
  425. package/src/vite/plugins/refresh-cmd.ts +127 -0
  426. package/src/vite/plugins/use-cache-transform.ts +313 -0
  427. package/src/vite/plugins/vercel-output.ts +384 -0
  428. package/src/vite/plugins/version-injector.ts +94 -0
  429. package/src/vite/plugins/version-plugin.ts +263 -0
  430. package/src/vite/plugins/virtual-entries.ts +234 -0
  431. package/src/vite/plugins/virtual-stub-plugin.ts +29 -0
  432. package/src/vite/rango.ts +560 -0
  433. package/src/vite/router-discovery.ts +1638 -0
  434. package/src/vite/{ast-handler-extract.ts → utils/ast-handler-extract.ts} +200 -37
  435. package/src/vite/utils/banner.ts +36 -0
  436. package/src/vite/utils/bundle-analysis.ts +132 -0
  437. package/src/vite/utils/client-chunks.ts +184 -0
  438. package/src/vite/utils/directive-prologue.ts +40 -0
  439. package/src/vite/utils/forward-user-plugins.ts +171 -0
  440. package/src/vite/utils/manifest-utils.ts +15 -0
  441. package/src/vite/utils/package-resolution.ts +89 -0
  442. package/src/vite/utils/prerender-utils.ts +249 -0
  443. package/src/vite/utils/shared-utils.ts +269 -0
  444. package/CLAUDE.md +0 -43
  445. package/dist/vite/index.named-routes.gen.ts +0 -103
  446. package/src/browser/lru-cache.ts +0 -69
  447. package/src/browser/react/use-client-cache.ts +0 -56
  448. package/src/browser/request-controller.ts +0 -164
  449. package/src/browser/shallow.ts +0 -35
  450. package/src/cache/memory-store.ts +0 -253
  451. package/src/handles/index.ts +0 -6
  452. package/src/href-context.ts +0 -33
  453. package/src/network-error-thrower.tsx +0 -21
  454. package/src/router.gen.ts +0 -6
  455. package/src/static-handler.gen.ts +0 -5
  456. package/src/urls.gen.ts +0 -8
  457. package/src/vite/expose-internal-ids.ts +0 -1167
  458. package/src/vite/package-resolution.ts +0 -125
  459. package/src/vite/virtual-entries.ts +0 -114
  460. /package/src/vite/{version.d.ts → plugins/version.d.ts} +0 -0
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Cloudflare custom-spans integration.
3
+ *
4
+ * Bridges the router's performance phases (request, middleware, action,
5
+ * loaders, handler, render, ssr) onto Cloudflare Workers custom spans so they show
6
+ * up in the trace waterfall and OpenTelemetry exports next to the platform's
7
+ * automatic spans (KV reads, D1 queries, fetch calls), with correct nesting.
8
+ *
9
+ * Usage (Cloudflare preset only):
10
+ *
11
+ * import { createRouter } from "@rangojs/router";
12
+ * import { createCloudflareTracing } from "@rangojs/router/cloudflare";
13
+ *
14
+ * export const router = createRouter({
15
+ * tracing: createCloudflareTracing(),
16
+ * });
17
+ *
18
+ * The runner reads `executionContext.tracing` (the same object as
19
+ * `import { tracing } from "cloudflare:workers"`) at call time. It is therefore
20
+ * dependency-free: no `cloudflare:workers` import and no hard dependency on
21
+ * `@cloudflare/workers-types`, matching the convention in cache/cf. When the
22
+ * worker is not running on a tracing-enabled Cloudflare runtime — Node, dev
23
+ * without a tracing destination, an older runtime — `executionContext.tracing`
24
+ * is undefined and every span call falls through to the work directly, so the
25
+ * request behaves exactly as if tracing were off. Whether spans are actually
26
+ * recorded is governed by the `observability`/tracing block in wrangler config.
27
+ *
28
+ * Span duration note: enterSpan ends a span when its callback's returned value
29
+ * (or promise) settles. For the streaming phases (request/render/ssr) that is at
30
+ * stream CONSTRUCTION, not body-drain. Instrumentation is best-effort and never
31
+ * wraps or buffers the response body, so it cannot regress streaming or latency.
32
+ * A loader/Suspense child that resolves mid-stream therefore keeps a rango.loader
33
+ * span that can extend past its render parent — overlapping spans are valid. Uses
34
+ * only the typed enterSpan API; spans bound work up to stream-handoff, matching
35
+ * the co-emitted perf metric.
36
+ */
37
+
38
+ import { _getRequestContext } from "../server/request-context.js";
39
+ import {
40
+ type RouterTracingConfig,
41
+ type SpanRunner,
42
+ type TracePhaseToggles,
43
+ NOOP_TRACE_SPAN,
44
+ } from "../router/tracing.js";
45
+
46
+ /**
47
+ * Minimal local view of Cloudflare's `Span`. Declared here (not imported from
48
+ * `@cloudflare/workers-types`) to avoid a hard type dependency.
49
+ */
50
+ interface CloudflareSpan {
51
+ readonly isTraced: boolean;
52
+ setAttribute(key: string, value?: boolean | number | string): void;
53
+ }
54
+
55
+ /** Minimal local view of Cloudflare's `Tracing` (only enterSpan is used). */
56
+ interface CloudflareTracing {
57
+ enterSpan<T>(name: string, callback: (span: CloudflareSpan) => T): T;
58
+ }
59
+
60
+ /** Options for createCloudflareTracing. */
61
+ export interface CloudflareTracingOptions {
62
+ /** Master switch. Defaults to true. */
63
+ enabled?: boolean;
64
+ /** Per-phase span toggles. Omitted phases default to enabled. */
65
+ spans?: TracePhaseToggles;
66
+ }
67
+
68
+ /**
69
+ * Resolve the per-request Cloudflare tracer from the active execution context.
70
+ * Returns undefined off-Cloudflare or when tracing is not enabled for the
71
+ * worker, in which case the caller runs the work without a span.
72
+ */
73
+ function getRequestTracer(): CloudflareTracing | undefined {
74
+ const executionContext = _getRequestContext()?.executionContext as
75
+ | { tracing?: CloudflareTracing }
76
+ | undefined;
77
+ const tracing = executionContext?.tracing;
78
+ return tracing && typeof tracing.enterSpan === "function"
79
+ ? tracing
80
+ : undefined;
81
+ }
82
+
83
+ const cloudflareSpanRunner: SpanRunner = (name, fn) => {
84
+ const tracer = getRequestTracer();
85
+ if (!tracer) return fn(NOOP_TRACE_SPAN);
86
+ // enterSpan runs the callback now and ends the span when its return value
87
+ // (or returned promise) settles; spans nest by JS async context. fn's param
88
+ // is the narrower TraceSpan, so the wider CloudflareSpan satisfies it directly.
89
+ return tracer.enterSpan(name, fn);
90
+ };
91
+
92
+ /**
93
+ * Create the tracing config for a Cloudflare router. Pass the result to
94
+ * `createRouter({ tracing })`. Spans are emitted for the request, middleware,
95
+ * action, loaders, handler, render, and ssr phases; pass `spans` to turn
96
+ * individual phases off.
97
+ *
98
+ * @see createOTelTracing (`@rangojs/router`) for the same slot on any platform
99
+ * with an OpenTelemetry SDK.
100
+ */
101
+ export function createCloudflareTracing(
102
+ options: CloudflareTracingOptions = {},
103
+ ): RouterTracingConfig {
104
+ return {
105
+ runner: cloudflareSpanRunner,
106
+ enabled: options.enabled ?? true,
107
+ spans: options.spans,
108
+ };
109
+ }
@@ -5,6 +5,7 @@
5
5
  */
6
6
 
7
7
  import type { ComponentType } from "react";
8
+ import { isUnderTestRunner } from "./runtime-env.js";
8
9
 
9
10
  /**
10
11
  * Symbol used by React to mark client component references.
@@ -33,7 +34,7 @@ const CLIENT_REFERENCE = Symbol.for("react.client.reference");
33
34
  * ```
34
35
  */
35
36
  export function isClientComponent(
36
- component: ComponentType<unknown> | unknown
37
+ component: ComponentType<unknown> | unknown,
37
38
  ): boolean {
38
39
  if (typeof component !== "function") {
39
40
  return false;
@@ -48,25 +49,43 @@ export function isClientComponent(
48
49
  *
49
50
  * @param component - The component to check
50
51
  * @param name - Name to use in error message (e.g., "document")
52
+ * @param opts.allowServerInTest - When true AND running under a test runner
53
+ * (`isUnderTestRunner()`), relax ONLY the "use client" requirement: a server
54
+ * component is accepted. The plugin's "use client" transform does not run in a
55
+ * bare unit test, so a real exported `document` (almost every app sets one) has
56
+ * no client marker and would otherwise throw at `createRouter`, blocking
57
+ * `dispatch`/`assertGeneratedRoutesMatch` against the real router. The document
58
+ * reference is irrelevant to those (no Flight render). The "not a JSX element"
59
+ * guard still fires, and a real dev/build still throws (mirrors the runtime
60
+ * fallback-id gating in handle.ts/loader.ts).
51
61
  * @throws Error if the component is not a client component
52
62
  */
53
63
  export function assertClientComponent(
54
64
  component: ComponentType<unknown> | unknown,
55
- name: string
65
+ name: string,
66
+ opts?: { allowServerInTest?: boolean },
56
67
  ): asserts component is ComponentType<unknown> {
57
68
  if (typeof component !== "function") {
58
69
  throw new Error(
59
70
  `${name} must be a client component function with "use client" directive. ` +
60
71
  `Make sure to pass the component itself, not a JSX element: ` +
61
- `${name}: My${capitalize(name)} (correct) vs ${name}: <My${capitalize(name)} /> (incorrect)`
72
+ `${name}: My${capitalize(name)} (correct) vs ${name}: <My${capitalize(name)} /> (incorrect)`,
62
73
  );
63
74
  }
64
75
 
76
+ // Under a test runner the "use client" transform did not run, so a real
77
+ // server-rendered `document` has no client marker; accept it (the reference is
78
+ // never serialized in dispatch/route-map checks). Outside a test runner this
79
+ // still throws — the build-time safety net is preserved.
80
+ if (opts?.allowServerInTest && isUnderTestRunner()) {
81
+ return;
82
+ }
83
+
65
84
  if (!isClientComponent(component)) {
66
85
  throw new Error(
67
86
  `${name} must be a client component with "use client" directive at the top of the file. ` +
68
87
  `Server components cannot be used as the ${name} because their function reference ` +
69
- `cannot be serialized in the RSC payload. Add "use client" to your ${name} file.`
88
+ `cannot be serialized in the RSC payload. Add "use client" to your ${name} file.`,
70
89
  );
71
90
  }
72
91
  }
@@ -2,22 +2,32 @@
2
2
 
3
3
  import type { ReactNode, ReactElement } from "react";
4
4
  import { MetaTags } from "../handles/MetaTags.js";
5
+ import { Scripts } from "../handles/Scripts.js";
5
6
 
6
7
  /**
7
8
  * Default document component that provides a basic HTML structure.
8
9
  * Used when no custom document is provided to createRouter.
9
- * Includes MetaTags for automatic charset, viewport, and route meta support.
10
+ * Includes MetaTags for automatic charset, viewport, and route meta support,
11
+ * and Scripts (head + body sites) so the Script handle works out of the box.
10
12
  *
11
13
  * Uses suppressHydrationWarning on <html> because the theme script
12
14
  * may modify class/style attributes before React hydrates.
13
15
  */
14
- export function DefaultDocument({ children }: { children: ReactNode }): ReactElement {
16
+ export function DefaultDocument({
17
+ children,
18
+ }: {
19
+ children: ReactNode;
20
+ }): ReactElement {
15
21
  return (
16
22
  <html lang="en" suppressHydrationWarning>
17
23
  <head>
18
24
  <MetaTags />
25
+ <Scripts />
19
26
  </head>
20
- <body>{children}</body>
27
+ <body>
28
+ <Scripts position="body" />
29
+ {children}
30
+ </body>
21
31
  </html>
22
32
  );
23
33
  }
@@ -0,0 +1,168 @@
1
+ /**
2
+ * Typed context variables for ctx.set() / ctx.get().
3
+ *
4
+ * createVar<T>() produces a typed token that handlers set and layouts/middleware
5
+ * read. The token carries a unique Symbol used as the property key on the
6
+ * per-request variables object — no build-time processing, no IDs.
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * import { createVar } from "@rangojs/router";
11
+ *
12
+ * interface PaginationData { current: number; total: number }
13
+ * export const Pagination = createVar<PaginationData>();
14
+ *
15
+ * // Non-cacheable var — ctx.get(User) throws inside a cache() boundary
16
+ * export const User = createVar<UserData>({ cache: false });
17
+ *
18
+ * // handler
19
+ * ctx.set(Pagination, { current: 1, total: 4 });
20
+ *
21
+ * // layout
22
+ * const pg = ctx.get(Pagination); // PaginationData | undefined
23
+ * ```
24
+ */
25
+
26
+ export interface ContextVar<T> {
27
+ readonly __brand: "context-var";
28
+ readonly key: symbol;
29
+ /** When false, ctx.get(var) throws inside a cache() boundary. */
30
+ readonly cache: boolean;
31
+ /** Phantom field to carry the type parameter. Never set at runtime. */
32
+ readonly __type?: T;
33
+ }
34
+
35
+ export interface ContextVarOptions {
36
+ /**
37
+ * When false, marks this variable as non-cacheable.
38
+ * Reading this var with ctx.get() inside a cache() boundary throws. Use for
39
+ * inherently request-specific data (user sessions, auth tokens, etc.) that
40
+ * must never be baked into cached segments.
41
+ *
42
+ * @default true
43
+ */
44
+ cache?: boolean;
45
+ }
46
+
47
+ /**
48
+ * Create a typed context variable token.
49
+ *
50
+ * The returned object is used with ctx.set(token, value) and ctx.get(token)
51
+ * for compile-time-checked data flow between handlers, layouts, and middleware.
52
+ */
53
+ export function createVar<T>(options?: ContextVarOptions): ContextVar<T> {
54
+ return {
55
+ __brand: "context-var" as const,
56
+ key: Symbol(),
57
+ cache: options?.cache !== false,
58
+ };
59
+ }
60
+
61
+ /**
62
+ * Type guard: is the value a ContextVar token?
63
+ */
64
+ export function isContextVar(value: unknown): value is ContextVar<unknown> {
65
+ return (
66
+ typeof value === "object" &&
67
+ value !== null &&
68
+ "__brand" in value &&
69
+ (value as { __brand: unknown }).__brand === "context-var"
70
+ );
71
+ }
72
+
73
+ /**
74
+ * Does a variables object hold any entries? Counts both string keys and the
75
+ * symbol-keyed entries (context vars are stored under symbols), so an object
76
+ * carrying only symbol-keyed vars is still reported as non-empty.
77
+ */
78
+ export function hasContextVars(variables: object): boolean {
79
+ return (
80
+ Object.keys(variables).length > 0 ||
81
+ Object.getOwnPropertySymbols(variables).length > 0
82
+ );
83
+ }
84
+
85
+ /**
86
+ * Symbol used as a Set stored on the variables object to track
87
+ * which keys hold non-cacheable values (from write-level { cache: false }).
88
+ */
89
+ const NON_CACHEABLE_KEYS: unique symbol = Symbol.for(
90
+ "rango:non-cacheable-keys",
91
+ );
92
+
93
+ function getNonCacheableKeys(variables: any): Set<string | symbol> {
94
+ if (!variables[NON_CACHEABLE_KEYS]) {
95
+ variables[NON_CACHEABLE_KEYS] = new Set();
96
+ }
97
+ return variables[NON_CACHEABLE_KEYS];
98
+ }
99
+
100
+ /**
101
+ * Check if a variable value is non-cacheable (either var-level or write-level).
102
+ */
103
+ export function isNonCacheable(
104
+ variables: any,
105
+ keyOrVar: string | ContextVar<any>,
106
+ ): boolean {
107
+ if (typeof keyOrVar !== "string" && !keyOrVar.cache) {
108
+ return true; // var-level policy
109
+ }
110
+ const key = typeof keyOrVar === "string" ? keyOrVar : keyOrVar.key;
111
+ const set = variables[NON_CACHEABLE_KEYS] as Set<string | symbol> | undefined;
112
+ return set?.has(key) ?? false; // write-level policy
113
+ }
114
+
115
+ /**
116
+ * Read a variable from the variables store.
117
+ * Accepts either a string key (legacy) or a ContextVar token (typed).
118
+ */
119
+ export function contextGet(
120
+ variables: any,
121
+ keyOrVar: string | ContextVar<any>,
122
+ ): any {
123
+ if (typeof keyOrVar === "string") return variables[keyOrVar];
124
+ return variables[keyOrVar.key];
125
+ }
126
+
127
+ /** Keys that must never be used as string variable names */
128
+ const FORBIDDEN_KEYS = new Set(["__proto__", "constructor", "prototype"]);
129
+
130
+ export interface ContextSetOptions {
131
+ /**
132
+ * When false, marks this specific write as non-cacheable.
133
+ * "Least cacheable wins" — if either the var definition or this option
134
+ * says cache: false, the value is non-cacheable.
135
+ *
136
+ * @default true (inherits from createVar)
137
+ */
138
+ cache?: boolean;
139
+ }
140
+
141
+ /**
142
+ * Write a variable to the variables store.
143
+ * Accepts either a string key (legacy) or a ContextVar token (typed).
144
+ */
145
+ export function contextSet(
146
+ variables: any,
147
+ keyOrVar: string | ContextVar<any>,
148
+ value: any,
149
+ options?: ContextSetOptions,
150
+ ): void {
151
+ if (typeof keyOrVar === "string") {
152
+ if (FORBIDDEN_KEYS.has(keyOrVar)) {
153
+ throw new Error(
154
+ `ctx.set(): "${keyOrVar}" is a reserved key and cannot be used as a variable name.`,
155
+ );
156
+ }
157
+ variables[keyOrVar] = value;
158
+ if (options?.cache === false) {
159
+ getNonCacheableKeys(variables).add(keyOrVar);
160
+ }
161
+ } else {
162
+ variables[keyOrVar.key] = value;
163
+ // Track write-level non-cacheable (var-level is checked via keyOrVar.cache)
164
+ if (options?.cache === false) {
165
+ getNonCacheableKeys(variables).add(keyOrVar.key);
166
+ }
167
+ }
168
+ }
package/src/debug.ts CHANGED
@@ -2,7 +2,7 @@
2
2
  * Debug utilities for manifest inspection and comparison
3
3
  */
4
4
 
5
- import type { EntryData } from "./server/context";
5
+ import { getParallelSlotCount, type EntryData } from "./server/context";
6
6
 
7
7
  /**
8
8
  * Serialized entry for debug output
@@ -34,7 +34,7 @@ export interface SerializedManifest {
34
34
  * Serialize a manifest Map into a JSON-friendly structure
35
35
  */
36
36
  export function serializeManifest(
37
- manifest: Map<string, EntryData>
37
+ manifest: Map<string, EntryData>,
38
38
  ): SerializedManifest {
39
39
  const routes: Record<string, SerializedEntry> = {};
40
40
  const layouts: Record<string, SerializedEntry> = {};
@@ -64,7 +64,7 @@ export function serializeManifest(
64
64
  hasLoader: entry.loader?.length > 0,
65
65
  hasMiddleware: entry.middleware?.length > 0,
66
66
  hasErrorBoundary: entry.errorBoundary?.length > 0,
67
- parallelCount: entry.parallel?.length ?? 0,
67
+ parallelCount: getParallelSlotCount(entry.parallel),
68
68
  interceptCount: entry.intercept?.length ?? 0,
69
69
  };
70
70
 
@@ -92,7 +92,7 @@ export function serializeManifest(
92
92
  */
93
93
  export function compareManifests(
94
94
  oldManifest: SerializedManifest,
95
- newManifest: SerializedManifest
95
+ newManifest: SerializedManifest,
96
96
  ): {
97
97
  addedRoutes: string[];
98
98
  removedRoutes: string[];
@@ -113,10 +113,20 @@ export function compareManifests(
113
113
  } {
114
114
  const addedRoutes: string[] = [];
115
115
  const removedRoutes: string[] = [];
116
- const changedRoutes: Array<{ key: string; field: string; old: any; new: any }> = [];
116
+ const changedRoutes: Array<{
117
+ key: string;
118
+ field: string;
119
+ old: any;
120
+ new: any;
121
+ }> = [];
117
122
  const addedLayouts: string[] = [];
118
123
  const removedLayouts: string[] = [];
119
- const changedLayouts: Array<{ key: string; field: string; old: any; new: any }> = [];
124
+ const changedLayouts: Array<{
125
+ key: string;
126
+ field: string;
127
+ old: any;
128
+ new: any;
129
+ }> = [];
120
130
 
121
131
  // Compare routes
122
132
  const oldRouteKeys = new Set(Object.keys(oldManifest.routes));
@@ -191,7 +201,7 @@ export function compareManifests(
191
201
  * Format manifest diff as a human-readable string
192
202
  */
193
203
  export function formatManifestDiff(
194
- diff: ReturnType<typeof compareManifests>
204
+ diff: ReturnType<typeof compareManifests>,
195
205
  ): string {
196
206
  const lines: string[] = [];
197
207
 
@@ -208,7 +218,7 @@ export function formatManifestDiff(
208
218
  if (diff.changedRoutes.length > 0) {
209
219
  lines.push("Changed routes:");
210
220
  diff.changedRoutes.forEach((c) =>
211
- lines.push(` ~ ${c.key}.${c.field}: ${c.old} -> ${c.new}`)
221
+ lines.push(` ~ ${c.key}.${c.field}: ${c.old} -> ${c.new}`),
212
222
  );
213
223
  }
214
224
 
@@ -225,7 +235,7 @@ export function formatManifestDiff(
225
235
  if (diff.changedLayouts.length > 0) {
226
236
  lines.push("Changed layouts:");
227
237
  diff.changedLayouts.forEach((c) =>
228
- lines.push(` ~ ${c.key}.${c.field}: ${c.old} -> ${c.new}`)
238
+ lines.push(` ~ ${c.key}.${c.field}: ${c.old} -> ${c.new}`),
229
239
  );
230
240
  }
231
241
 
@@ -0,0 +1,52 @@
1
+ import type { ReactNode } from "react";
2
+ import { isLoaderDataResult } from "./types.js";
3
+
4
+ // Shared by segment-system (server) and LoaderResolver (client) so the
5
+ // legacy/ok/error-fallback/throw decode of resolved loader values lives once.
6
+ // Last failing loader wins errorFallback; an error without a fallback throws.
7
+ export function decodeLoaderResults(
8
+ resolvedData: any[],
9
+ loaderIds: string[],
10
+ ): { loaderData: Record<string, any>; errorFallback: ReactNode } {
11
+ const loaderData: Record<string, any> = {};
12
+ let errorFallback: ReactNode = null;
13
+
14
+ for (let i = 0; i < loaderIds.length; i++) {
15
+ const id = loaderIds[i];
16
+ const result = resolvedData[i];
17
+
18
+ if (!isLoaderDataResult(result)) {
19
+ loaderData[id] = result;
20
+ continue;
21
+ }
22
+
23
+ if (result.ok) {
24
+ loaderData[id] = result.data;
25
+ continue;
26
+ }
27
+
28
+ // null/undefined is the producer's ONLY "no boundary found" sentinel
29
+ // (loader-resolution.ts sets fallback: null for the no-boundary and the
30
+ // fallback-render-threw cases). A matched boundary's rendered ReactNode can
31
+ // legitimately be falsy (0, "", false), so test for null explicitly rather
32
+ // than truthiness, otherwise a valid falsy fallback is discarded and the
33
+ // original loader error is rethrown.
34
+ if (result.fallback != null) {
35
+ errorFallback = result.fallback;
36
+ } else {
37
+ // No boundary: rethrow preserving the ErrorInfo identity (name/stack/
38
+ // code/cause) instead of a stripped generic Error.
39
+ const info = result.error;
40
+ const err = new Error(
41
+ info.message,
42
+ info.cause !== undefined ? { cause: info.cause } : undefined,
43
+ );
44
+ if (info.name) err.name = info.name;
45
+ if (info.stack) err.stack = info.stack;
46
+ if (info.code !== undefined) (err as { code?: string }).code = info.code;
47
+ throw err;
48
+ }
49
+ }
50
+
51
+ return { loaderData, errorFallback };
52
+ }