@rangojs/router 0.0.0-experimental.20 → 0.0.0-experimental.204030a9

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 (293) hide show
  1. package/AGENTS.md +4 -0
  2. package/README.md +242 -55
  3. package/dist/bin/rango.js +277 -99
  4. package/dist/vite/index.js +2929 -1132
  5. package/dist/vite/index.js.bak +5448 -0
  6. package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
  7. package/package.json +68 -21
  8. package/skills/breadcrumbs/SKILL.md +252 -0
  9. package/skills/bundle-analysis/SKILL.md +159 -0
  10. package/skills/cache-guide/SKILL.md +243 -21
  11. package/skills/caching/SKILL.md +159 -10
  12. package/skills/composability/SKILL.md +27 -2
  13. package/skills/document-cache/SKILL.md +78 -55
  14. package/skills/handler-use/SKILL.md +364 -0
  15. package/skills/hooks/SKILL.md +262 -51
  16. package/skills/host-router/SKILL.md +243 -0
  17. package/skills/i18n/SKILL.md +276 -0
  18. package/skills/intercept/SKILL.md +46 -4
  19. package/skills/layout/SKILL.md +28 -7
  20. package/skills/links/SKILL.md +249 -17
  21. package/skills/loader/SKILL.md +291 -31
  22. package/skills/middleware/SKILL.md +49 -12
  23. package/skills/migrate-nextjs/SKILL.md +562 -0
  24. package/skills/migrate-react-router/SKILL.md +769 -0
  25. package/skills/mime-routes/SKILL.md +27 -0
  26. package/skills/observability/SKILL.md +137 -0
  27. package/skills/parallel/SKILL.md +197 -6
  28. package/skills/prerender/SKILL.md +125 -102
  29. package/skills/rango/SKILL.md +242 -23
  30. package/skills/react-compiler/SKILL.md +168 -0
  31. package/skills/response-routes/SKILL.md +66 -9
  32. package/skills/route/SKILL.md +91 -8
  33. package/skills/router-setup/SKILL.md +98 -8
  34. package/skills/server-actions/SKILL.md +751 -0
  35. package/skills/streams-and-websockets/SKILL.md +283 -0
  36. package/skills/testing/SKILL.md +511 -188
  37. package/skills/typesafety/SKILL.md +354 -50
  38. package/skills/use-cache/SKILL.md +34 -5
  39. package/skills/view-transitions/SKILL.md +294 -0
  40. package/src/__augment-tests__/augment.ts +81 -0
  41. package/src/__augment-tests__/augmented.check.ts +117 -0
  42. package/src/__internal.ts +92 -0
  43. package/src/browser/action-coordinator.ts +53 -36
  44. package/src/browser/app-shell.ts +52 -0
  45. package/src/browser/app-version.ts +14 -0
  46. package/src/browser/event-controller.ts +91 -70
  47. package/src/browser/history-state.ts +21 -0
  48. package/src/browser/index.ts +3 -3
  49. package/src/browser/link-interceptor.ts +4 -0
  50. package/src/browser/navigation-bridge.ts +183 -18
  51. package/src/browser/navigation-client.ts +187 -57
  52. package/src/browser/navigation-store.ts +75 -17
  53. package/src/browser/navigation-transaction.ts +21 -37
  54. package/src/browser/partial-update.ts +143 -40
  55. package/src/browser/prefetch/cache.ts +275 -28
  56. package/src/browser/prefetch/fetch.ts +191 -46
  57. package/src/browser/prefetch/policy.ts +6 -0
  58. package/src/browser/prefetch/queue.ts +123 -20
  59. package/src/browser/prefetch/resource-ready.ts +77 -0
  60. package/src/browser/rango-state.ts +53 -13
  61. package/src/browser/react/Link.tsx +98 -14
  62. package/src/browser/react/NavigationProvider.tsx +110 -33
  63. package/src/browser/react/context.ts +7 -2
  64. package/src/browser/react/filter-segment-order.ts +51 -7
  65. package/src/browser/react/index.ts +3 -0
  66. package/src/browser/react/location-state-shared.ts +175 -4
  67. package/src/browser/react/location-state.ts +39 -13
  68. package/src/browser/react/use-handle.ts +23 -64
  69. package/src/browser/react/use-navigation.ts +22 -2
  70. package/src/browser/react/use-params.ts +20 -8
  71. package/src/browser/react/use-reverse.ts +106 -0
  72. package/src/browser/react/use-router.ts +43 -10
  73. package/src/browser/react/use-segments.ts +11 -8
  74. package/src/browser/response-adapter.ts +25 -0
  75. package/src/browser/rsc-router.tsx +200 -75
  76. package/src/browser/scroll-restoration.ts +46 -39
  77. package/src/browser/segment-reconciler.ts +36 -9
  78. package/src/browser/segment-structure-assert.ts +2 -2
  79. package/src/browser/server-action-bridge.ts +31 -36
  80. package/src/browser/types.ts +81 -5
  81. package/src/build/collect-fallback-refs.ts +107 -0
  82. package/src/build/generate-manifest.ts +65 -40
  83. package/src/build/generate-route-types.ts +5 -0
  84. package/src/build/index.ts +2 -0
  85. package/src/build/route-trie.ts +69 -26
  86. package/src/build/route-types/codegen.ts +4 -4
  87. package/src/build/route-types/include-resolution.ts +9 -2
  88. package/src/build/route-types/per-module-writer.ts +7 -4
  89. package/src/build/route-types/router-processing.ts +278 -88
  90. package/src/build/route-types/scan-filter.ts +9 -2
  91. package/src/build/route-types/source-scan.ts +118 -0
  92. package/src/build/runtime-discovery.ts +9 -20
  93. package/src/cache/cache-runtime.ts +15 -11
  94. package/src/cache/cache-scope.ts +76 -49
  95. package/src/cache/cf/cf-cache-store.ts +501 -18
  96. package/src/cache/cf/index.ts +5 -1
  97. package/src/cache/document-cache.ts +17 -7
  98. package/src/cache/index.ts +1 -0
  99. package/src/cache/taint.ts +55 -0
  100. package/src/client.rsc.tsx +5 -1
  101. package/src/client.tsx +95 -284
  102. package/src/context-var.ts +72 -2
  103. package/src/debug.ts +2 -2
  104. package/src/decode-loader-results.ts +36 -0
  105. package/src/errors.ts +30 -1
  106. package/src/handle.ts +65 -12
  107. package/src/handles/breadcrumbs.ts +66 -0
  108. package/src/handles/index.ts +1 -0
  109. package/src/host/index.ts +2 -5
  110. package/src/host/router.ts +129 -57
  111. package/src/host/types.ts +31 -2
  112. package/src/host/utils.ts +1 -1
  113. package/src/href-client.ts +140 -20
  114. package/src/index.rsc.ts +15 -40
  115. package/src/index.ts +92 -76
  116. package/src/loader-store.ts +500 -0
  117. package/src/loader.rsc.ts +2 -5
  118. package/src/loader.ts +3 -10
  119. package/src/missing-id-error.ts +68 -0
  120. package/src/outlet-context.ts +1 -1
  121. package/src/prerender/store.ts +57 -15
  122. package/src/prerender.ts +141 -80
  123. package/src/response-utils.ts +37 -0
  124. package/src/reverse.ts +65 -15
  125. package/src/route-content-wrapper.tsx +6 -28
  126. package/src/route-definition/dsl-helpers.ts +435 -260
  127. package/src/route-definition/helper-factories.ts +29 -139
  128. package/src/route-definition/helpers-types.ts +110 -34
  129. package/src/route-definition/index.ts +3 -3
  130. package/src/route-definition/redirect.ts +11 -3
  131. package/src/route-definition/resolve-handler-use.ts +155 -0
  132. package/src/route-definition/use-item-types.ts +32 -0
  133. package/src/route-map-builder.ts +7 -1
  134. package/src/route-types.ts +37 -41
  135. package/src/router/basename.ts +14 -0
  136. package/src/router/content-negotiation.ts +113 -1
  137. package/src/router/error-handling.ts +1 -1
  138. package/src/router/find-match.ts +4 -2
  139. package/src/router/handler-context.ts +105 -39
  140. package/src/router/intercept-resolution.ts +15 -22
  141. package/src/router/lazy-includes.ts +12 -9
  142. package/src/router/loader-resolution.ts +175 -23
  143. package/src/router/logging.ts +5 -2
  144. package/src/router/manifest.ts +31 -16
  145. package/src/router/match-api.ts +129 -193
  146. package/src/router/match-handlers.ts +63 -20
  147. package/src/router/match-middleware/background-revalidation.ts +30 -2
  148. package/src/router/match-middleware/cache-lookup.ts +136 -106
  149. package/src/router/match-middleware/cache-store.ts +54 -10
  150. package/src/router/match-middleware/intercept-resolution.ts +9 -7
  151. package/src/router/match-middleware/segment-resolution.ts +61 -5
  152. package/src/router/match-result.ts +124 -18
  153. package/src/router/metrics.ts +239 -14
  154. package/src/router/middleware-types.ts +61 -31
  155. package/src/router/middleware.ts +226 -124
  156. package/src/router/navigation-snapshot.ts +182 -0
  157. package/src/router/pattern-matching.ts +118 -19
  158. package/src/router/prerender-match.ts +114 -10
  159. package/src/router/preview-match.ts +32 -102
  160. package/src/router/request-classification.ts +286 -0
  161. package/src/router/revalidation.ts +85 -9
  162. package/src/router/route-snapshot.ts +245 -0
  163. package/src/router/router-context.ts +6 -1
  164. package/src/router/router-interfaces.ts +91 -29
  165. package/src/router/router-options.ts +89 -19
  166. package/src/router/router-registry.ts +2 -5
  167. package/src/router/segment-resolution/fresh.ts +240 -23
  168. package/src/router/segment-resolution/helpers.ts +30 -25
  169. package/src/router/segment-resolution/loader-cache.ts +1 -0
  170. package/src/router/segment-resolution/revalidation.ts +483 -289
  171. package/src/router/segment-resolution/view-transition-default.ts +36 -0
  172. package/src/router/segment-wrappers.ts +2 -0
  173. package/src/router/substitute-pattern-params.ts +56 -0
  174. package/src/router/telemetry.ts +99 -0
  175. package/src/router/trie-matching.ts +38 -15
  176. package/src/router/types.ts +9 -0
  177. package/src/router/url-params.ts +49 -0
  178. package/src/router.ts +120 -32
  179. package/src/rsc/handler-context.ts +2 -2
  180. package/src/rsc/handler.ts +524 -370
  181. package/src/rsc/helpers.ts +91 -43
  182. package/src/rsc/index.ts +1 -21
  183. package/src/rsc/loader-fetch.ts +23 -3
  184. package/src/rsc/manifest-init.ts +5 -1
  185. package/src/rsc/origin-guard.ts +28 -10
  186. package/src/rsc/progressive-enhancement.ts +39 -10
  187. package/src/rsc/response-route-handler.ts +46 -53
  188. package/src/rsc/rsc-rendering.ts +69 -89
  189. package/src/rsc/runtime-warnings.ts +9 -10
  190. package/src/rsc/server-action.ts +39 -47
  191. package/src/rsc/ssr-setup.ts +144 -0
  192. package/src/rsc/types.ts +19 -3
  193. package/src/search-params.ts +20 -17
  194. package/src/segment-content-promise.ts +67 -0
  195. package/src/segment-loader-promise.ts +122 -0
  196. package/src/segment-system.tsx +219 -67
  197. package/src/serialize.ts +243 -0
  198. package/src/server/context.ts +285 -63
  199. package/src/server/cookie-store.ts +28 -4
  200. package/src/server/handle-store.ts +19 -0
  201. package/src/server/loader-registry.ts +9 -8
  202. package/src/server/request-context.ts +228 -65
  203. package/src/server.ts +6 -0
  204. package/src/ssr/index.tsx +9 -1
  205. package/src/static-handler.ts +19 -7
  206. package/src/testing/cache-status.ts +166 -0
  207. package/src/testing/collect-handle.ts +63 -0
  208. package/src/testing/dispatch.ts +440 -0
  209. package/src/testing/dom.entry.ts +22 -0
  210. package/src/testing/e2e/fixture.ts +154 -0
  211. package/src/testing/e2e/index.ts +149 -0
  212. package/src/testing/e2e/matchers.ts +51 -0
  213. package/src/testing/e2e/page-helpers.ts +272 -0
  214. package/src/testing/e2e/parity.ts +306 -0
  215. package/src/testing/e2e/server.ts +183 -0
  216. package/src/testing/flight-matchers.ts +104 -0
  217. package/src/testing/flight-runtime.d.ts +21 -0
  218. package/src/testing/flight.entry.ts +22 -0
  219. package/src/testing/flight.ts +182 -0
  220. package/src/testing/generated-routes.ts +223 -0
  221. package/src/testing/index.ts +98 -0
  222. package/src/testing/internal/context.ts +151 -0
  223. package/src/testing/render-route.tsx +536 -0
  224. package/src/testing/run-loader.ts +296 -0
  225. package/src/testing/run-middleware.ts +170 -0
  226. package/src/testing/vitest-stubs/cloudflare-email.ts +9 -0
  227. package/src/testing/vitest-stubs/cloudflare-workers.ts +21 -0
  228. package/src/testing/vitest-stubs/plugin-rsc.ts +16 -0
  229. package/src/testing/vitest-stubs/version.ts +5 -0
  230. package/src/testing/vitest.ts +112 -0
  231. package/src/theme/index.ts +4 -13
  232. package/src/types/cache-types.ts +4 -4
  233. package/src/types/global-namespace.ts +39 -26
  234. package/src/types/handler-context.ts +197 -79
  235. package/src/types/index.ts +1 -0
  236. package/src/types/loader-types.ts +41 -15
  237. package/src/types/request-scope.ts +126 -0
  238. package/src/types/route-config.ts +17 -8
  239. package/src/types/route-entry.ts +19 -1
  240. package/src/types/segments.ts +37 -6
  241. package/src/urls/include-helper.ts +34 -67
  242. package/src/urls/index.ts +0 -3
  243. package/src/urls/path-helper-types.ts +50 -9
  244. package/src/urls/path-helper.ts +63 -63
  245. package/src/urls/pattern-types.ts +48 -19
  246. package/src/urls/response-types.ts +25 -22
  247. package/src/urls/type-extraction.ts +26 -116
  248. package/src/urls/urls-function.ts +1 -5
  249. package/src/use-loader.tsx +487 -44
  250. package/src/vite/debug.ts +185 -0
  251. package/src/vite/discovery/bundle-postprocess.ts +63 -91
  252. package/src/vite/discovery/discover-routers.ts +106 -53
  253. package/src/vite/discovery/discovery-errors.ts +194 -0
  254. package/src/vite/discovery/gate-state.ts +171 -0
  255. package/src/vite/discovery/prerender-collection.ts +222 -107
  256. package/src/vite/discovery/route-types-writer.ts +40 -84
  257. package/src/vite/discovery/self-gen-tracking.ts +27 -1
  258. package/src/vite/discovery/state.ts +50 -13
  259. package/src/vite/discovery/virtual-module-codegen.ts +13 -23
  260. package/src/vite/index.ts +10 -3
  261. package/src/vite/plugin-types.ts +111 -72
  262. package/src/vite/plugins/cjs-to-esm.ts +8 -7
  263. package/src/vite/plugins/client-ref-dedup.ts +16 -0
  264. package/src/vite/plugins/client-ref-hashing.ts +28 -5
  265. package/src/vite/plugins/cloudflare-protocol-loader-hook.d.mts +23 -0
  266. package/src/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
  267. package/src/vite/plugins/cloudflare-protocol-stub.ts +214 -0
  268. package/src/vite/plugins/expose-action-id.ts +55 -33
  269. package/src/vite/plugins/expose-id-utils.ts +24 -8
  270. package/src/vite/plugins/expose-ids/export-analysis.ts +100 -20
  271. package/src/vite/plugins/expose-ids/handler-transform.ts +12 -35
  272. package/src/vite/plugins/expose-ids/loader-transform.ts +3 -5
  273. package/src/vite/plugins/expose-ids/router-transform.ts +20 -3
  274. package/src/vite/plugins/expose-internal-ids.ts +544 -317
  275. package/src/vite/plugins/performance-tracks.ts +92 -0
  276. package/src/vite/plugins/refresh-cmd.ts +127 -0
  277. package/src/vite/plugins/use-cache-transform.ts +65 -50
  278. package/src/vite/plugins/version-injector.ts +39 -23
  279. package/src/vite/plugins/version-plugin.ts +72 -3
  280. package/src/vite/plugins/virtual-entries.ts +2 -2
  281. package/src/vite/rango.ts +265 -226
  282. package/src/vite/router-discovery.ts +924 -137
  283. package/src/vite/utils/ast-handler-extract.ts +15 -15
  284. package/src/vite/utils/banner.ts +4 -4
  285. package/src/vite/utils/bundle-analysis.ts +4 -2
  286. package/src/vite/utils/client-chunks.ts +190 -0
  287. package/src/vite/utils/forward-user-plugins.ts +193 -0
  288. package/src/vite/utils/manifest-utils.ts +21 -5
  289. package/src/vite/utils/package-resolution.ts +41 -1
  290. package/src/vite/utils/prerender-utils.ts +98 -5
  291. package/src/vite/utils/shared-utils.ts +109 -27
  292. package/src/browser/action-response-classifier.ts +0 -99
  293. package/src/route-definition/route-function.ts +0 -119
