@rangojs/router 0.24.4 → 0.25.0

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 (267) hide show
  1. package/README.md +1 -0
  2. package/dist/bin/rango.js +363 -39
  3. package/dist/testing/vitest.js +37 -4
  4. package/dist/types/browser/event-controller.d.ts +24 -5
  5. package/dist/types/browser/intercept-utils.d.ts +5 -2
  6. package/dist/types/browser/merge-segment-loaders.d.ts +0 -23
  7. package/dist/types/browser/navigation-transaction.d.ts +5 -5
  8. package/dist/types/browser/partial-update.d.ts +21 -13
  9. package/dist/types/browser/prefetch/cache.d.ts +8 -0
  10. package/dist/types/browser/rango-marker.d.ts +7 -0
  11. package/dist/types/browser/react/filter-segment-order.d.ts +10 -0
  12. package/dist/types/browser/react/use-handle.d.ts +20 -0
  13. package/dist/types/browser/react/use-href.d.ts +6 -0
  14. package/dist/types/browser/react/use-router.d.ts +6 -1
  15. package/dist/types/browser/response-adapter.d.ts +5 -3
  16. package/dist/types/browser/scroll-restoration.d.ts +9 -3
  17. package/dist/types/browser/segment-reconciler.d.ts +10 -13
  18. package/dist/types/browser/types.d.ts +38 -8
  19. package/dist/types/build/route-types/per-module-writer.d.ts +12 -0
  20. package/dist/types/cache/cf/cf-shell-frame.d.ts +2 -0
  21. package/dist/types/cache/types.d.ts +8 -0
  22. package/dist/types/client-urls/client-urls.d.ts +6 -2
  23. package/dist/types/client-urls/revalidate-registry.d.ts +1 -1
  24. package/dist/types/client-urls/server-projection.d.ts +9 -5
  25. package/dist/types/client-urls/types.d.ts +29 -16
  26. package/dist/types/client.d.ts +2 -2
  27. package/dist/types/client.rsc.d.ts +2 -2
  28. package/dist/types/decode-loader-results.d.ts +28 -7
  29. package/dist/types/href-client.d.ts +3 -1
  30. package/dist/types/index.rsc.d.ts +1 -1
  31. package/dist/types/loader-redirect.d.ts +2 -3
  32. package/dist/types/outlet-context.d.ts +7 -6
  33. package/dist/types/prefetch-markers.d.ts +14 -5
  34. package/dist/types/route-content-wrapper.d.ts +17 -14
  35. package/dist/types/route-definition/helpers-types.d.ts +7 -2
  36. package/dist/types/route-definition/resolve-handler-use.d.ts +6 -1
  37. package/dist/types/route-definition/rewrite.d.ts +48 -5
  38. package/dist/types/route-types.d.ts +2 -2
  39. package/dist/types/router/handler-context.d.ts +14 -2
  40. package/dist/types/router/intercept-resolution.d.ts +19 -46
  41. package/dist/types/router/lazy-includes.d.ts +7 -0
  42. package/dist/types/router/match-middleware/intercept-resolution.d.ts +2 -2
  43. package/dist/types/router/middleware.d.ts +0 -16
  44. package/dist/types/router/navigation-snapshot.d.ts +2 -0
  45. package/dist/types/router/prefetch-cache-ttl.d.ts +8 -11
  46. package/dist/types/router/revalidation.d.ts +8 -1
  47. package/dist/types/router/router-context.d.ts +4 -16
  48. package/dist/types/router/router-interfaces.d.ts +0 -6
  49. package/dist/types/router/router-options.d.ts +4 -7
  50. package/dist/types/router/segment-resolution/prefetch-deferral.d.ts +31 -8
  51. package/dist/types/router/segment-resolution/revalidation.d.ts +38 -4
  52. package/dist/types/router/segment-resolution.d.ts +1 -1
  53. package/dist/types/router/segment-wrappers.d.ts +3 -5
  54. package/dist/types/router/substitute-pattern-params.d.ts +3 -2
  55. package/dist/types/router/tracing.d.ts +2 -2
  56. package/dist/types/rsc/full-payload.d.ts +14 -2
  57. package/dist/types/rsc/helpers.d.ts +8 -0
  58. package/dist/types/rsc/manifest-init.d.ts +20 -0
  59. package/dist/types/rsc/prefetch-render-error.d.ts +22 -0
  60. package/dist/types/rsc/progressive-enhancement.d.ts +20 -0
  61. package/dist/types/rsc/rewrite-passes.d.ts +12 -2
  62. package/dist/types/rsc/server-action.d.ts +6 -0
  63. package/dist/types/rsc/shell-capture-constants.d.ts +10 -1
  64. package/dist/types/rsc/shell-capture.d.ts +7 -0
  65. package/dist/types/rsc/types.d.ts +5 -0
  66. package/dist/types/segment-system.d.ts +15 -0
  67. package/dist/types/server/context.d.ts +64 -4
  68. package/dist/types/server/request-context.d.ts +41 -4
  69. package/dist/types/server/request-headers.d.ts +12 -0
  70. package/dist/types/server/rewrite-trail.d.ts +8 -1
  71. package/dist/types/testing/e2e/index.d.ts +1 -1
  72. package/dist/types/testing/flight.entry.d.ts +1 -1
  73. package/dist/types/testing/index.d.ts +1 -1
  74. package/dist/types/testing/internal/client-urls-projection.d.ts +4 -3
  75. package/dist/types/testing/render-route.d.ts +21 -11
  76. package/dist/types/testing/run-loader.d.ts +7 -0
  77. package/dist/types/testing/run-middleware.d.ts +7 -0
  78. package/dist/types/testing/run-revalidate.d.ts +5 -4
  79. package/dist/types/testing/serve-shell-request.d.ts +41 -4
  80. package/dist/types/testing/shell-status.d.ts +17 -0
  81. package/dist/types/testing/vitest-stubs/version.d.ts +1 -0
  82. package/dist/types/testing/vitest.d.ts +10 -1
  83. package/dist/types/thenable-status.d.ts +0 -6
  84. package/dist/types/types/handler-context.d.ts +12 -1
  85. package/dist/types/types/loader-types.d.ts +4 -3
  86. package/dist/types/types/route-entry.d.ts +2 -1
  87. package/dist/types/urls/path-helper-types.d.ts +4 -2
  88. package/dist/types/vite/discovery/client-urls-projection.d.ts +55 -5
  89. package/dist/types/vite/discovery/state.d.ts +20 -0
  90. package/dist/types/vite/plugins/client-urls-revalidate-hoist.d.ts +28 -16
  91. package/dist/types/vite/plugins/hoist/source.d.ts +8 -7
  92. package/dist/types/vite/plugins/virtual-entries.d.ts +6 -0
  93. package/dist/types/vite/utils/prerender-utils.d.ts +7 -0
  94. package/dist/vite/index.js +6816 -6547
  95. package/package.json +1 -1
  96. package/skills/app-structure/SKILL.md +255 -0
  97. package/skills/app-structure/content-site.md +333 -0
  98. package/skills/app-structure/e-commerce.md +435 -0
  99. package/skills/app-structure/large-app.md +885 -0
  100. package/skills/app-structure/multi-app.md +280 -0
  101. package/skills/breadcrumbs/SKILL.md +6 -1
  102. package/skills/caching/SKILL.md +3 -1
  103. package/skills/catalog.json +12 -0
  104. package/skills/client-urls/SKILL.md +185 -32
  105. package/skills/composability/SKILL.md +10 -8
  106. package/skills/handler-use/SKILL.md +48 -9
  107. package/skills/hooks/handle-and-actions.md +104 -0
  108. package/skills/hooks/navigation.md +24 -10
  109. package/skills/hooks/urls.md +1 -1
  110. package/skills/i18n/SKILL.md +2 -1
  111. package/skills/intercept/SKILL.md +60 -29
  112. package/skills/layout/SKILL.md +6 -3
  113. package/skills/links/SKILL.md +151 -17
  114. package/skills/loader/SKILL.md +32 -10
  115. package/skills/middleware/SKILL.md +111 -15
  116. package/skills/observability/SKILL.md +11 -0
  117. package/skills/parallel/SKILL.md +5 -5
  118. package/skills/ppr/SKILL.md +43 -8
  119. package/skills/prerender/SKILL.md +1 -2
  120. package/skills/rango/SKILL.md +3 -0
  121. package/skills/response-routes/SKILL.md +4 -1
  122. package/skills/scripts/SKILL.md +12 -1
  123. package/skills/server-actions/SKILL.md +60 -20
  124. package/skills/testing/SKILL.md +1 -0
  125. package/skills/testing/client-components.md +13 -10
  126. package/skills/testing/loader.md +25 -24
  127. package/skills/testing/middleware.md +11 -11
  128. package/skills/testing/response-routes.md +4 -1
  129. package/skills/testing/serve-shell-request.md +85 -21
  130. package/skills/ux-architecture/SKILL.md +980 -0
  131. package/skills/view-transitions/SKILL.md +43 -10
  132. package/src/__augment-tests__/augmented.check.ts +28 -0
  133. package/src/browser/event-controller.ts +44 -18
  134. package/src/browser/intercept-utils.ts +3 -2
  135. package/src/browser/merge-segment-loaders.ts +0 -85
  136. package/src/browser/navigation-bridge.ts +112 -84
  137. package/src/browser/navigation-client.ts +20 -15
  138. package/src/browser/navigation-store.ts +4 -2
  139. package/src/browser/navigation-transaction.ts +9 -8
  140. package/src/browser/partial-update.ts +90 -32
  141. package/src/browser/prefetch/cache.ts +8 -0
  142. package/src/browser/prefetch/fetch.ts +13 -3
  143. package/src/browser/rango-marker.ts +13 -0
  144. package/src/browser/react/NavigationProvider.tsx +25 -7
  145. package/src/browser/react/filter-segment-order.ts +13 -0
  146. package/src/browser/react/use-action.ts +9 -15
  147. package/src/browser/react/use-handle.ts +63 -71
  148. package/src/browser/react/use-href.tsx +24 -5
  149. package/src/browser/react/use-link-status.ts +1 -1
  150. package/src/browser/react/use-navigation.ts +30 -40
  151. package/src/browser/react/use-router.ts +37 -6
  152. package/src/browser/react/use-segments.ts +8 -12
  153. package/src/browser/response-adapter.ts +11 -5
  154. package/src/browser/rsc-router.tsx +3 -1
  155. package/src/browser/scroll-restoration.ts +34 -20
  156. package/src/browser/segment-reconciler.ts +11 -29
  157. package/src/browser/server-action-bridge.ts +52 -16
  158. package/src/browser/types.ts +40 -8
  159. package/src/build/generate-manifest.ts +12 -1
  160. package/src/build/route-types/codegen.ts +6 -2
  161. package/src/build/route-types/per-module-writer.ts +94 -61
  162. package/src/cache/cache-runtime.ts +9 -0
  163. package/src/cache/cache-scope.ts +2 -1
  164. package/src/cache/cf/cf-cache-store.ts +2 -0
  165. package/src/cache/cf/cf-shell-frame.ts +3 -0
  166. package/src/cache/types.ts +8 -0
  167. package/src/cache/vercel/vercel-cache-store.ts +6 -1
  168. package/src/client-urls/client-urls.ts +202 -153
  169. package/src/client-urls/revalidate-registry.ts +12 -3
  170. package/src/client-urls/server-projection.ts +137 -66
  171. package/src/client-urls/types.ts +30 -15
  172. package/src/client.rsc.tsx +11 -1
  173. package/src/client.tsx +9 -1
  174. package/src/decode-loader-results.ts +64 -31
  175. package/src/href-client.ts +3 -1
  176. package/src/index.rsc.ts +1 -0
  177. package/src/index.ts +12 -4
  178. package/src/loader-redirect.tsx +2 -3
  179. package/src/outlet-context.ts +7 -6
  180. package/src/prefetch-markers.ts +15 -5
  181. package/src/reverse.ts +3 -3
  182. package/src/route-content-wrapper.tsx +19 -17
  183. package/src/route-definition/dsl-helpers.ts +27 -7
  184. package/src/route-definition/helpers-types.ts +7 -2
  185. package/src/route-definition/resolve-handler-use.ts +23 -10
  186. package/src/route-definition/rewrite.ts +86 -19
  187. package/src/route-types.ts +1 -2
  188. package/src/router/find-match.ts +37 -1
  189. package/src/router/handler-context.ts +85 -40
  190. package/src/router/intercept-resolution.ts +161 -307
  191. package/src/router/lazy-includes.ts +43 -27
  192. package/src/router/loader-resolution.ts +21 -1
  193. package/src/router/manifest.ts +6 -8
  194. package/src/router/match-api.ts +11 -1
  195. package/src/router/match-middleware/background-revalidation.ts +2 -4
  196. package/src/router/match-middleware/cache-lookup.ts +16 -20
  197. package/src/router/match-middleware/intercept-resolution.ts +41 -60
  198. package/src/router/match-result.ts +31 -2
  199. package/src/router/middleware.ts +6 -147
  200. package/src/router/navigation-snapshot.ts +38 -2
  201. package/src/router/prefetch-cache-ttl.ts +10 -18
  202. package/src/router/prerender-match.ts +2 -1
  203. package/src/router/revalidation.ts +24 -3
  204. package/src/router/router-context.ts +5 -18
  205. package/src/router/router-interfaces.ts +0 -7
  206. package/src/router/router-options.ts +4 -7
  207. package/src/router/segment-resolution/fresh.ts +6 -2
  208. package/src/router/segment-resolution/helpers.ts +7 -1
  209. package/src/router/segment-resolution/prefetch-deferral.ts +62 -12
  210. package/src/router/segment-resolution/revalidation.ts +75 -35
  211. package/src/router/segment-resolution.ts +0 -1
  212. package/src/router/segment-wrappers.ts +8 -13
  213. package/src/router/substitute-pattern-params.ts +3 -2
  214. package/src/router/tracing.ts +2 -2
  215. package/src/router.ts +2 -9
  216. package/src/rsc/full-payload.ts +26 -4
  217. package/src/rsc/handler.ts +83 -41
  218. package/src/rsc/helpers.ts +13 -0
  219. package/src/rsc/manifest-init.ts +56 -3
  220. package/src/rsc/prefetch-render-error.ts +38 -0
  221. package/src/rsc/progressive-enhancement.ts +161 -211
  222. package/src/rsc/rewrite-passes.ts +56 -9
  223. package/src/rsc/rsc-rendering.ts +79 -23
  224. package/src/rsc/server-action.ts +25 -2
  225. package/src/rsc/shell-capture-constants.ts +22 -0
  226. package/src/rsc/shell-capture.ts +11 -0
  227. package/src/rsc/types.ts +5 -0
  228. package/src/segment-system.tsx +125 -119
  229. package/src/server/context.ts +96 -5
  230. package/src/server/handle-store.ts +3 -2
  231. package/src/server/request-context.ts +42 -3
  232. package/src/server/request-headers.ts +32 -0
  233. package/src/server/rewrite-trail.ts +55 -9
  234. package/src/ssr/ssr-root.tsx +6 -10
  235. package/src/testing/dispatch.ts +4 -5
  236. package/src/testing/e2e/index.ts +1 -0
  237. package/src/testing/flight.entry.ts +1 -0
  238. package/src/testing/index.ts +1 -0
  239. package/src/testing/internal/client-urls-projection.ts +31 -11
  240. package/src/testing/render-route.tsx +83 -40
  241. package/src/testing/run-loader.ts +26 -2
  242. package/src/testing/run-middleware.ts +10 -0
  243. package/src/testing/run-revalidate.ts +13 -6
  244. package/src/testing/serve-shell-request.ts +77 -4
  245. package/src/testing/shell-status.ts +40 -0
  246. package/src/testing/vitest-stubs/version.ts +1 -0
  247. package/src/testing/vitest.ts +74 -7
  248. package/src/thenable-status.ts +0 -11
  249. package/src/types/handler-context.ts +12 -1
  250. package/src/types/loader-types.ts +4 -3
  251. package/src/types/route-entry.ts +2 -1
  252. package/src/urls/include-helper.ts +38 -11
  253. package/src/urls/path-helper-types.ts +4 -2
  254. package/src/vite/discovery/client-urls-projection.ts +205 -16
  255. package/src/vite/discovery/discover-routers.ts +2 -2
  256. package/src/vite/discovery/prerender-collection.ts +2 -6
  257. package/src/vite/discovery/state.ts +26 -0
  258. package/src/vite/plugins/client-urls-revalidate-hoist.ts +190 -77
  259. package/src/vite/plugins/expose-internal-ids.ts +46 -40
  260. package/src/vite/plugins/hoist/source.ts +8 -7
  261. package/src/vite/plugins/transition-when-hoist.ts +1 -0
  262. package/src/vite/plugins/version-plugin.ts +7 -0
  263. package/src/vite/plugins/version.d.ts +7 -0
  264. package/src/vite/plugins/virtual-entries.ts +20 -0
  265. package/src/vite/rango.ts +4 -1
  266. package/src/vite/router-discovery.ts +39 -7
  267. package/src/vite/utils/prerender-utils.ts +17 -0
