@rangojs/router 0.0.0-experimental.1878aa49 → 0.0.0-experimental.190fb30d

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 (443) hide show
  1. package/AGENTS.md +8 -4
  2. package/README.md +303 -741
  3. package/dist/bin/rango.js +605 -146
  4. package/dist/testing/vitest.js +82 -0
  5. package/dist/vite/index.js +4117 -1211
  6. package/dist/vite/index.js.bak +5448 -0
  7. package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
  8. package/package.json +86 -15
  9. package/skills/api-client/SKILL.md +211 -0
  10. package/skills/breadcrumbs/SKILL.md +85 -6
  11. package/skills/bundle-analysis/SKILL.md +159 -0
  12. package/skills/cache-guide/SKILL.md +228 -33
  13. package/skills/caching/SKILL.md +336 -19
  14. package/skills/catalog.json +271 -0
  15. package/skills/comparison/SKILL.md +50 -0
  16. package/skills/comparison/agents/openai.yaml +4 -0
  17. package/skills/comparison/references/framework-comparison.md +837 -0
  18. package/skills/composability/SKILL.md +110 -4
  19. package/skills/css/SKILL.md +76 -0
  20. package/skills/debug-manifest/SKILL.md +5 -3
  21. package/skills/defer-hydration/SKILL.md +235 -0
  22. package/skills/document-cache/SKILL.md +87 -56
  23. package/skills/fonts/SKILL.md +1 -1
  24. package/skills/handler-use/SKILL.md +364 -0
  25. package/skills/hooks/SKILL.md +73 -691
  26. package/skills/hooks/data.md +273 -0
  27. package/skills/hooks/handle-and-actions.md +103 -0
  28. package/skills/hooks/navigation.md +110 -0
  29. package/skills/hooks/outlets.md +41 -0
  30. package/skills/hooks/state.md +228 -0
  31. package/skills/hooks/urls.md +135 -0
  32. package/skills/host-router/SKILL.md +129 -27
  33. package/skills/i18n/SKILL.md +276 -0
  34. package/skills/intercept/SKILL.md +94 -18
  35. package/skills/layout/SKILL.md +62 -19
  36. package/skills/links/SKILL.md +247 -17
  37. package/skills/loader/SKILL.md +248 -10
  38. package/skills/middleware/SKILL.md +57 -16
  39. package/skills/migrate-nextjs/SKILL.md +745 -0
  40. package/skills/migrate-react-router/SKILL.md +153 -0
  41. package/skills/migrate-react-router/cloudflare-workers.md +129 -0
  42. package/skills/migrate-react-router/component-migration.md +196 -0
  43. package/skills/migrate-react-router/data-and-actions.md +225 -0
  44. package/skills/migrate-react-router/route-mapping.md +271 -0
  45. package/skills/mime-routes/SKILL.md +29 -2
  46. package/skills/observability/SKILL.md +202 -0
  47. package/skills/parallel/SKILL.md +99 -10
  48. package/skills/ppr/SKILL.md +618 -0
  49. package/skills/prerender/SKILL.md +178 -124
  50. package/skills/rango/SKILL.md +318 -24
  51. package/skills/react-compiler/SKILL.md +168 -0
  52. package/skills/response-routes/SKILL.md +138 -49
  53. package/skills/route/SKILL.md +141 -9
  54. package/skills/router-setup/SKILL.md +44 -9
  55. package/skills/scripts/SKILL.md +179 -0
  56. package/skills/server-actions/SKILL.md +776 -0
  57. package/skills/shell-manifest/SKILL.md +185 -0
  58. package/skills/streams-and-websockets/SKILL.md +283 -0
  59. package/skills/tailwind/SKILL.md +28 -4
  60. package/skills/testing/SKILL.md +130 -0
  61. package/skills/testing/bindings.md +103 -0
  62. package/skills/testing/cache-prerender.md +127 -0
  63. package/skills/testing/client-components.md +124 -0
  64. package/skills/testing/e2e-parity.md +125 -0
  65. package/skills/testing/flight.md +91 -0
  66. package/skills/testing/handles.md +131 -0
  67. package/skills/testing/loader.md +128 -0
  68. package/skills/testing/middleware.md +99 -0
  69. package/skills/testing/render-handler.md +122 -0
  70. package/skills/testing/response-routes.md +95 -0
  71. package/skills/testing/reverse-and-types.md +85 -0
  72. package/skills/testing/server-actions.md +107 -0
  73. package/skills/testing/server-tree.md +128 -0
  74. package/skills/testing/setup.md +123 -0
  75. package/skills/theme/SKILL.md +1 -1
  76. package/skills/typesafety/SKILL.md +45 -626
  77. package/skills/typesafety/env-and-bindings.md +254 -0
  78. package/skills/typesafety/generated-files-and-cli.md +335 -0
  79. package/skills/typesafety/params-and-search.md +153 -0
  80. package/skills/typesafety/route-types.md +209 -0
  81. package/skills/use-cache/SKILL.md +74 -15
  82. package/skills/vercel/SKILL.md +128 -0
  83. package/skills/view-transitions/SKILL.md +337 -0
  84. package/src/__augment-tests__/augment.ts +81 -0
  85. package/src/__augment-tests__/augmented.check.ts +116 -0
  86. package/src/__internal.ts +1 -66
  87. package/src/browser/action-coordinator.ts +53 -36
  88. package/src/browser/action-fence.ts +47 -0
  89. package/src/browser/app-shell.ts +39 -0
  90. package/src/browser/connection-warmup.ts +134 -0
  91. package/src/browser/cookie-name.ts +140 -0
  92. package/src/browser/event-controller.ts +252 -158
  93. package/src/browser/history-state.ts +21 -0
  94. package/src/browser/index.ts +3 -3
  95. package/src/browser/invalidate-client-cache.ts +52 -0
  96. package/src/browser/logging.ts +28 -0
  97. package/src/browser/merge-segment-loaders.ts +6 -4
  98. package/src/browser/navigation-bridge.ts +115 -27
  99. package/src/browser/navigation-client.ts +163 -70
  100. package/src/browser/navigation-store-handle.ts +38 -0
  101. package/src/browser/navigation-store.ts +161 -73
  102. package/src/browser/navigation-transaction.ts +9 -59
  103. package/src/browser/network-error-handler.ts +34 -7
  104. package/src/browser/partial-update.ts +186 -135
  105. package/src/browser/prefetch/cache.ts +246 -71
  106. package/src/browser/prefetch/fetch.ts +350 -38
  107. package/src/browser/prefetch/queue.ts +61 -12
  108. package/src/browser/rango-state.ts +158 -76
  109. package/src/browser/react/Link.tsx +70 -9
  110. package/src/browser/react/NavigationProvider.tsx +202 -120
  111. package/src/browser/react/ScrollRestoration.tsx +10 -6
  112. package/src/browser/react/filter-segment-order.ts +66 -7
  113. package/src/browser/react/index.ts +0 -48
  114. package/src/browser/react/location-state-shared.ts +178 -8
  115. package/src/browser/react/location-state.ts +39 -14
  116. package/src/browser/react/use-action.ts +6 -15
  117. package/src/browser/react/use-handle.ts +23 -69
  118. package/src/browser/react/use-href.tsx +8 -1
  119. package/src/browser/react/use-link-status.ts +33 -8
  120. package/src/browser/react/use-navigation.ts +32 -7
  121. package/src/browser/react/use-params.ts +20 -10
  122. package/src/browser/react/use-reverse.ts +106 -0
  123. package/src/browser/react/use-router.ts +25 -3
  124. package/src/browser/react/use-search-params.ts +0 -5
  125. package/src/browser/react/use-segments.ts +11 -21
  126. package/src/browser/response-adapter.ts +99 -8
  127. package/src/browser/rsc-router.tsx +145 -28
  128. package/src/browser/scroll-restoration.ts +44 -27
  129. package/src/browser/segment-reconciler.ts +44 -12
  130. package/src/browser/segment-structure-assert.ts +2 -2
  131. package/src/browser/server-action-bridge.ts +236 -65
  132. package/src/browser/types.ts +102 -9
  133. package/src/browser/validate-redirect-origin.ts +43 -16
  134. package/src/build/collect-fallback-refs.ts +107 -0
  135. package/src/build/generate-manifest.ts +205 -159
  136. package/src/build/generate-route-types.ts +3 -1
  137. package/src/build/index.ts +11 -3
  138. package/src/build/prefix-tree-utils.ts +123 -0
  139. package/src/build/route-trie.ts +198 -41
  140. package/src/build/route-types/ast-route-extraction.ts +15 -8
  141. package/src/build/route-types/codegen.ts +16 -5
  142. package/src/build/route-types/include-resolution.ts +456 -62
  143. package/src/build/route-types/param-extraction.ts +6 -3
  144. package/src/build/route-types/per-module-writer.ts +22 -6
  145. package/src/build/route-types/router-processing.ts +128 -51
  146. package/src/build/route-types/scan-filter.ts +9 -2
  147. package/src/build/route-types/source-scan.ts +216 -0
  148. package/src/build/runtime-discovery.ts +13 -21
  149. package/src/cache/cache-error.ts +104 -0
  150. package/src/cache/cache-key-utils.ts +58 -13
  151. package/src/cache/cache-policy.ts +108 -34
  152. package/src/cache/cache-runtime.ts +454 -101
  153. package/src/cache/cache-scope.ts +187 -96
  154. package/src/cache/cache-tag.ts +149 -0
  155. package/src/cache/cf/cf-base64.ts +33 -0
  156. package/src/cache/cf/cf-cache-constants.ts +127 -0
  157. package/src/cache/cf/cf-cache-store.ts +2206 -372
  158. package/src/cache/cf/cf-cache-types.ts +349 -0
  159. package/src/cache/cf/cf-kv-utils.ts +46 -0
  160. package/src/cache/cf/cf-tag-marker-memo.ts +105 -0
  161. package/src/cache/cf/index.ts +6 -16
  162. package/src/cache/document-cache.ts +126 -41
  163. package/src/cache/handle-snapshot.ts +70 -0
  164. package/src/cache/index.ts +23 -20
  165. package/src/cache/memory-segment-store.ts +243 -37
  166. package/src/cache/profile-registry.ts +46 -31
  167. package/src/cache/read-through-swr.ts +56 -12
  168. package/src/cache/segment-codec.ts +13 -21
  169. package/src/cache/shell-snapshot.ts +417 -0
  170. package/src/cache/tag-invalidation.ts +230 -0
  171. package/src/cache/types.ts +194 -99
  172. package/src/cache/vercel/index.ts +11 -0
  173. package/src/cache/vercel/vercel-cache-store.ts +1132 -0
  174. package/src/client.rsc.tsx +41 -21
  175. package/src/client.tsx +116 -290
  176. package/src/cloudflare/index.ts +11 -0
  177. package/src/cloudflare/tracing.ts +108 -0
  178. package/src/component-utils.ts +19 -0
  179. package/src/components/DefaultDocument.tsx +8 -2
  180. package/src/context-var.ts +18 -6
  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 +30 -4
  186. package/src/escape-script.ts +52 -0
  187. package/src/handle.ts +104 -34
  188. package/src/handles/MetaTags.tsx +24 -53
  189. package/src/handles/Scripts.tsx +183 -0
  190. package/src/handles/breadcrumbs.ts +35 -8
  191. package/src/handles/deferred-resolution.ts +127 -0
  192. package/src/handles/is-thenable.ts +18 -0
  193. package/src/handles/meta.ts +14 -40
  194. package/src/handles/script.ts +244 -0
  195. package/src/host/cookie-handler.ts +9 -60
  196. package/src/host/errors.ts +13 -22
  197. package/src/host/index.ts +9 -2
  198. package/src/host/pattern-matcher.ts +23 -52
  199. package/src/host/router.ts +107 -99
  200. package/src/host/testing.ts +40 -27
  201. package/src/host/types.ts +37 -4
  202. package/src/host/utils.ts +1 -1
  203. package/src/href-client.ts +137 -22
  204. package/src/index.rsc.ts +100 -13
  205. package/src/index.ts +143 -19
  206. package/src/internal-debug.ts +11 -10
  207. package/src/loader-store.ts +500 -0
  208. package/src/loader.rsc.ts +20 -13
  209. package/src/loader.ts +12 -11
  210. package/src/missing-id-error.ts +68 -0
  211. package/src/outlet-context.ts +1 -1
  212. package/src/outlet-provider.tsx +1 -5
  213. package/src/prerender/param-hash.ts +16 -16
  214. package/src/prerender/store.ts +37 -41
  215. package/src/prerender.ts +215 -86
  216. package/src/redirect-origin.ts +114 -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 +65 -15
  221. package/src/root-error-boundary.tsx +1 -19
  222. package/src/route-content-wrapper.tsx +19 -77
  223. package/src/route-definition/dsl-helpers.ts +461 -304
  224. package/src/route-definition/helper-factories.ts +28 -140
  225. package/src/route-definition/helpers-types.ts +147 -72
  226. package/src/route-definition/index.ts +4 -2
  227. package/src/route-definition/redirect.ts +44 -11
  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-map-builder.ts +41 -20
  231. package/src/route-types.ts +37 -46
  232. package/src/router/basename.ts +14 -0
  233. package/src/router/content-negotiation.ts +164 -17
  234. package/src/router/error-handling.ts +45 -18
  235. package/src/router/find-match.ts +129 -30
  236. package/src/router/handler-context.ts +47 -22
  237. package/src/router/instrument.ts +355 -0
  238. package/src/router/intercept-resolution.ts +39 -20
  239. package/src/router/lazy-includes.ts +85 -62
  240. package/src/router/loader-resolution.ts +277 -54
  241. package/src/router/logging.ts +0 -6
  242. package/src/router/manifest.ts +97 -57
  243. package/src/router/match-api.ts +178 -218
  244. package/src/router/match-context.ts +0 -22
  245. package/src/router/match-handlers.ts +211 -165
  246. package/src/router/match-middleware/background-revalidation.ts +40 -24
  247. package/src/router/match-middleware/cache-lookup.ts +168 -277
  248. package/src/router/match-middleware/cache-store.ts +64 -52
  249. package/src/router/match-middleware/intercept-resolution.ts +0 -22
  250. package/src/router/match-middleware/segment-resolution.ts +45 -14
  251. package/src/router/match-pipelines.ts +1 -42
  252. package/src/router/match-result.ts +113 -45
  253. package/src/router/metrics.ts +0 -34
  254. package/src/router/middleware-types.ts +7 -134
  255. package/src/router/middleware.ts +298 -172
  256. package/src/router/navigation-snapshot.ts +133 -0
  257. package/src/router/params-util.ts +23 -0
  258. package/src/router/parse-pattern.ts +115 -0
  259. package/src/router/pattern-matching.ts +181 -150
  260. package/src/router/prefetch-cache-ttl.ts +51 -0
  261. package/src/router/prefetch-limits.ts +37 -0
  262. package/src/router/prerender-match.ts +201 -56
  263. package/src/router/preview-match.ts +35 -103
  264. package/src/router/request-classification.ts +291 -0
  265. package/src/router/revalidation.ts +123 -73
  266. package/src/router/route-snapshot.ts +256 -0
  267. package/src/router/router-context.ts +6 -29
  268. package/src/router/router-interfaces.ts +125 -35
  269. package/src/router/router-options.ts +166 -5
  270. package/src/router/router-registry.ts +2 -5
  271. package/src/router/segment-resolution/fresh.ts +188 -86
  272. package/src/router/segment-resolution/helpers.ts +86 -6
  273. package/src/router/segment-resolution/loader-cache.ts +155 -39
  274. package/src/router/segment-resolution/loader-mask.ts +67 -0
  275. package/src/router/segment-resolution/loader-snapshot.ts +308 -0
  276. package/src/router/segment-resolution/revalidation.ts +344 -319
  277. package/src/router/segment-resolution/static-store.ts +19 -5
  278. package/src/router/segment-resolution/streamed-handler-telemetry.ts +52 -0
  279. package/src/router/segment-resolution/view-transition-default.ts +56 -0
  280. package/src/router/segment-resolution.ts +5 -1
  281. package/src/router/segment-wrappers.ts +6 -5
  282. package/src/router/state-cookie-name.ts +33 -0
  283. package/src/router/substitute-pattern-params.ts +75 -0
  284. package/src/router/telemetry-otel.ts +160 -200
  285. package/src/router/telemetry.ts +105 -20
  286. package/src/router/timeout.ts +0 -20
  287. package/src/router/tracing.ts +215 -0
  288. package/src/router/trie-matching.ts +171 -59
  289. package/src/router/types.ts +9 -63
  290. package/src/router/url-params.ts +57 -0
  291. package/src/router.ts +170 -72
  292. package/src/rsc/full-payload.ts +70 -0
  293. package/src/rsc/handler-context.ts +3 -2
  294. package/src/rsc/handler.ts +678 -513
  295. package/src/rsc/helpers.ts +168 -46
  296. package/src/rsc/index.ts +2 -5
  297. package/src/rsc/json-route-result.ts +38 -0
  298. package/src/rsc/loader-fetch.ts +127 -31
  299. package/src/rsc/manifest-init.ts +29 -42
  300. package/src/rsc/nonce.ts +10 -1
  301. package/src/rsc/origin-guard.ts +39 -25
  302. package/src/rsc/progressive-enhancement.ts +134 -15
  303. package/src/rsc/redirect-guard.ts +100 -0
  304. package/src/rsc/response-cache-serve.ts +238 -0
  305. package/src/rsc/response-error.ts +79 -12
  306. package/src/rsc/response-route-handler.ts +99 -189
  307. package/src/rsc/rsc-rendering.ts +497 -77
  308. package/src/rsc/runtime-warnings.ts +23 -10
  309. package/src/rsc/server-action.ts +286 -114
  310. package/src/rsc/shell-capture.ts +1166 -0
  311. package/src/rsc/shell-serve.ts +181 -0
  312. package/src/rsc/ssr-setup.ts +17 -1
  313. package/src/rsc/transition-gate.ts +89 -0
  314. package/src/rsc/types.ts +54 -5
  315. package/src/runtime-env.ts +18 -0
  316. package/src/search-params.ts +35 -30
  317. package/src/segment-content-promise.ts +67 -0
  318. package/src/segment-loader-promise.ts +167 -0
  319. package/src/segment-system.tsx +355 -204
  320. package/src/serialize.ts +243 -0
  321. package/src/server/context.ts +247 -54
  322. package/src/server/cookie-parse.ts +32 -0
  323. package/src/server/cookie-store.ts +152 -5
  324. package/src/server/handle-store.ts +40 -38
  325. package/src/server/loader-registry.ts +33 -42
  326. package/src/server/request-context.ts +502 -161
  327. package/src/ssr/index.tsx +467 -178
  328. package/src/ssr/inject-rsc-eager.ts +167 -0
  329. package/src/ssr/ssr-root.tsx +228 -0
  330. package/src/static-handler.ts +27 -18
  331. package/src/testing/cache-status.ts +162 -0
  332. package/src/testing/collect-handle.ts +46 -0
  333. package/src/testing/dispatch.ts +813 -0
  334. package/src/testing/dom.entry.ts +22 -0
  335. package/src/testing/e2e/fixture.ts +188 -0
  336. package/src/testing/e2e/index.ts +128 -0
  337. package/src/testing/e2e/matchers.ts +35 -0
  338. package/src/testing/e2e/page-helpers.ts +272 -0
  339. package/src/testing/e2e/parity.ts +387 -0
  340. package/src/testing/e2e/server.ts +195 -0
  341. package/src/testing/flight-matchers.ts +97 -0
  342. package/src/testing/flight-normalize.ts +11 -0
  343. package/src/testing/flight-runtime.d.ts +57 -0
  344. package/src/testing/flight-tree.ts +682 -0
  345. package/src/testing/flight.entry.ts +52 -0
  346. package/src/testing/flight.ts +257 -0
  347. package/src/testing/generated-routes.ts +199 -0
  348. package/src/testing/index.ts +105 -0
  349. package/src/testing/internal/context.ts +371 -0
  350. package/src/testing/internal/flight-client-globals.ts +30 -0
  351. package/src/testing/internal/seed-vars.ts +54 -0
  352. package/src/testing/render-handler.ts +357 -0
  353. package/src/testing/render-route.tsx +584 -0
  354. package/src/testing/run-loader.ts +385 -0
  355. package/src/testing/run-middleware.ts +205 -0
  356. package/src/testing/run-transition-when.ts +164 -0
  357. package/src/testing/vitest-stubs/cloudflare-email.ts +9 -0
  358. package/src/testing/vitest-stubs/cloudflare-workers.ts +21 -0
  359. package/src/testing/vitest-stubs/plugin-rsc.ts +16 -0
  360. package/src/testing/vitest-stubs/version.ts +5 -0
  361. package/src/testing/vitest.ts +305 -0
  362. package/src/theme/ThemeProvider.tsx +56 -84
  363. package/src/theme/ThemeScript.tsx +7 -9
  364. package/src/theme/constants.ts +52 -13
  365. package/src/theme/index.ts +0 -7
  366. package/src/theme/theme-context.ts +1 -5
  367. package/src/theme/theme-script.ts +22 -21
  368. package/src/theme/use-theme.ts +0 -3
  369. package/src/types/boundaries.ts +0 -35
  370. package/src/types/cache-types.ts +13 -4
  371. package/src/types/error-types.ts +30 -90
  372. package/src/types/global-namespace.ts +54 -41
  373. package/src/types/handler-context.ts +120 -62
  374. package/src/types/index.ts +3 -10
  375. package/src/types/loader-types.ts +40 -10
  376. package/src/types/request-scope.ts +112 -0
  377. package/src/types/route-config.ts +20 -52
  378. package/src/types/route-entry.ts +12 -7
  379. package/src/types/segments.ts +136 -15
  380. package/src/urls/include-helper.ts +40 -75
  381. package/src/urls/include-provider.ts +71 -0
  382. package/src/urls/index.ts +2 -11
  383. package/src/urls/path-helper-types.ts +102 -23
  384. package/src/urls/path-helper.ts +62 -111
  385. package/src/urls/pattern-types.ts +72 -19
  386. package/src/urls/response-types.ts +25 -22
  387. package/src/urls/type-extraction.ts +98 -154
  388. package/src/urls/urls-function.ts +1 -19
  389. package/src/use-loader.tsx +346 -89
  390. package/src/vercel/index.ts +11 -0
  391. package/src/vercel/tracing.ts +88 -0
  392. package/src/vite/debug.ts +185 -0
  393. package/src/vite/discovery/bundle-postprocess.ts +36 -38
  394. package/src/vite/discovery/dev-prerender-cache.ts +117 -0
  395. package/src/vite/discovery/discover-routers.ts +127 -86
  396. package/src/vite/discovery/discovery-errors.ts +255 -0
  397. package/src/vite/discovery/gate-state.ts +171 -0
  398. package/src/vite/discovery/prerender-collection.ts +214 -132
  399. package/src/vite/discovery/route-types-writer.ts +40 -84
  400. package/src/vite/discovery/self-gen-tracking.ts +27 -1
  401. package/src/vite/discovery/state.ts +57 -4
  402. package/src/vite/discovery/virtual-module-codegen.ts +14 -34
  403. package/src/vite/index.ts +15 -0
  404. package/src/vite/inject-client-debug.ts +88 -0
  405. package/src/vite/plugin-types.ts +261 -6
  406. package/src/vite/plugins/cjs-to-esm.ts +16 -19
  407. package/src/vite/plugins/client-ref-dedup.ts +16 -11
  408. package/src/vite/plugins/client-ref-hashing.ts +28 -15
  409. package/src/vite/plugins/cloudflare-protocol-loader-hook.d.mts +23 -0
  410. package/src/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
  411. package/src/vite/plugins/cloudflare-protocol-stub.ts +194 -0
  412. package/src/vite/plugins/expose-action-id.ts +48 -95
  413. package/src/vite/plugins/expose-id-utils.ts +96 -51
  414. package/src/vite/plugins/expose-ids/export-analysis.ts +101 -34
  415. package/src/vite/plugins/expose-ids/handler-transform.ts +15 -64
  416. package/src/vite/plugins/expose-ids/loader-transform.ts +14 -24
  417. package/src/vite/plugins/expose-ids/router-transform.ts +118 -29
  418. package/src/vite/plugins/expose-internal-ids.ts +553 -317
  419. package/src/vite/plugins/performance-tracks.ts +26 -25
  420. package/src/vite/plugins/refresh-cmd.ts +89 -27
  421. package/src/vite/plugins/use-cache-transform.ts +73 -83
  422. package/src/vite/plugins/vercel-output.ts +384 -0
  423. package/src/vite/plugins/version-injector.ts +40 -29
  424. package/src/vite/plugins/version-plugin.ts +37 -40
  425. package/src/vite/plugins/virtual-entries.ts +138 -27
  426. package/src/vite/rango.ts +238 -139
  427. package/src/vite/router-discovery.ts +1091 -159
  428. package/src/vite/utils/ast-handler-extract.ts +26 -35
  429. package/src/vite/utils/banner.ts +1 -1
  430. package/src/vite/utils/bundle-analysis.ts +10 -15
  431. package/src/vite/utils/client-chunks.ts +184 -0
  432. package/src/vite/utils/directive-prologue.ts +40 -0
  433. package/src/vite/utils/forward-user-plugins.ts +171 -0
  434. package/src/vite/utils/manifest-utils.ts +4 -59
  435. package/src/vite/utils/package-resolution.ts +20 -52
  436. package/src/vite/utils/prerender-utils.ts +98 -38
  437. package/src/vite/utils/shared-utils.ts +142 -43
  438. package/src/browser/action-response-classifier.ts +0 -99
  439. package/src/browser/react/use-client-cache.ts +0 -58
  440. package/src/browser/shallow.ts +0 -40
  441. package/src/handles/index.ts +0 -7
  442. package/src/network-error-thrower.tsx +0 -23
  443. package/src/router/middleware-cookies.ts +0 -55