@@ -0,0 +1,245 @@
1
+ /**
2
+ * Route Snapshot
3
+ *
4
+ * Pure data type representing the fully-resolved state of a single route match.
5
+ * Consolidates the duplicated findMatch + loadManifest + collectRouteMiddleware +
6
+ * cacheScope derivation that previously lived separately in preview-match.ts
7
+ * and match-api.ts.
8
+ *
9
+ * resolveRoute() is the factory: given a pathname and dependencies, it returns
10
+ * a RouteSnapshot (or redirect/null). Consumers (createMatchContextForFull,
11
+ * createMatchContextForPartial, previewMatch) read snapshot fields instead of
12
+ * re-deriving them.
13
+ */
14
+
15
+ import type { CacheScope } from "../cache/cache-scope.js";
16
+ import { createCacheScope } from "../cache/cache-scope.js";
17
+ import type { EntryData, MetricsStore } from "../server/context.js";
18
+ import { loadManifest } from "./manifest.js";
19
+ import { collectRouteMiddleware } from "./middleware.js";
20
+ import type { CollectedMiddleware } from "./middleware-types.js";
21
+ import { traverseBack } from "./pattern-matching.js";
22
+ import type { RouteMatchResult } from "./pattern-matching.js";
23
+
24
+ /**
25
+ * Immutable snapshot of a resolved route match.
26
+ *
27
+ * Contains everything derivable from (pathname, findMatch, loadManifest)
28
+ * without request context, navigation state, or intercept logic.
29
+ */
30
+ export interface RouteSnapshot<TEnv = any> {
31
+ /** Raw match result from the trie/pattern matcher */
32
+ matched: RouteMatchResult<TEnv>;
33
+ /** Resolved manifest entry (with loaded handler, loader, etc.) */
34
+ manifestEntry: EntryData;
35
+ /** All entries in the route chain (from traverseBack) */
36
+ entries: EntryData[];
37
+ /** Canonical route key (e.g. "blog.detail") */
38
+ routeKey: string;
39
+ /** Last segment of a dotted route key (e.g. "detail" from "blog.detail") */
40
+ localRouteName: string;
41
+ /** Extracted route params */
42
+ params: Record<string, string>;
43
+ /** Collected route-level middleware from the entry tree */
44
+ routeMiddleware: CollectedMiddleware[];
45
+ /** Merged cache scope from the entry chain */
46
+ cacheScope: CacheScope | null;
47
+ /** Whether the matched route is a passthrough route */
48
+ isPassthrough: boolean;
49
+ /** Response type for non-RSC routes (e.g. "application/json") */
50
+ responseType?: string;
51
+ }
52
+
53
+ export type ResolveRouteResult<TEnv = any> =
54
+ | { type: "match"; snapshot: RouteSnapshot<TEnv> }
55
+ | { type: "redirect"; redirectTo: string }
56
+ | null;
57
+
58
+ export interface ResolveRouteDeps<TEnv = any> {
59
+ findMatch: (pathname: string) => RouteMatchResult<TEnv> | null;
60
+ metricsStore?: MetricsStore;
61
+ isSSR?: boolean;
62
+ /**
63
+ * When true, skip entries array and cacheScope chain construction.
64
+ * Used by previewMatch which only needs matched, manifestEntry,
65
+ * routeMiddleware, and responseType — avoids an extra traverseBack
66
+ * allocation and cacheScope composition on the hot classification path.
67
+ */
68
+ lite?: boolean;
69
+ /**
70
+ * When true, skip pushing the "route-matching" metric internally.
71
+ * Used by createMatchContextForPartial on the fresh path (no snapshot
72
+ * reuse) so it can measure current + prev + intercept-source findMatch
73
+ * calls under one combined "route-matching" metric. On the reuse path,
74
+ * the partial path emits "route-matching:nav" for the prev +
75
+ * intercept-source lookups only (current-route resolution was done
76
+ * during classification without metrics).
77
+ */
78
+ skipRouteMatchMetric?: boolean;
79
+ }
80
+
81
+ /**
82
+ * Resolve a pathname into a RouteSnapshot.
83
+ *
84
+ * This is the single source of truth for route derivation. It performs:
85
+ * 1. findMatch(pathname)
86
+ * 2. Redirect check
87
+ * 3. loadManifest
88
+ * 4. Passthrough detection
89
+ * 5. collectRouteMiddleware
90
+ * 6. Cache scope chain
91
+ * 7. responseType + localRouteName extraction
92
+ *
93
+ * Metrics timing is preserved identically to the previous inline code.
94
+ */
95
+ export async function resolveRoute<TEnv = any>(
96
+ pathname: string,
97
+ deps: ResolveRouteDeps<TEnv>,
98
+ ): Promise<ResolveRouteResult<TEnv>> {
99
+ const {
100
+ metricsStore,
101
+ isSSR = false,
102
+ lite = false,
103
+ skipRouteMatchMetric = false,
104
+ } = deps;
105
+
106
+ const routeMatchStart =
107
+ metricsStore && !skipRouteMatchMetric ? performance.now() : 0;
108
+ const matched = deps.findMatch(pathname);
109
+ if (metricsStore && !skipRouteMatchMetric) {
110
+ metricsStore.metrics.push({
111
+ label: "route-matching",
112
+ duration: performance.now() - routeMatchStart,
113
+ startTime: routeMatchStart - metricsStore.requestStart,
114
+ });
115
+ }
116
+
117
+ if (!matched) {
118
+ return null;
119
+ }
120
+
121
+ if (matched.redirectTo) {
122
+ return { type: "redirect", redirectTo: matched.redirectTo };
123
+ }
124
+
125
+ const manifestStart = metricsStore ? performance.now() : 0;
126
+ const manifestEntry = await loadManifest(
127
+ matched.entry,
128
+ matched.routeKey,
129
+ pathname,
130
+ metricsStore,
131
+ isSSR,
132
+ );
133
+ if (metricsStore) {
134
+ metricsStore.metrics.push({
135
+ label: "manifest-loading",
136
+ duration: performance.now() - manifestStart,
137
+ startTime: manifestStart - metricsStore.requestStart,
138
+ });
139
+ }
140
+
141
+ const isPassthrough =
142
+ manifestEntry.type === "route" && manifestEntry.isPassthrough === true;
143
+
144
+ let entries: EntryData[];
145
+ let cacheScope: CacheScope | null = null;
146
+ if (lite) {
147
+ entries = [];
148
+ } else {
149
+ ({ entries, cacheScope } = buildEntriesAndCacheScope(manifestEntry));
150
+ }
151
+
152
+ const routeMiddleware = collectRouteMiddleware(
153
+ lite ? traverseBack(manifestEntry) : entries,
154
+ matched.params,
155
+ );
156
+
157
+ const responseType =
158
+ matched.responseType ||
159
+ (manifestEntry.type === "route" ? manifestEntry.responseType : undefined);
160
+
161
+ const localRouteName = matched.routeKey.includes(".")
162
+ ? matched.routeKey.split(".").pop()!
163
+ : matched.routeKey;
164
+
165
+ return {
166
+ type: "match",
167
+ snapshot: {
168
+ matched,
169
+ manifestEntry,
170
+ entries,
171
+ routeKey: matched.routeKey,
172
+ localRouteName,
173
+ params: matched.params,
174
+ routeMiddleware,
175
+ cacheScope,
176
+ isPassthrough,
177
+ responseType,
178
+ },
179
+ };
180
+ }
181
+
182
+ /**
183
+ * Fill in the entries and cacheScope fields on a lite snapshot.
184
+ *
185
+ * When classifyRequest produces a lite snapshot (entries=[], cacheScope=null),
186
+ * this function computes the missing fields from manifestEntry without
187
+ * re-running findMatch, loadManifest, or collectRouteMiddleware.
188
+ *
189
+ * If the snapshot already has entries, returns it as-is.
190
+ */
191
+ export function ensureFullRouteSnapshot<TEnv = any>(
192
+ snapshot: RouteSnapshot<TEnv>,
193
+ ): RouteSnapshot<TEnv> {
194
+ if (snapshot.entries.length > 0) {
195
+ return snapshot;
196
+ }
197
+
198
+ const { entries, cacheScope } = buildEntriesAndCacheScope(
199
+ snapshot.manifestEntry,
200
+ );
201
+ return { ...snapshot, entries, cacheScope };
202
+ }
203
+
204
+ /**
205
+ * Materialize the entry chain and derive the merged cache scope.
206
+ * Shared by resolveRoute (non-lite) and ensureFullRouteSnapshot.
207
+ */
208
+ function buildEntriesAndCacheScope(manifestEntry: EntryData): {
209
+ entries: EntryData[];
210
+ cacheScope: CacheScope | null;
211
+ } {
212
+ const entries = [...traverseBack(manifestEntry)];
213
+ let cacheScope: CacheScope | null = null;
214
+ for (const entry of entries) {
215
+ if (entry.cache) {
216
+ cacheScope = createCacheScope(entry.cache, cacheScope);
217
+ }
218
+ }
219
+ return { entries, cacheScope };
220
+ }
221
+
222
+ /**
223
+ * Test helper: create a RouteSnapshot with sensible defaults and overrides.
224
+ */
225
+ export function createRouteSnapshot<TEnv = any>(
226
+ overrides?: Partial<RouteSnapshot<TEnv>>,
227
+ ): RouteSnapshot<TEnv> {
228
+ return {
229
+ matched: {
230
+ entry: {} as any,
231
+ routeKey: "test",
232
+ params: {},
233
+ optionalParams: new Set(),
234
+ } as RouteMatchResult<TEnv>,
235
+ manifestEntry: { type: "route", shortCode: "R0", parent: null } as any,
236
+ entries: [],
237
+ routeKey: "test",
238
+ localRouteName: "test",
239
+ params: {},
240
+ routeMiddleware: [],
241
+ cacheScope: null,
242
+ isPassthrough: false,
243
+ ...overrides,
244
+ };
245
+ }
@@ -138,6 +138,7 @@ export interface RouterContext<TEnv = any> {
138
138
  interceptResult: InterceptResult | null,
139
139
  localRouteName: string,
140
140
  pathname: string,
141
+ stale?: boolean,
141
142
  ) => Promise<{ segments: ResolvedSegment[]; matchedIds: string[] }>;