package/README.md CHANGED
@@ -540,6 +540,7 @@ Use it when you need it:
540
540
  | multi-column layouts, independent slots | [`/parallel`](./skills/parallel/SKILL.md) |
541
541
  | open a route as a modal on soft navigation | [`/intercept`](./skills/intercept/SKILL.md) |
542
542
  | compose route modules / sub-apps | [`/route`](./skills/route/SKILL.md), [`/composability`](./skills/composability/SKILL.md) |
543
+ | lay out a whole app | [`/app-structure`](./skills/app-structure/SKILL.md) |
543
544
  | cache a single function or component | [`/use-cache`](./skills/use-cache/SKILL.md), [`/cache-guide`](./skills/cache-guide/SKILL.md) |
544
545
  | feed live loaders from a cached shell | [`/shell-manifest`](./skills/shell-manifest/SKILL.md) |
545
546
  | edge caching with Cache-Control | [`/document-cache`](./skills/document-cache/SKILL.md) |
package/dist/bin/rango.js CHANGED
@@ -160,7 +160,9 @@ function generatePerModuleTypesSource(routes) {
160
160
  }
161
161
  deduped.set(name, { pattern, params, search });
162
162
  }
163
- const sorted = [...deduped.entries()].sort(([a], [b]) => a.localeCompare(b));
163
+ const sorted = [...deduped.entries()].sort(
164
+ ([a], [b]) => a.localeCompare(b, "en")
165
+ );
164
166
  const body = sorted.map(([name, { pattern, params, search }]) => {
165
167
  const key = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name) ? name : `"${name}"`;
166
168
  return formatRouteEntry(key, pattern, params, search);
@@ -173,7 +175,7 @@ export type routes = typeof routes;
173
175
  `;
174
176
  }
175
177
  function generateRouteTypesSource(routeManifest, searchSchemas) {
176
- const entries = Object.entries(routeManifest).filter(([name]) => !isAutoGeneratedRouteName(name)).sort(([a], [b]) => a.localeCompare(b));
178
+ const entries = Object.entries(routeManifest).filter(([name]) => !isAutoGeneratedRouteName(name)).sort(([a], [b]) => a.localeCompare(b, "en"));
177
179
  const filteredSearchSchemas = searchSchemas ? Object.fromEntries(
178
180
  Object.entries(searchSchemas).filter(
179
181
  ([name]) => !isAutoGeneratedRouteName(name)
@@ -817,51 +819,68 @@ function findUrlsVariableNames(code) {
817
819
  visit(sourceFile);
818
820
  return names;
819
821
  }
820
- function writePerModuleRouteTypesForFile(filePath) {
821
- try {
822
- const source = readFileSync2(filePath, "utf-8");
823
- if (!source.includes("urls(") && !source.includes("clientUrls(")) return;
824
- const varNames = findUrlsVariableNames(source);
825
- let routes;
826
- if (varNames.length > 0) {
827
- routes = [];
828
- const memo = createScanMemo();
829
- for (const varName of varNames) {
830
- const { routes: routeMap, searchSchemas } = buildCombinedRouteMapWithSearch(
831
- filePath,
832
- varName,
833
- void 0,
834
- void 0,
835
- void 0,
836
- memo
837
- );
838
- for (const [name, pattern] of Object.entries(routeMap)) {
839
- const params = extractParamsFromPattern(pattern);
840
- routes.push({
841
- name,
842
- pattern,
843
- ...params ? { params } : {},
844
- ...searchSchemas[name] ? { search: searchSchemas[name] } : {}
845
- });
846
- }
822
+ function planPerModuleRouteTypesForFile(filePath) {
823
+ const source = readFileSync2(filePath, "utf-8");
824
+ if (!source.includes("urls(") && !source.includes("clientUrls(")) return null;
825
+ const varNames = findUrlsVariableNames(source);
826
+ let routes;
827
+ if (varNames.length > 0) {
828
+ routes = [];
829
+ const memo = createScanMemo();
830
+ for (const varName of varNames) {
831
+ const { routes: routeMap, searchSchemas } = buildCombinedRouteMapWithSearch(
832
+ filePath,
833
+ varName,
834
+ void 0,
835
+ void 0,
836
+ void 0,
837
+ memo
838
+ );
839
+ for (const [name, pattern] of Object.entries(routeMap)) {
840
+ const params = extractParamsFromPattern(pattern);
841
+ routes.push({
842
+ name,
843
+ pattern,
844
+ ...params ? { params } : {},
845
+ ...searchSchemas[name] ? { search: searchSchemas[name] } : {}
846
+ });
847
847
  }
848
- } else {
849
- routes = extractRoutesFromSource(source);
850
848
  }
851
- const genPath = filePath.replace(/\.(tsx?|jsx?)$/, ".gen.ts");
852
- if (routes.length === 0) {
853
- if (varNames.length > 0 && !existsSync2(genPath)) {
854
- writeFileSync(genPath, generatePerModuleTypesSource([]));
849
+ } else {
850
+ routes = extractRoutesFromSource(source);
851
+ }
852
+ const genPath = filePath.replace(/\.(tsx?|jsx?)$/, ".gen.ts");
853
+ if (routes.length === 0) {
854
+ if (varNames.length === 0) return null;
855
+ return {
856
+ genPath,
857
+ isPlaceholder: true,
858
+ content: generatePerModuleTypesSource([])
859
+ };
860
+ }
861
+ return {
862
+ genPath,
863
+ isPlaceholder: false,
864
+ content: generatePerModuleTypesSource(routes)
865
+ };
866
+ }
867
+ function writePerModuleRouteTypesForFile(filePath) {
868
+ try {
869
+ const plan = planPerModuleRouteTypesForFile(filePath);
870
+ if (!plan) return;
871
+ const { genPath, isPlaceholder, content } = plan;
872
+ const exists = existsSync2(genPath);
873
+ if (isPlaceholder) {
874
+ if (!exists) {
875
+ writeFileSync(genPath, content);
855
876
  console.log(
856
877
  `[rango] Generated route types (placeholder) -> ${genPath}`
857
878
  );
858
879
  }
859
880
  return;
860
881
  }
861
- const genSource = generatePerModuleTypesSource(routes);
862
- const existing = existsSync2(genPath) ? readFileSync2(genPath, "utf-8") : null;
863
- if (existing !== genSource) {
864
- writeFileSync(genPath, genSource);
882
+ if (!exists || readFileSync2(genPath, "utf-8") !== content) {
883
+ writeFileSync(genPath, content);
865
884
  console.log(`[rango] Generated route types -> ${genPath}`);
866
885
  }
867
886
  } catch (err) {
@@ -1619,15 +1638,315 @@ var init_router_versions = __esm({
1619
1638
  }
1620
1639
  });
1621
1640
 
1641
+ // package.json
1642
+ var package_default;
1643
+ var init_package = __esm({
1644
+ "package.json"() {
1645
+ package_default = {
1646
+ name: "@rangojs/router",
1647
+ version: "0.25.0",
1648
+ description: "Django-inspired RSC router with composable URL patterns",
1649
+ keywords: [
1650
+ "react",
1651
+ "react-server-components",
1652
+ "router",
1653
+ "rsc",
1654
+ "vite"
1655
+ ],
1656
+ homepage: "https://github.com/rangojs/rango#readme",
1657
+ bugs: {
1658
+ url: "https://github.com/rangojs/rango/issues"
1659
+ },
1660
+ license: "MIT",
1661
+ author: "Ivo Todorov",
1662
+ repository: {
1663
+ type: "git",
1664
+ url: "git+https://github.com/rangojs/rango.git",
1665
+ directory: "packages/rangojs-router"
1666
+ },
1667
+ bin: {
1668
+ rango: "./dist/bin/rango.js"
1669
+ },
1670
+ files: [
1671
+ "src",
1672
+ "!src/**/__tests__",
1673
+ "!src/**/__mocks__",
1674
+ "!src/**/*.test.ts",
1675
+ "!src/**/*.test.tsx",
1676
+ "dist",
1677
+ "skills",
1678
+ "AGENTS.md",
1679
+ "README.md",
1680
+ "LICENSE"
1681
+ ],
1682
+ type: "module",
1683
+ exports: {
1684
+ ".": {
1685
+ types: "./dist/types/index.rsc.d.ts",
1686
+ "react-server": "./src/index.rsc.ts",
1687
+ default: "./src/index.ts"
1688
+ },
1689
+ "./server": {
1690
+ types: "./dist/types/server.d.ts",
1691
+ import: "./src/server.ts"
1692
+ },
1693
+ "./client": {
1694
+ types: "./dist/types/client.d.ts",
1695
+ "react-server": "./src/client.rsc.tsx",
1696
+ default: "./src/client.tsx"
1697
+ },
1698
+ "./browser": {
1699
+ types: "./dist/types/browser/index.d.ts",
1700
+ default: "./src/browser/index.ts"
1701
+ },
1702
+ "./ssr": {
1703
+ types: "./dist/types/ssr/index.d.ts",
1704
+ default: "./src/ssr/index.tsx"
1705
+ },
1706
+ "./rsc": {
1707
+ types: "./dist/types/rsc/index.d.ts",
1708
+ "react-server": "./src/rsc/index.ts",
1709
+ default: "./src/rsc/index.ts"
1710
+ },
1711
+ "./vite": {
1712
+ types: "./dist/types/vite/index.d.ts",
1713
+ import: "./dist/vite/index.js"
1714
+ },
1715
+ "./types": {
1716
+ types: "./src/vite/plugins/version.d.ts"
1717
+ },
1718
+ "./__internal": {
1719
+ types: "./dist/types/__internal.d.ts",
1720
+ default: "./src/__internal.ts"
1721
+ },
1722
+ "./internal/deps/browser": {
1723
+ types: "./dist/types/deps/browser.d.ts",
1724
+ default: "./src/deps/browser.ts"
1725
+ },
1726
+ "./internal/deps/ssr": {
1727
+ types: "./dist/types/deps/ssr.d.ts",
1728
+ default: "./src/deps/ssr.ts"
1729
+ },
1730
+ "./internal/deps/rsc": {
1731
+ types: "./dist/types/deps/rsc.d.ts",
1732
+ "react-server": "./src/deps/rsc.ts",
1733
+ default: "./src/deps/rsc.ts"
1734
+ },
1735
+ "./internal/deps/rsc-client": {
1736
+ types: "./dist/types/deps/rsc-client.d.ts",
1737
+ "react-server": "./src/deps/rsc-client.ts",
1738
+ default: "./src/deps/rsc-client.ts"
1739
+ },
1740
+ "./internal/deps/html-stream-client": {
1741
+ types: "./dist/types/deps/html-stream-client.d.ts",
1742
+ default: "./src/deps/html-stream-client.ts"
1743
+ },
1744
+ "./internal/deps/html-stream-server": {
1745
+ types: "./dist/types/deps/html-stream-server.d.ts",
1746
+ default: "./src/deps/html-stream-server.ts"
1747
+ },
1748
+ "./internal/rsc-handler": {
1749
+ types: "./dist/types/rsc/handler.d.ts",
1750
+ "react-server": "./src/rsc/handler.ts",
1751
+ default: "./src/rsc/handler.ts"
1752
+ },
1753
+ "./internal/browser/dev-discovery": {
1754
+ types: "./dist/types/browser/dev-discovery.d.ts",
1755
+ default: "./src/browser/dev-discovery.ts"
1756
+ },
1757
+ "./cache": {
1758
+ types: "./dist/types/cache/index.d.ts",
1759
+ "react-server": "./src/cache/index.ts",
1760
+ default: "./src/cache/index.ts"
1761
+ },
1762
+ "./cache-runtime": {
1763
+ types: "./dist/types/cache/cache-runtime.d.ts",
1764
+ "react-server": "./src/cache/cache-runtime.ts",
1765
+ default: "./src/cache/cache-runtime.ts"
1766
+ },
1767
+ "./cloudflare": {
1768
+ types: "./dist/types/cloudflare/index.d.ts",
1769
+ "react-server": "./src/cloudflare/index.ts",
1770
+ default: "./src/cloudflare/index.ts"
1771
+ },
1772
+ "./prerender": {
1773
+ types: "./dist/types/prerender/index.d.ts",
1774
+ "react-server": "./src/prerender/index.ts",
1775
+ default: "./src/prerender/index.ts"
1776
+ },
1777
+ "./prerender/cloudflare": {
1778
+ types: "./dist/types/prerender/cloudflare.d.ts",
1779
+ "react-server": "./src/prerender/cloudflare.ts",
1780
+ default: "./src/prerender/cloudflare.ts"
1781
+ },
1782
+ "./vercel": {
1783
+ types: "./dist/types/vercel/index.d.ts",
1784
+ "react-server": "./src/vercel/index.ts",
1785
+ default: "./src/vercel/index.ts"
1786
+ },
1787
+ "./theme": {
1788
+ types: "./dist/types/theme/index.d.ts",
1789
+ default: "./src/theme/index.ts"
1790
+ },
1791
+ "./build": {
1792
+ types: "./dist/types/build/index.d.ts",
1793
+ import: "./src/build/index.ts"
1794
+ },
1795
+ "./build/shell-capture": {
1796
+ types: "./dist/types/prerender/build-shell-capture.d.ts",
1797
+ "react-server": "./src/prerender/build-shell-capture.ts",
1798
+ import: "./src/prerender/build-shell-capture.ts"
1799
+ },
1800
+ "./host": {
1801
+ types: "./dist/types/host/index.d.ts",
1802
+ "react-server": "./src/host/index.ts",
1803
+ default: "./src/host/index.ts"
1804
+ },
1805
+ "./host/testing": {
1806
+ types: "./dist/types/host/testing.d.ts",
1807
+ default: "./src/host/testing.ts"
1808
+ },
1809
+ "./testing": {
1810
+ types: "./dist/types/testing/index.d.ts",
1811
+ default: "./src/testing/index.ts"
1812
+ },
1813
+ "./testing/vitest": {
1814
+ types: "./dist/types/testing/vitest.d.ts",
1815
+ default: "./dist/testing/vitest.js"
1816
+ },
1817
+ "./testing/dom": {
1818
+ types: "./dist/types/testing/dom.entry.d.ts",
1819
+ default: "./src/testing/dom.entry.ts"
1820
+ },
1821
+ "./testing/e2e": {
1822
+ types: "./dist/types/testing/e2e/index.d.ts",
1823
+ default: "./src/testing/e2e/index.ts"
1824
+ },
1825
+ "./testing/flight": {
1826
+ types: "./dist/types/testing/flight.entry.d.ts",
1827
+ "react-server": "./src/testing/flight.entry.ts",
1828
+ default: "./src/testing/flight.entry.ts"
1829
+ },
1830
+ "./testing/flight-matchers": {
1831
+ types: "./dist/types/testing/flight-matchers.d.ts",
1832
+ default: "./src/testing/flight-matchers.ts"
1833
+ }
1834
+ },
1835
+ publishConfig: {
1836
+ access: "public",
1837
+ tag: "latest"
1838
+ },
1839
+ scripts: {
1840
+ build: "pnpm run build:types && pnpm exec esbuild src/vite/index.ts --bundle --format=esm --outfile=dist/vite/index.js --platform=node --packages=external && mkdir -p dist/vite/plugins && cp src/vite/plugins/cloudflare-protocol-loader-hook.mjs dist/vite/plugins/cloudflare-protocol-loader-hook.mjs && pnpm exec esbuild src/testing/vitest.ts --bundle --format=esm --outfile=dist/testing/vitest.js --platform=node --packages=external && pnpm exec esbuild src/bin/rango.ts --bundle --format=esm --outfile=dist/bin/rango.js --platform=node --packages=external --banner:js='#!/usr/bin/env node' && chmod +x dist/bin/rango.js",
1841
+ "build:types": "rm -rf dist/types && pnpm exec tsc -p tsconfig.types.json",
1842
+ prepublishOnly: "pnpm build",
1843
+ typecheck: "tsc --noEmit && tsc -p tsconfig.strict-check.json --noEmit && tsc -p tsconfig.augment-check.json --noEmit",
1844
+ test: "playwright test",
1845
+ "test:preload": "playwright test --config=playwright.preload.config.ts",
1846
+ "test:ui": "playwright test --ui",
1847
+ "test:hmr-local": "playwright test --project=dev-warmup --project=hmr-basename --project=hmr-prerender --no-deps --workers=1 && RANGO_E2E_ROUTE_HMR_ONLY=1 playwright test --project=hmr-routes --no-deps --workers=1",
1848
+ "test:hmr-routes-local": "RANGO_E2E_ROUTE_HMR_ONLY=1 playwright test --project=hmr-routes --no-deps --workers=1",
1849
+ "test:unit": "pnpm run build:types && vitest run",
1850
+ "test:unit:watch": "vitest",
1851
+ "test:unit:rsc": "vitest run --config vitest.rsc.config.ts"
1852
+ },
1853
+ dependencies: {
1854
+ "@types/debug": "^4.1.12",
1855
+ "@vitejs/plugin-rsc": "^0.5.36",
1856
+ debug: "^4.4.1",
1857
+ "magic-string": "^0.30.17",
1858
+ picomatch: "^4.0.4",
1859
+ "rsc-html-stream": "^0.0.7",
1860
+ srvx: "^0.11.15",
1861
+ tinyexec: "^0.3.2",
1862
+ typescript: "^5.3.0 || ^6.0.0"
1863
+ },
1864
+ devDependencies: {
1865
+ "@opentelemetry/api": "^1.9.0",
1866
+ "@opentelemetry/context-async-hooks": "^2.9.0",
1867
+ "@opentelemetry/sdk-trace-base": "^2.9.0",
1868
+ "@playwright/test": "^1.62.0",
1869
+ "@shared/e2e": "workspace:*",
1870
+ "@testing-library/dom": "^10.4.1",
1871
+ "@testing-library/react": "^16.3.2",
1872
+ "@types/node": "^24.10.1",
1873
+ "@types/react": "catalog:",
1874
+ "@types/react-dom": "catalog:",
1875
+ esbuild: "^0.28.1",
1876
+ "happy-dom": "^20.10.1",
1877
+ jiti: "^2.7.0",
1878
+ react: "catalog:",
1879
+ "react-dom": "catalog:",
1880
+ vitest: "^4.1.11"
1881
+ },
1882
+ peerDependencies: {
1883
+ "@cloudflare/vite-plugin": "^1.42.1",
1884
+ "@opentelemetry/api": "^1.9.0",
1885
+ "@playwright/test": "^1.49.1",
1886
+ "@testing-library/react": ">=16",
1887
+ "@vercel/functions": "^3.0.0",
1888
+ react: ">=19.2.8 <20",
1889
+ "react-dom": ">=19.2.8 <20",
1890
+ vite: "^8.0.16",
1891
+ vitest: ">=3"
1892
+ },
1893
+ peerDependenciesMeta: {
1894
+ "@cloudflare/vite-plugin": {
1895
+ optional: true
1896
+ },
1897
+ "@opentelemetry/api": {
1898
+ optional: true
1899
+ },
1900
+ "@playwright/test": {
1901
+ optional: true
1902
+ },
1903
+ "@testing-library/react": {
1904
+ optional: true
1905
+ },
1906
+ "@vercel/functions": {
1907
+ optional: true
1908
+ },
1909
+ vite: {
1910
+ optional: true
1911
+ },
1912
+ vitest: {
1913
+ optional: true
1914
+ }
1915
+ },
1916
+ engines: {
1917
+ node: ">=24.0.0"
1918
+ }
1919
+ };
1920
+ }
1921
+ });
1922
+
1923
+ // src/vite/utils/banner.ts
1924
+ var rangoVersion;
1925
+ var init_banner = __esm({
1926
+ "src/vite/utils/banner.ts"() {
1927
+ "use strict";
1928
+ init_package();
1929
+ rangoVersion = package_default.version;
1930
+ }
1931
+ });
1932
+
1622
1933
  // src/vite/plugins/virtual-entries.ts
1934
+ function getPackageVersionExport() {
1935
+ return `export const PACKAGE_VERSION = ${JSON.stringify(rangoVersion)};`;
1936
+ }
1937
+ function getVirtualPackageVersionContent() {
1938
+ return getPackageVersionExport();
1939
+ }
1623
1940
  function getVirtualVersionContent(version) {
1624
1941
  return [
1942
+ getPackageVersionExport(),
1625
1943
  `export const VERSION = ${JSON.stringify(version)};`,
1626
1944
  `export const ROUTER_VERSIONS = undefined;`
1627
1945
  ].join("\n");
1628
1946
  }
1629
1947
  function getVirtualBuildVersionContent() {
1630
1948
  return [
1949
+ getPackageVersionExport(),
1631
1950
  `export const ROUTER_VERSIONS = ${ROUTER_VERSIONS_PLACEHOLDER};`,
1632
1951
  `export const VERSION = ROUTER_VERSIONS[${JSON.stringify(DEFAULT_ROUTER_VERSIONS_KEY)}][1];`
1633
1952
  ].join("\n");
@@ -1637,6 +1956,7 @@ var init_virtual_entries = __esm({
1637
1956
  "src/vite/plugins/virtual-entries.ts"() {
1638
1957
  "use strict";
1639
1958
  init_router_versions();
1959
+ init_banner();
1640
1960
  VIRTUAL_ENTRY_BROWSER = `
1641
1961
  import {
1642
1962
  createFromReadableStream,
@@ -1851,6 +2171,10 @@ function createVersionPlugin() {
1851
2171
  },
1852
2172
  load(id) {
1853
2173
  if (id === "\0" + VIRTUAL_IDS.version) {
2174
+ const environment = this?.environment?.name;
2175
+ if (environment === "client") {
2176
+ return getVirtualPackageVersionContent();
2177
+ }
1854
2178
  return isDev ? getVirtualVersionContent(currentVersion) : getVirtualBuildVersionContent();
1855
2179
  }
1856
2180
  return null;
@@ -1,7 +1,15 @@
1
1
  // src/testing/vitest.ts
2
+ import { readFile } from "node:fs/promises";
2
3
  import { createRequire } from "node:module";
3
4
  import { fileURLToPath, pathToFileURL } from "node:url";
4
5
 
6
+ // src/vite/plugins/hoist/literal.ts
7
+ function parseLang(file) {
8
+ if (/\.[cm]?ts$/.test(file)) return "ts";
9
+ if (/\.[cm]?jsx?$/.test(file)) return "jsx";
10
+ return "tsx";
11
+ }
12
+
5
13
  // src/vite/plugins/use-cache-transform.ts
6
14
  import path2 from "node:path";
7
15
 
@@ -251,6 +259,22 @@ var CLIENT_ENTRY_RSC_PATH = here("src/client.rsc.tsx").replaceAll(
251
259
  var CLIENT_URLS_INSTALLER_PATH = here(
252
260
  "src/testing/internal/client-urls-projection.ts"
253
261
  ).replaceAll("\\", "/");
262
+ async function loadsAsSource(code, file) {
263
+ if (!code.includes("use client") && !code.includes("export")) return false;
264
+ const { parseAstAsync } = await import("vite");
265
+ const { hasDirective } = await import("@vitejs/plugin-rsc/transforms");
266
+ let body;
267
+ try {
268
+ body = (await parseAstAsync(code, { lang: parseLang(file) })).body;
269
+ } catch {
270
+ return false;
271
+ }
272
+ if (hasDirective(body, "use client")) return true;
273
+ const nodes = body;
274
+ return nodes.some((node) => node.type !== "ImportDeclaration") && nodes.every(
275
+ (node) => node.type === "ImportDeclaration" || node.type === "ExportAllDeclaration" || node.type === "ExportNamedDeclaration" && node.source != null
276
+ );
277
+ }
254
278
  function clientUrlsProjectionInstall(id) {
255
279
  const source = `${id}${id.includes("?") ? "&" : "?"}${CLIENT_URLS_SOURCE_QUERY}`;
256
280
  return `
@@ -309,11 +333,20 @@ function rangoUseClientTransform() {
309
333
  resolveId: {
310
334
  // Before Vite's resolver, which resolves the bare specifier first.
311
335
  order: "pre",
312
- handler(source, importer) {
313
- if (source === "@rangojs/router/client" && importer?.includes(CLIENT_URLS_SOURCE_QUERY)) {
314
- return CLIENT_ENTRY_RSC_PATH;
336
+ async handler(source, importer) {
337
+ if (!importer?.includes(CLIENT_URLS_SOURCE_QUERY)) return void 0;
338
+ if (source === "@rangojs/router/client") return CLIENT_ENTRY_RSC_PATH;
339
+ const resolved = await this.resolve(source, importer, {
340
+ skipSelf: true
341
+ });
342
+ if (!resolved || resolved.external) return void 0;
343
+ const { id } = resolved;
344
+ const file = id.split("?", 1)[0];
345
+ if (id.startsWith("\0") || id.includes("/node_modules/") || id.includes(CLIENT_URLS_SOURCE_QUERY) || !/\.[cm]?[jt]sx?$/.test(file)) {
346
+ return void 0;
315
347
  }
316
- return void 0;
348
+ const code = await readFile(file, "utf-8").catch(() => "");
349
+ return await loadsAsSource(code, file) ? `${id}${id.includes("?") ? "&" : "?"}${CLIENT_URLS_SOURCE_QUERY}` : void 0;
317
350
  }
318
351
  },
319
352
  async transform(code, id) {
@@ -39,6 +39,8 @@ export interface ActionEntry {
39
39
  result?: unknown;
40
40
  /** Error from action (set on failure) */
41
41
  error?: unknown;
42
+ /** A loader that failed while it re-ran after the action (set on success) */
43
+ revalidationError?: unknown;
42
44
  /** Segment IDs that were revalidated by this action */
43
45
  revalidatedSegments: string[];
44
46
  /** Timestamp when action started */
@@ -79,9 +81,14 @@ export type StateListener = () => void;
79
81
  */
80
82
  export type ActionStateListener = (state: TrackedActionState) => void;
81
83
  /**
82
- * Handle state listener
84
+ * Handle state listener. `signal`, from a payload update: "anchor" when it
85
+ * hands React a page in a transition (route state on screen until it
86
+ * commits), "release" when it hands one urgently; "release" again once the
87
+ * page committed (useCommitAnchoredState, react/use-handle.ts).
83
88
  */
84
- export type HandleListener = () => void;
89
+ export type HandleListener = (signal?: RouteStateSignal) => void;
90
+ /** See HandleListener. */
91
+ export type RouteStateSignal = "anchor" | "release";
85
92
  /**
86
93
  * Internal handle state stored in controller.
87
94
  *
@@ -158,8 +165,8 @@ export interface ActionHandle extends Disposable {
158
165
  * later-initiated winner's response lands and overwrites it.
159
166
  */
160
167
  claimLocationState(state: Record<string, unknown>): Record<string, unknown>;
161
- /** Complete the action with result */
162
- complete(result?: unknown): void;
168
+ /** Complete the action with result, and a loader rerun's error, if any */
169
+ complete(result?: unknown, revalidationError?: unknown): void;
163
170
  /** Fail the action with error */
164
171
  fail(error: unknown): void;
165
172
  /** Whether action was completed (success or failure) */
@@ -200,8 +207,18 @@ export interface EventController {
200
207
  * Deliver queued location, params, navigation, and handle notifications now.
201
208
  * NavigationProvider calls this inside the payload update so React assigns
202
209
  * every route-state hook update to the same normal or transition lane.
210
+ * `signal`: handle readers learn how the page is handed over. After
211
+ * "anchor" (a page in a transition) values that arrive before
212
+ * releaseRouteState() wait for it (useCommitAnchoredState,
213
+ * react/use-handle.ts).
203
214
  */
204
- flushRouteState(): void;
215
+ flushRouteState(signal?: RouteStateSignal): void;
216
+ /**
217
+ * The page the last flushRouteState("anchor") announced has committed:
218
+ * NavigationProvider calls this from its layout effect, and handle readers
219
+ * apply what waited, in that task.
220
+ */
221
+ releaseRouteState(): void;
205
222
  setHandleData(data: HandleData, matched?: string[], isPartial?: boolean,
206
223
  /**
207
224
  * Segment ids that were re-resolved on the server this request (the
@@ -269,6 +286,8 @@ export interface EventController {
269
286
  type LocationChangeController = Pick<EventController, "getState" | "subscribe">;
270
287
  /** Share one controller subscription across all location-change consumers. */
271
288
  export declare function subscribeToLocationChange(eventController: LocationChangeController, listener: (href: string) => void): () => void;
289
+ /** An action with no tracked entry: idle, nothing reported. */
290
+ export declare const DEFAULT_ACTION_STATE: TrackedActionState;
272
291
  export interface EventControllerConfig {
273
292
  initialLocation?: NavigationLocation;
274
293
  }
@@ -3,10 +3,13 @@ import type { SlotState } from "../types.js";
3
3
  /**
4
4
  * Check if a segment is an intercept segment.
5
5
  * Intercept segments have namespace starting with "intercept:" — both the
6
- * parallel container (@modal) and its content children receive this namespace
6
+ * parallel container (@modal) and its loader segments receive this namespace
7
7
  * from intercept-resolution.ts. Regular parallel segments like @sidebar do not.
8
+ * Server-safe: the cache and match pipeline share it.
8
9
  */
9
- export declare function isInterceptSegment(s: ResolvedSegment): boolean;
10
+ export declare function isInterceptSegment(s: {
11
+ namespace?: string;
12
+ }): boolean;
10
13
  /**
11
14
  * Split an array of segments into main and intercept groups.
12
15
  * Intercept segments are separated for explicit injection into the render tree
@@ -1,27 +1,4 @@
1
1
  import type { ResolvedSegment } from "./types.js";
2
- /**
3
- * Merge partial loader data from server with cached loader data.
4
- *
5
- * During partial revalidation (stale or action), the server may return only
6
- * some loaders that pass the revalidation check, none when every one is held.
7
- * The component still needs all loader data, so we merge fresh data with
8
- * cached data. Two settled aggregates merge into the array itself: a fresh
9
- * Promise.all over settled parts is a promise React has not read, and content
10
- * on screen handed one suspends (docs/internal/suspense-contract.md, rule 2).
11
- *
12
- * @param fromServer - Segment returned from server with partial loaders
13
- * @param fromCache - Cached segment with full loader data
14
- * @returns Merged segment with complete loader data
15
- */
16
- export declare function mergeSegmentLoaders(fromServer: ResolvedSegment, fromCache: ResolvedSegment): ResolvedSegment;
17
- /**
18
- * Check if segments need loader merging during partial revalidation.
19
- *
20
- * Returns true when:
21
- * - Server returned fewer loaders than cached (partial revalidation)
22
- * - Both segments have loader data promises
23
- */
24
- export declare function needsLoaderMerge(fromServer: ResolvedSegment, fromCache: ResolvedSegment | undefined): fromCache is ResolvedSegment;
25
2
  /**
26
3
  * Insert diff segments that aren't in the matched array into allSegments.
27
4
  *
@@ -1,4 +1,4 @@
1
- import type { NavigateOptionsInternal, NavigationStore, ResolvedSegment, StreamingToken } from "./types.js";
1
+ import type { NavigateOptionsInternal, NavigationStore, ResolvedSegment, ShownEntry, StreamingToken } from "./types.js";
2
2
  import type { EventController, NavigationHandle } from "./event-controller.js";
3
3
  export { resolveNavigationState } from "./history-state.js";
4
4
  /**
@@ -28,8 +28,8 @@ interface CommitOptions {
28
28
  * merged in. The page left was saved by handleTraversalStart.
29
29
  */
30
30
  traversal?: boolean;
31
- /** The committed route's name (payload metadata), remembered per history entry. */
32
- routeName?: string;
31
+ /** The payload's metadata: its route name and rewrite target, remembered per history entry. */
32
+ metadata?: ShownEntry;
33
33
  /**
34
34
  * transition({ when }) decision already made at this navigation's first
35
35
  * presentation (the optimistic clientUrls() swap). Not a commit input: the
@@ -60,8 +60,8 @@ interface BoundCommitOverrides {
60
60
  cacheOnly?: boolean;
61
61
  /** Server-set location state to merge into history.pushState */
62
62
  serverState?: Record<string, unknown>;
63
- /** The committed route's name (payload metadata), remembered per history entry. */
64
- routeName?: string;
63
+ /** The payload's metadata: its route name and rewrite target, remembered per history entry. */
64
+ metadata?: ShownEntry;
65
65
  /** No payload follows this commit (CommitOptions.treeless). */
66
66
  treeless?: boolean;
67
67
  }