@@ -5,8 +5,8 @@
5
5
  */
6
6
 
7
7
  import type { ReactNode } from "react";
8
- import { track } from "../server/context";
9
8
  import type { EntryData } from "../server/context";
9
+ import { observePhase, PHASES } from "./instrument.js";
10
10
  import { contextGet } from "../context-var.js";
11
11
  import type {
12
12
  ResolvedSegment,
@@ -19,11 +19,17 @@ import type {
19
19
  ErrorBoundaryFallbackProps,
20
20
  ErrorInfo,
21
21
  } from "../types";
22
- import type { LoaderRevalidationResult, ActionContext } from "./types";
23
- import { isHandle, type Handle } from "../handle.js";
24
- import type { HandleStore } from "../server/handle-store.js";
22
+ import { isHandle, collectHandleData, type Handle } from "../handle.js";
23
+ import { withDefer } from "../defer.js";
24
+ import { buildHandleSnapshot } from "../server/handle-store.js";
25
25
  import { getFetchableLoader } from "../server/fetchable-loader-store.js";
26
26
  import { _getRequestContext } from "../server/request-context.js";
27
+ import {
28
+ isInsideLoaderScope,
29
+ runInsideLoaderBodyScope,
30
+ isInsidePushCallbackScope,
31
+ runInsidePushCallbackScope,
32
+ } from "../server/context.js";
27
33
  import { debugLog } from "./logging.js";
28
34
 
29
35
  /**
@@ -65,7 +71,9 @@ export function wrapLoaderWithErrorHandling<T>(
65
71
  ) => ErrorInfo,
66
72
  onError?: LoaderErrorCallback,
67
73
  ): Promise<LoaderDataResult<T>> {
68
- // Extract loader name from segmentId (format: "M1L0D0.loaderName")
74
+ // Extract the trailing token from segmentId (format: "<shortCode>D<i>.<loaderId>").
75
+ // The token is the loader's $$id (hash#export in prod, pathfrag#export in dev),
76
+ // not a clean display name.
69
77
  const loaderName = segmentId.split(".").pop() || "unknown";
70
78
 
71
79
  return Promise.resolve(promise)
@@ -101,16 +109,40 @@ export function wrapLoaderWithErrorHandling<T>(
101
109
  };
102
110
  }
103
111
 
104
- // Render fallback on server
112
+ // Render fallback on server. The user ErrorBoundaryHandler may throw
113
+ // synchronously; if it does we must NOT let that rejection escape — the
114
+ // wrapped LoaderDataResult promise is contracted to never reject (see
115
+ // segment-resolution/fresh.ts `await Promise.all(...wrapped)`), and a
116
+ // rejection here would collapse the whole entry and discard healthy
117
+ // sibling loader data. On a fallback-render throw, fall back to the
118
+ // no-boundary result (fallback: null) so the client throws the ORIGINAL
119
+ // error, and the wrapped promise still resolves to a LoaderDataResult.
105
120
  let renderedFallback: ReactNode;
106
- if (typeof fallback === "function") {
107
- // ErrorBoundaryHandler - call with error info
108
- const props: ErrorBoundaryFallbackProps = {
121
+ try {
122
+ if (typeof fallback === "function") {
123
+ // ErrorBoundaryHandler - call with error info
124
+ const props: ErrorBoundaryFallbackProps = {
125
+ error: errorInfo,
126
+ };
127
+ renderedFallback = fallback(props);
128
+ } else {
129
+ renderedFallback = fallback;
130
+ }
131
+ } catch (fallbackError) {
132
+ debugLog("loader", "error boundary fallback render threw", {
133
+ segmentId,
134
+ message: errorInfo.message,
135
+ fallbackError:
136
+ fallbackError instanceof Error
137
+ ? fallbackError.message
138
+ : String(fallbackError),
139
+ });
140
+ return {
141
+ __loaderResult: true,
142
+ ok: false,
109
143
  error: errorInfo,
144
+ fallback: null,
110
145
  };
111
- renderedFallback = fallback(props);
112
- } else {
113
- renderedFallback = fallback;
114
146
  }
115
147
 
116
148
  debugLog("loader", "loader error wrapped with boundary fallback", {
@@ -243,6 +275,15 @@ function createLoaderExecutor<TEnv>(
243
275
 
244
276
  const currentLoaderId = loader.$$id;
245
277
  const variables = (ctx as InternalHandlerContext<any, TEnv>)._variables;
278
+
279
+ // Capture whether this loader is being started from a DSL loader scope
280
+ // (runInsideLoaderScope in fresh.ts). Handler-invoked loaders are NOT
281
+ // inside loader scope. This determines whether rendered() is allowed.
282
+ const isDslLoader = isInsideLoaderScope();
283
+
284
+ let renderedResolved = false;
285
+ let renderedPromise: Promise<void> | null = null;
286
+
246
287
  // Loader functions are always fresh (never cached), so they get an
247
288
  // unguarded get that bypasses non-cacheable read guards. This applies
248
289
  // to ALL loaders — DSL and handler-called — because the loader
@@ -256,26 +297,150 @@ function createLoaderExecutor<TEnv>(
256
297
  search: (ctx as any).search,
257
298
  pathname: ctx.pathname,
258
299
  url: ctx.url,
300
+ originalUrl: ctx.originalUrl,
259
301
  env: ctx.env,
302
+ waitUntil: ctx.waitUntil.bind(ctx),
303
+ executionContext: ctx.executionContext,
260
304
  get: ((keyOrVar: any) =>
261
305
  contextGet(variables, keyOrVar)) as typeof ctx.get,
262
- use: <TDep, TDepParams = any>(
263
- dep: LoaderDefinition<TDep, TDepParams>,
264
- ): Promise<TDep> => {
265
- return useLoader(dep, currentLoaderId);
266
- },
306
+ use: ((item: LoaderDefinition<any, any> | Handle<any, any>) => {
307
+ if (isHandle(item)) {
308
+ if (!renderedResolved) {
309
+ throw new Error(
310
+ `ctx.use(handle) in a loader requires "await ctx.rendered()" first. ` +
311
+ `Handle "${item.$$id}" cannot be read until the render tree has settled.`,
312
+ );
313
+ }
314
+ const reqCtx = reqCtxRef ?? _getRequestContext();
315
+ if (!reqCtx) {
316
+ throw new Error(
317
+ `ctx.use(handle) failed: request context not available.`,
318
+ );
319
+ }
320
+ const segmentOrder = reqCtx._renderBarrierSegmentOrder ?? [];
321
+ // The complete snapshot is cached at barrier resolution for
322
+ // non-streaming trees, and by rendered() after handleStore.settled for
323
+ // streaming trees (where the eager snapshot would have been incomplete
324
+ // because loading() handlers were still in flight). Either way it is
325
+ // present by the time a loader reads a handle; the fresh build is only
326
+ // a defensive fallback.
327
+ const snapshot =
328
+ reqCtx._renderBarrierHandleSnapshot ??
329
+ buildHandleSnapshot(reqCtx._handleStore, segmentOrder);
330
+ return collectHandleData(item, snapshot, segmentOrder);
331
+ }
332
+
333
+ // Loader case
334
+ return useLoader(item as LoaderDefinition<any, any>, currentLoaderId);
335
+ }) as LoaderContext["use"],
267
336
  method: "GET",
268
337
  body: undefined,
269
338
  reverse: ctx.reverse as LoaderContext["reverse"],
339
+ rendered: (): Promise<void> => {
340
+ // Guard: only DSL loaders may use rendered()
341
+ if (!isDslLoader) {
342
+ throw new Error(
343
+ `ctx.rendered() is only available in DSL loaders (registered via loader() in urls()). ` +
344
+ `Handler-invoked loaders (ctx.use(Loader) inside a handler) cannot use rendered().`,
345
+ );
346
+ }
347
+
348
+ const reqCtx = reqCtxRef ?? _getRequestContext();
349
+
350
+ if (renderedPromise) return renderedPromise;
351
+
352
+ if (!reqCtx) {
353
+ throw new Error(
354
+ `ctx.rendered() failed: request context not available.`,
355
+ );
356
+ }
357
+
358
+ // Bidirectional deadlock check: if a handler already started
359
+ // awaiting this loader, calling rendered() would deadlock. This is the
360
+ // real cycle guard (it holds for both streaming and non-streaming): the
361
+ // handler blocks segment resolution, which blocks the barrier, which
362
+ // blocks this loader.
363
+ if (reqCtx._handlerLoaderDeps?.has(currentLoaderId)) {
364
+ throw new Error(
365
+ `Deadlock: loader "${currentLoaderId}" called ctx.rendered() but a handler ` +
366
+ `is already awaiting this loader via ctx.use(). The handler blocks ` +
367
+ `segment resolution, which blocks the barrier, which blocks this loader. ` +
368
+ `Move the data dependency to a loader-to-loader pattern instead.`,
369
+ );
370
+ }
371
+
372
+ // Register this loader as waiting for the barrier so that
373
+ // setupLoaderAccess can detect deadlocks when a handler
374
+ // tries to await the same loader via ctx.use().
375
+ if (!reqCtx._renderBarrierWaiters) {
376
+ reqCtx._renderBarrierWaiters = new Set();
377
+ }
378
+ reqCtx._renderBarrierWaiters.add(currentLoaderId);
379
+
380
+ // Streaming trees (loading()): the barrier resolves once the segment
381
+ // tree is resolved, but loading() handlers stream behind Suspense and
382
+ // their handle pushes are still in flight then. Their async execution
383
+ // IS tracked in the handle store (trackHandler -> store.track), so after
384
+ // the barrier we seal (no further handlers register once the tree is
385
+ // resolved) and wait for settled — every tracked handler, streaming
386
+ // included, has finished pushing. The loader's own segment streams in
387
+ // after, so this does not block the shell; the deadlock guard above
388
+ // keeps a handler from depending on this loader.
389
+ const streaming = reqCtx._treeHasStreaming === true;
390
+ renderedPromise = reqCtx._renderBarrier.then(async () => {
391
+ if (streaming) {
392
+ reqCtx._handleStore.seal();
393
+ await reqCtx._handleStore.settled;
394
+ // The eager snapshot was intentionally left unbuilt for streaming
395
+ // (it would have been incomplete). Build the complete one once, now
396
+ // that the store has settled, so every ctx.use(handle) reads the
397
+ // cached snapshot instead of rebuilding it per call.
398
+ reqCtx._renderBarrierHandleSnapshot ??= buildHandleSnapshot(
399
+ reqCtx._handleStore,
400
+ reqCtx._renderBarrierSegmentOrder ?? [],
401
+ );
402
+ }
403
+ renderedResolved = true;
404
+ });
405
+ return renderedPromise;
406
+ },
270
407
  };
271
408
 
272
- const doneLoader = track(`loader:${loader.$$id}`, 2);
273
- const promise = Promise.resolve(
274
- loaderFn(loaderCtx as LoaderContext<any, TEnv>),
275
- ).finally(() => {
276
- pendingLoaders.delete(loader.$$id);
277
- doneLoader();
278
- });
409
+ // Meter this loader once via observePhase (loader:<id> perf metric +
410
+ // rango.loader span); loaderFn runs inside the span callback so its KV/D1/
411
+ // fetch spans nest under it. This is one of the observePhase loader funnels —
412
+ // see instrument.ts for the single-metering contract.
413
+ //
414
+ // Run the loader body inside loader scope so request-scoped reads
415
+ // (cookies()/headers() and non-cacheable ctx.get) are exempt from the
416
+ // cache-purity guards: loaders always run fresh, so their reads never leak
417
+ // into a cached segment. DSL loaders are already wrapped by fresh.ts; this
418
+ // also covers handler-invoked loaders (ctx.use(Loader) from a handler),
419
+ // which otherwise execute in the caller's cache scope and would wrongly
420
+ // throw. rendered() gating uses the captured isDslLoader (above), so this
421
+ // does not grant rendered() to handler-invoked loaders. Uses a body-only
422
+ // scope, so isInsideLoaderScope() / barrier / deadlock gating is unchanged.
423
+ //
424
+ // `handlerInvoked` (!isDslLoader) rides on the scope for the CONSUMPTION-
425
+ // LANE RULE: a handler-consumed loader's value is a BAKED copy in every
426
+ // shared artifact (cache(), "use cache", the PPR shell), so its identity
427
+ // reads are exempt from the shell-capture guard — same allowance the
428
+ // cache-purity guards give it. DSL segment loaders keep their lane
429
+ // machinery (live = masked at capture, bake = guarded). A DSL loader's
430
+ // nested deps inherit isDslLoader=false only when the CHAIN started in a
431
+ // handler; a chain started by the segment funnel stays DSL (the loader
432
+ // scope ALS survives the body's awaits).
433
+ const promise = observePhase(PHASES.loader(loader.$$id), () =>
434
+ Promise.resolve(
435
+ runInsideLoaderBodyScope(
436
+ () => loaderFn(loaderCtx as LoaderContext<any, TEnv>),
437
+ loader.$$id,
438
+ !isDslLoader,
439
+ ),
440
+ ).finally(() => {
441
+ pendingLoaders.delete(loader.$$id);
442
+ }),
443
+ );
279
444
 
280
445
  loaderPromises.set(loader.$$id, promise);
281
446
  return promise;
@@ -297,12 +462,13 @@ export function setupLoaderAccess<TEnv>(
297
462
  ctx: HandlerContext<any, TEnv>,
298
463
  loaderPromises: Map<string, Promise<any>>,
299
464
  ): void {
300
- // Eagerly capture the HandleStore at setup time (before pipeline async ops).
301
- // In workerd/Cloudflare, dynamic imports and fetch() in the match pipeline
302
- // can disrupt AsyncLocalStorage, causing getRequestContext() to return
303
- // undefined when handlers later call ctx.use(handle). Capturing early
304
- // ensures the store reference survives ALS disruption.
305
- const handleStoreRef = _getRequestContext()?._handleStore;
465
+ // Eagerly capture the request context and HandleStore at setup time
466
+ // (before pipeline async ops). In workerd/Cloudflare, dynamic imports and
467
+ // fetch() in the match pipeline can disrupt AsyncLocalStorage, causing
468
+ // getRequestContext() to return undefined when handlers later call
469
+ // ctx.use(handle). Capturing early ensures references survive ALS disruption.
470
+ const reqCtxRef = _getRequestContext();
471
+ const handleStoreRef = reqCtxRef?._handleStore;
306
472
 
307
473
  const useLoader = createLoaderExecutor(ctx, loaderPromises);
308
474
 
@@ -320,21 +486,72 @@ export function setupLoaderAccess<TEnv>(
320
486
  );
321
487
  }
322
488
 
323
- return (
324
- dataOrFn: unknown | Promise<unknown> | (() => Promise<unknown>),
325
- ) => {
326
- if (!store) return;
327
-
328
- const valueOrPromise =
329
- typeof dataOrFn === "function"
330
- ? (dataOrFn as () => Promise<unknown>)()
331
- : dataOrFn;
489
+ return withDefer(
490
+ (dataOrFn: unknown | Promise<unknown> | (() => Promise<unknown>)) => {
491
+ if (!store) return;
492
+
493
+ if (typeof dataOrFn === "function") {
494
+ // Run the callback inside the push-callback scope so ctx.use(loader)
495
+ // calls it makes — including after its own awaits, for an async
496
+ // callback — are not registered as handler-to-loader deps and do not
497
+ // trip the deadlock guard. A pushed promise value is not tracked by
498
+ // handleStore.settled and does not block segment resolution, so it
499
+ // cannot form a rendered() deadlock. The ALS scope (not a plain
500
+ // boolean) is what survives the callback's awaits.
501
+ const result = runInsidePushCallbackScope(() =>
502
+ (dataOrFn as () => Promise<unknown>)(),
503
+ );
504
+ store.push(handle.$$id, segmentId, result);
505
+ return;
506
+ }
507
+
508
+ store.push(handle.$$id, segmentId, dataOrFn);
509
+ },
510
+ );
511
+ }
332
512
 
333
- store.push(handle.$$id, segmentId, valueOrPromise);
334
- };
513
+ // Deadlock guard and handler-to-loader dependency tracking.
514
+ // Skip when inside a DSL loader scope (resolveLoaderData also calls
515
+ // ctx.use() but that's DSL-to-DSL, not handler-to-loader) or when
516
+ // inside a handle push callback (push callbacks don't block segment
517
+ // resolution so they can't cause rendered() deadlocks). The push-callback
518
+ // check is an ALS scope so it also exempts an ASYNC callback's continuation
519
+ // after its first await — relevant on streaming trees, where the guard
520
+ // state now stays live until handleStore.settled.
521
+ const loader = item as LoaderDefinition<any, any>;
522
+ if (!isInsideLoaderScope() && !isInsidePushCallbackScope()) {
523
+ const reqCtx = reqCtxRef ?? _getRequestContext();
524
+ if (reqCtx) {
525
+ // Direction 1: handler awaits loader that already called rendered()
526
+ if (
527
+ loaderPromises.has(loader.$$id) &&
528
+ reqCtx._renderBarrierWaiters?.has(loader.$$id)
529
+ ) {
530
+ throw new Error(
531
+ `Deadlock: handler is awaiting loader "${loader.$$id}" which called ctx.rendered(). ` +
532
+ `The loader is waiting for segment resolution, but the handler blocks resolution. ` +
533
+ `Move the data dependency to a loader-to-loader pattern instead.`,
534
+ );
535
+ }
536
+ // Direction 2: track dep so rendered() can detect the deadlock if the
537
+ // loader calls it later. Skip once the guard window is CLOSED — for a
538
+ // non-streaming tree that is when the barrier resolves (rendered()
539
+ // resolves immediately), and for a streaming tree it is when
540
+ // handleStore.settled completes (rendered() keeps waiting until then, so
541
+ // a loading() handler resuming after the barrier can still form a
542
+ // cycle). Using the explicit guard-closed flag rather than
543
+ // _renderBarrierSegmentOrder keeps tracking live across the streaming
544
+ // settle wait. (Handle push callbacks are already excluded above via
545
+ // isInsidePushCallbackScope(), so they cannot produce false positives
546
+ // here.)
547
+ if (!reqCtx._renderBarrierGuardClosed) {
548
+ if (!reqCtx._handlerLoaderDeps) reqCtx._handlerLoaderDeps = new Set();
549
+ reqCtx._handlerLoaderDeps.add(loader.$$id);
550
+ }
551
+ }
335
552
  }
336
553
 
337
- return useLoader(item as LoaderDefinition<any, any>, null);
554
+ return useLoader(loader, null);
338
555
  }) as typeof ctx.use;
339
556
  }
340
557
 
@@ -361,18 +578,21 @@ export function setupBuildUse<TEnv>(ctx: HandlerContext<any, TEnv>): void {
361
578
  );
362
579
  }
363
580
 
364
- return (
365
- dataOrFn: unknown | Promise<unknown> | (() => Promise<unknown>),
366
- ) => {
367
- if (!store) return;
581
+ // Wrap with withDefer so ctx.use(Handle).defer(...) works on the build /
582
+ // prerender path, matching production setupLoaderAccess. Without it a
583
+ // prerender handler calling .defer() throws "defer is not a function".
584
+ return withDefer(
585
+ (dataOrFn: unknown | Promise<unknown> | (() => Promise<unknown>)) => {
586
+ if (!store) return;
368
587
 
369
- const valueOrPromise =
370
- typeof dataOrFn === "function"
371
- ? (dataOrFn as () => Promise<unknown>)()
372
- : dataOrFn;
588
+ const valueOrPromise =
589
+ typeof dataOrFn === "function"
590
+ ? (dataOrFn as () => Promise<unknown>)()
591
+ : dataOrFn;
373
592
 
374
- store.push(handle.$$id, segmentId, valueOrPromise);
375
- };
593
+ store.push(handle.$$id, segmentId, valueOrPromise);
594
+ },
595
+ );
376
596
  }
377
597
 
378
598
  // Loader case: not available during pre-rendering
@@ -400,8 +620,11 @@ export function setupLoaderAccessSilent<TEnv>(
400
620
 
401
621
  ctx.use = ((item: LoaderDefinition<any, any> | Handle<any, any>) => {
402
622
  if (isHandle(item)) {
403
- // Silent mode - return a no-op so handle data is not pushed during caching
404
- return (_dataOrFn: unknown) => {};
623
+ // Silent mode - return a no-op so handle data is not pushed during caching.
624
+ // Wrap with withDefer so ctx.use(Handle).defer(...) still resolves to a
625
+ // callable resolver (also a no-op here), matching production's push shape
626
+ // instead of throwing "defer is not a function".
627
+ return withDefer((_dataOrFn: unknown) => {});
405
628
  }
406
629
 
407
630
  return useLoader(item as LoaderDefinition<any, any>, null);
@@ -1,8 +1,6 @@
1
1
  import { AsyncLocalStorage } from "node:async_hooks";
2
2
  import { INTERNAL_RANGO_DEBUG } from "../internal-debug.js";
3
3
 
4
- // -- Revalidation trace types --
5
-
6
4
  export interface RevalidationTraceEntry {
7
5
  segmentId: string;
8
6
  segmentType: string;
@@ -36,8 +34,6 @@ export interface RevalidationTrace {
36
34
  entries: RevalidationTraceEntry[];
37
35
  }
38
36
 
39
- // -- Log context --
40
-
41
37
  interface RouterLogContext {
42
38
  requestId: string;
43
39
  transactionId: string;
@@ -195,8 +191,6 @@ export function debugWarn(
195
191
  console.warn(`${prefix} ${message}`);
196
192
  }
197
193
 
198
- // -- Revalidation trace helpers --
199
-
200
194
  export function isTraceActive(): boolean {
201
195
  if (!INTERNAL_RANGO_DEBUG) return false;
202
196
  const ctx = routerLogContext.getStore();