142
143
 
143
144
  // Generator-based segment resolution (for pipeline)
@@ -188,7 +189,10 @@ export interface RouterContext<TEnv = any> {
188
189
  | "cache-hit"
189
190
  | "loader"
190
191
  | "parallel"
191
- | "orphan-layout";
192
+ | "orphan-layout"
193
+ | "route-handler"
194
+ | "layout-handler"
195
+ | "intercept-loader";
192
196
  }) => Promise<boolean>;
193
197
 
194
198
  // Request context
@@ -206,6 +210,7 @@ export interface RouterContext<TEnv = any> {
206
210
  params: Record<string, string>,
207
211
  handlerContext: HandlerContext<any, TEnv>,
208
212
  loaderPromises: Map<string, Promise<any>>,
213
+ options?: { skipLoaders?: boolean },
209
214
  ) => Promise<ResolvedSegment[]>;
210
215
 
211
216
  // Generator-based simple resolution
@@ -2,6 +2,7 @@ import type { ComponentType, ReactNode } from "react";
2
2
  import type { SerializedManifest } from "../debug.js";
3
3
  import type { ReverseFunction } from "../reverse.js";
4
4
  import type { UrlPatterns } from "../urls.js";
5
+ import type { UrlBuilder, EnvCompatible } from "../urls/pattern-types.js";
5
6
  import type { EntryData } from "../server/context";
6
7
  import type { ErrorInfo, MatchResult } from "../types";
7
8
  import type { NonceProvider } from "../rsc/types.js";
@@ -12,7 +13,7 @@ import type {
12
13
  } from "../cache/types.js";
13
14
  import type { MiddlewareEntry, MiddlewareFn } from "./middleware.js";
14
15
  import { RSC_ROUTER_BRAND } from "./router-registry.js";
15
- import type { RSCRouterOptions, RootLayoutProps } from "./router-options.js";
16
+ import type { RangoOptions, RootLayoutProps } from "./router-options.js";
16
17
  import type { DefaultVars } from "../types/global-namespace.js";
17
18
  import type { ResolvedTimeouts, OnTimeoutCallback } from "./timeout.js";
18
19
 
@@ -48,16 +49,16 @@ type MergeRoutesWithResponses<
48
49
  };
49
50
 
50
51
  /**
51
- * Public RSC Router interface — the user-facing API surface.
52
+ * Public Rango router interface — the user-facing API surface.
52
53
  *
53
54
  * Users interact with this type when building and using routers.
54
- * Internal framework code uses RSCRouterInternal (via toInternal()) to access
55
+ * Internal framework code uses RangoInternal (via toInternal()) to access
55
56
  * matching, build-time, and configuration members that are not part of the
56
57
  * public contract.
57
58
  *
58
59
  * TRoutes accumulates all registered route types through the builder chain.
59
60
  */
60
- export interface RSCRouter<
61
+ export interface Rango<
61
62
  TEnv = any,
62
63
  TRoutes extends Record<string, unknown> = Record<string, string>,
63
64
  > {
@@ -68,23 +69,36 @@ export interface RSCRouter<
68
69
  readonly id: string;
69
70
 
70
71
  /**
71
- * Register routes using URL patterns from urls()
72
+ * URL prefix applied to all routes. Undefined when no basename is configured.
73
+ */
74
+ readonly basename: string | undefined;
75
+
76
+ /**
77
+ * Register routes using URL patterns from urls() or a builder function
72
78
  *
73
79
  * @example
74
80
  * ```typescript
75
- * createRouter({})
76
- * .routes(urlpatterns)
81
+ * // With urls()
82
+ * createRouter({}).routes(urlpatterns)
83
+ *
84
+ * // With builder function (urls() is implicit)
85
+ * createRouter({}).routes(({ path, layout }) => [
86
+ * layout(RootLayout, () => [
87
+ * path("/", HomePage),
88
+ * ]),
89
+ * ])
77
90
  * ```
78
91
  */
79
- routes<T extends UrlPatterns<TEnv, any>>(
80
- patterns: T,
81
- ): RSCRouter<
92
+ routes<T extends UrlPatterns<any, any, any>>(
93
+ patterns: T & EnvCompatible<T, TEnv>,
94
+ ): Rango<
82
95
  TEnv,
83
96
  TRoutes &
84
97
  (NonNullable<T["_routes"]> extends Record<string, unknown>
85
98
  ? MergeRoutesWithResponses<NonNullable<T["_routes"]>, T["_responses"]>
86
99
  : Record<string, string>)
87
100
  >;
101
+ routes(builder: UrlBuilder<TEnv>): Rango<TEnv, TRoutes>;
88
102
 
89
103
  /**
90
104
  * Add global middleware that runs on all routes
@@ -100,7 +114,7 @@ export interface RSCRouter<
100
114
  use(
101
115
  patternOrMiddleware: string | MiddlewareFn<TEnv>,
102
116
  middleware?: MiddlewareFn<TEnv>,
103
- ): RSCRouter<TEnv, TRoutes>;
117
+ ): Rango<TEnv, TRoutes>;
104
118
 
105
119
  /**
106
120
  * Type-safe URL builder for registered routes
@@ -127,7 +141,7 @@ export interface RSCRouter<
127
141
  * type AppRoutes = typeof _router.routeMap;
128
142
  *
129
143
  * declare global {
130
- * namespace RSCRouter {
144
+ * namespace Rango {
131
145
  * interface RegisteredRoutes extends AppRoutes {}
132
146
  * }
133
147
  * }
@@ -163,16 +177,16 @@ export interface RSCRouter<
163
177
  }
164
178
 
165
179
  /**
166
- * Internal RSC Router interface — the full framework-facing API.
180
+ * Internal Rango router interface — the full framework-facing API.
167
181
  *
168
182
  * This type includes all members used by the Vite plugin, RSC handler,
169
183
  * pre-rendering pipeline, and other framework internals. It is NOT exported
170
184
  * from the public package API.
171
185
  *
172
- * Use toInternal(router) to assert a public RSCRouter into this type
186
+ * Use toInternal(router) to assert a public Rango into this type
173
187
  * at the boundary where framework code receives a user-provided router.
174
188
  */
175
- export interface RSCRouterInternal<
189
+ export interface RangoInternal<
176
190
  TEnv = any,
177
191
  TRoutes extends Record<string, unknown> = Record<string, string>,
178
192
  > {
@@ -188,18 +202,28 @@ export interface RSCRouterInternal<
188
202
  */
189
203
  readonly id: string;
190
204
 
205
+ /** URL prefix applied to all routes. */
206
+ readonly basename: string | undefined;
207
+
191
208
  /**
192
- * Register routes using URL patterns from urls()
193
- */
194
- routes<T extends UrlPatterns<TEnv, any>>(
195
- patterns: T,
196
- ): RSCRouter<
209
+ * Register routes using URL patterns from urls() or a builder function.
210
+ *
211
+ * Env compatibility is checked by EnvCompatible: an env-agnostic urls() block
212
+ * (its env is `unknown` — e.g. a shared module, or an app that does not augment
213
+ * `Rango.Env`) attaches to any router, while a urls<TEnv>() block carrying a
214
+ * concrete env is accepted only when this router's `TEnv` satisfies it. So a
215
+ * `urls<{ DB }>()` cannot be mounted on a `createRouter<{}>()`.
216
+ */
217
+ routes<T extends UrlPatterns<any, any, any>>(
218
+ patterns: T & EnvCompatible<T, TEnv>,
219
+ ): Rango<
197
220
  TEnv,
198
221
  TRoutes &
199
222
  (NonNullable<T["_routes"]> extends Record<string, unknown>
200
223
  ? MergeRoutesWithResponses<NonNullable<T["_routes"]>, T["_responses"]>
201
224
  : Record<string, string>)
202
225
  >;
226
+ routes(builder: UrlBuilder<TEnv>): Rango<TEnv, TRoutes>;
203
227
 
204
228
  /**
205
229
  * Add global middleware that runs on all routes
@@ -207,7 +231,7 @@ export interface RSCRouterInternal<
207
231
  use(
208
232
  patternOrMiddleware: string | MiddlewareFn<TEnv>,
209
233
  middleware?: MiddlewareFn<TEnv>,
210
- ): RSCRouter<TEnv, TRoutes>;
234
+ ): Rango<TEnv, TRoutes>;
211
235
 
212
236
  /**
213
237
  * Type-safe URL builder for registered routes
@@ -229,17 +253,17 @@ export interface RSCRouterInternal<
229
253
  * Error callback for monitoring/alerting
230
254
  * Called when errors occur in loaders, actions, or routes
231
255
  */
232
- readonly onError?: RSCRouterOptions<TEnv>["onError"];
256
+ readonly onError?: RangoOptions<TEnv>["onError"];
233
257
 
234
258
  /**
235
259
  * Cache configuration
236
260
  */
237
- readonly cache?: RSCRouterOptions<TEnv>["cache"];
261
+ readonly cache?: RangoOptions<TEnv>["cache"];
238
262
 
239
263
  /**
240
264
  * Not found component to render when no route matches
241
265
  */
242
- readonly notFound?: RSCRouterOptions<TEnv>["notFound"];
266
+ readonly notFound?: RangoOptions<TEnv>["notFound"];
243
267
 
244
268
  /**
245
269
  * Resolved theme configuration (null if theme not enabled)
@@ -258,10 +282,17 @@ export interface RSCRouterInternal<
258
282
 
259
283
  /**
260
284
  * Cache-Control header value for prefetch responses.
261
- * False means no browser caching of prefetch responses.
285
+ * False means no caching of prefetch responses.
286
+ * Derived from prefetchCacheTTL.
262
287
  */
263
288
  readonly prefetchCacheControl: string | false;
264
289
 
290
+ /**
291
+ * TTL in milliseconds for the client-side in-memory prefetch cache.
292
+ * 0 means caching is disabled.
293
+ */
294
+ readonly prefetchCacheTTL: number;
295
+
265
296
  /**
266
297
  * Whether connection warmup is enabled.
267
298
  * When true, the client sends HEAD /?_rsc_warmup after idle periods
@@ -269,6 +300,12 @@ export interface RSCRouterInternal<
269
300
  */
270
301
  readonly warmupEnabled: boolean;
271
302
 
303
+ /**
304
+ * Whether router-wide performance debugging is enabled.
305
+ * Used by the request handler to create metrics before middleware runs.
306
+ */
307
+ readonly debugPerformance?: boolean;
308
+
272
309
  /**
273
310
  * Whether ?__debug_manifest is allowed in production.
274
311
  * Always enabled in development.
@@ -325,6 +362,20 @@ export interface RSCRouterInternal<
325
362
  */
326
363
  readonly __sourceFile?: string;
327
364
 
365
+ /** @internal basename for runtime manifest generation */
366
+ readonly __basename?: string;
367
+
368
+ /**
369
+ * @internal Router-level error/notFound fallbacks (`createRouter` options),
370
+ * exposed for the build-time clientChunks discovery so a `"use client"`
371
+ * default boundary is routed into the dedicated `app-fallback` chunk. Unlike
372
+ * the route-tree `errorBoundary()`/`notFoundBoundary()` helpers these never
373
+ * land in `EntryData`, so they are read directly off the router instance.
374
+ */
375
+ readonly __defaultErrorBoundary?: RangoOptions<TEnv>["defaultErrorBoundary"];
376
+ readonly __defaultNotFoundBoundary?: RangoOptions<TEnv>["defaultNotFoundBoundary"];
377
+ readonly __notFound?: RangoOptions<TEnv>["notFound"];
378
+
328
379
  match(
329
380
  request: Request,
330
381
  input?: RouterRequestInput<TEnv>,
@@ -340,6 +391,8 @@ export interface RSCRouterInternal<
340
391
  params: Record<string, string>,
341
392
  buildVars?: Record<string, any>,
342
393
  isPassthroughRoute?: boolean,
394
+ buildEnv?: any,
395
+ devMode?: boolean,
343
396
  ): Promise<{
344
397
  segments: SerializedSegmentData[];
345
398
  handles: Record<string, SegmentHandleData>;
@@ -358,6 +411,8 @@ export interface RSCRouterInternal<
358
411
  handler: Function,
359
412
  handlerId: string,
360
413
  routeName?: string,
414
+ buildEnv?: any,
415
+ devMode?: boolean,
361
416
  ): Promise<{ encoded: string; handles: Record<string, unknown[]> } | null>;
362
417
 
363
418
  /**
@@ -411,6 +466,13 @@ export interface RSCRouterInternal<
411
466
  segmentType?: ErrorInfo["segmentType"],
412
467
  ): Promise<MatchResult | null>;
413
468
 
469
+ /**
470
+ * Low-level route matching function.
471
+ * Used by classifyRequest() for request classification without
472
+ * entering the full match pipeline.
473
+ */
474
+ findMatch(pathname: string, metricsStore?: any): any;
475
+
414
476
  /**
415
477
  * Debug utility to serialize the manifest for inspection
416
478
  * Returns a JSON-friendly representation of all routes and layouts
@@ -424,16 +486,16 @@ export interface RSCRouterInternal<
424
486
  }
425
487
 
426
488
  /**
427
- * Assert a public RSCRouter into the internal type.
489
+ * Assert a public Rango into the internal type.
428
490
  *
429
491
  * Use this at the boundary where framework code receives a user-provided
430
492
  * router and needs access to internal members (match, config, build-time).
431
493
  * The cast is safe because createRouter() always produces an object that
432
- * satisfies RSCRouterInternal; the public type is just a narrower view.
494
+ * satisfies RangoInternal; the public type is just a narrower view.
433
495
  */
434
496
  export function toInternal<
435
497
  TEnv = any,
436
498
  TRoutes extends Record<string, unknown> = Record<string, string>,
437
- >(router: RSCRouter<TEnv, TRoutes>): RSCRouterInternal<TEnv, TRoutes> {
438
- return router as RSCRouterInternal<TEnv, TRoutes>;
499
+ >(router: Rango<TEnv, TRoutes>): RangoInternal<TEnv, TRoutes> {
500
+ return router as RangoInternal<TEnv, TRoutes>;
439
501
  }