@rangojs/router 0.24.3 → 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 (274) 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-root.d.ts +4 -1
  23. package/dist/types/client-urls/client-urls.d.ts +28 -2
  24. package/dist/types/client-urls/navigation.d.ts +6 -8
  25. package/dist/types/client-urls/revalidate-registry.d.ts +1 -1
  26. package/dist/types/client-urls/server-projection.d.ts +20 -5
  27. package/dist/types/client-urls/types.d.ts +35 -17
  28. package/dist/types/client.d.ts +2 -2
  29. package/dist/types/client.rsc.d.ts +2 -2
  30. package/dist/types/decode-loader-results.d.ts +28 -7
  31. package/dist/types/href-client.d.ts +3 -1
  32. package/dist/types/index.rsc.d.ts +1 -1
  33. package/dist/types/loader-redirect.d.ts +2 -3
  34. package/dist/types/outlet-context.d.ts +7 -6
  35. package/dist/types/prefetch-markers.d.ts +14 -5
  36. package/dist/types/route-content-wrapper.d.ts +17 -14
  37. package/dist/types/route-definition/helpers-types.d.ts +7 -2
  38. package/dist/types/route-definition/resolve-handler-use.d.ts +6 -1
  39. package/dist/types/route-definition/rewrite.d.ts +48 -5
  40. package/dist/types/route-types.d.ts +2 -2
  41. package/dist/types/router/handler-context.d.ts +14 -2
  42. package/dist/types/router/intercept-resolution.d.ts +19 -46
  43. package/dist/types/router/lazy-includes.d.ts +7 -0
  44. package/dist/types/router/match-middleware/intercept-resolution.d.ts +2 -2
  45. package/dist/types/router/middleware.d.ts +0 -16
  46. package/dist/types/router/navigation-snapshot.d.ts +2 -0
  47. package/dist/types/router/prefetch-cache-ttl.d.ts +8 -11
  48. package/dist/types/router/revalidation.d.ts +8 -1
  49. package/dist/types/router/router-context.d.ts +4 -16
  50. package/dist/types/router/router-interfaces.d.ts +0 -6
  51. package/dist/types/router/router-options.d.ts +4 -7
  52. package/dist/types/router/segment-resolution/prefetch-deferral.d.ts +31 -8
  53. package/dist/types/router/segment-resolution/revalidation.d.ts +38 -4
  54. package/dist/types/router/segment-resolution.d.ts +1 -1
  55. package/dist/types/router/segment-wrappers.d.ts +3 -5
  56. package/dist/types/router/substitute-pattern-params.d.ts +19 -2
  57. package/dist/types/router/tracing.d.ts +2 -2
  58. package/dist/types/rsc/full-payload.d.ts +14 -2
  59. package/dist/types/rsc/helpers.d.ts +8 -0
  60. package/dist/types/rsc/manifest-init.d.ts +20 -0
  61. package/dist/types/rsc/prefetch-render-error.d.ts +22 -0
  62. package/dist/types/rsc/progressive-enhancement.d.ts +20 -0
  63. package/dist/types/rsc/rewrite-passes.d.ts +12 -2
  64. package/dist/types/rsc/server-action.d.ts +6 -0
  65. package/dist/types/rsc/shell-capture-constants.d.ts +10 -1
  66. package/dist/types/rsc/shell-capture.d.ts +7 -0
  67. package/dist/types/rsc/types.d.ts +5 -0
  68. package/dist/types/segment-system.d.ts +15 -0
  69. package/dist/types/server/context.d.ts +64 -4
  70. package/dist/types/server/request-context.d.ts +41 -4
  71. package/dist/types/server/request-headers.d.ts +12 -0
  72. package/dist/types/server/rewrite-trail.d.ts +8 -1
  73. package/dist/types/testing/e2e/index.d.ts +1 -1
  74. package/dist/types/testing/flight.entry.d.ts +1 -1
  75. package/dist/types/testing/index.d.ts +1 -1
  76. package/dist/types/testing/internal/client-urls-projection.d.ts +4 -3
  77. package/dist/types/testing/render-route.d.ts +21 -11
  78. package/dist/types/testing/run-loader.d.ts +7 -0
  79. package/dist/types/testing/run-middleware.d.ts +7 -0
  80. package/dist/types/testing/run-revalidate.d.ts +5 -4
  81. package/dist/types/testing/serve-shell-request.d.ts +41 -4
  82. package/dist/types/testing/shell-status.d.ts +17 -0
  83. package/dist/types/testing/vitest-stubs/version.d.ts +1 -0
  84. package/dist/types/testing/vitest.d.ts +10 -1
  85. package/dist/types/thenable-status.d.ts +0 -6
  86. package/dist/types/types/handler-context.d.ts +12 -1
  87. package/dist/types/types/loader-types.d.ts +4 -3
  88. package/dist/types/types/route-entry.d.ts +2 -1
  89. package/dist/types/urls/path-helper-types.d.ts +4 -2
  90. package/dist/types/urls/path-helper.d.ts +5 -0
  91. package/dist/types/vite/discovery/client-urls-projection.d.ts +55 -5
  92. package/dist/types/vite/discovery/state.d.ts +20 -0
  93. package/dist/types/vite/plugins/client-urls-revalidate-hoist.d.ts +28 -16
  94. package/dist/types/vite/plugins/hoist/source.d.ts +8 -7
  95. package/dist/types/vite/plugins/virtual-entries.d.ts +6 -0
  96. package/dist/types/vite/utils/prerender-utils.d.ts +7 -0
  97. package/dist/vite/index.js +6816 -6547
  98. package/package.json +1 -1
  99. package/skills/app-structure/SKILL.md +255 -0
  100. package/skills/app-structure/content-site.md +333 -0
  101. package/skills/app-structure/e-commerce.md +435 -0
  102. package/skills/app-structure/large-app.md +885 -0
  103. package/skills/app-structure/multi-app.md +280 -0
  104. package/skills/breadcrumbs/SKILL.md +6 -1
  105. package/skills/caching/SKILL.md +3 -1
  106. package/skills/catalog.json +12 -0
  107. package/skills/client-urls/SKILL.md +195 -32
  108. package/skills/composability/SKILL.md +10 -8
  109. package/skills/handler-use/SKILL.md +48 -9
  110. package/skills/hooks/handle-and-actions.md +104 -0
  111. package/skills/hooks/navigation.md +24 -10
  112. package/skills/hooks/urls.md +1 -1
  113. package/skills/i18n/SKILL.md +2 -1
  114. package/skills/intercept/SKILL.md +60 -29
  115. package/skills/layout/SKILL.md +6 -3
  116. package/skills/links/SKILL.md +151 -17
  117. package/skills/loader/SKILL.md +32 -10
  118. package/skills/middleware/SKILL.md +111 -15
  119. package/skills/observability/SKILL.md +11 -0
  120. package/skills/parallel/SKILL.md +5 -5
  121. package/skills/ppr/SKILL.md +43 -8
  122. package/skills/prerender/SKILL.md +1 -2
  123. package/skills/rango/SKILL.md +3 -0
  124. package/skills/response-routes/SKILL.md +4 -1
  125. package/skills/scripts/SKILL.md +12 -1
  126. package/skills/server-actions/SKILL.md +60 -20
  127. package/skills/testing/SKILL.md +1 -0
  128. package/skills/testing/client-components.md +13 -10
  129. package/skills/testing/loader.md +25 -24
  130. package/skills/testing/middleware.md +11 -11
  131. package/skills/testing/response-routes.md +4 -1
  132. package/skills/testing/serve-shell-request.md +85 -21
  133. package/skills/ux-architecture/SKILL.md +980 -0
  134. package/skills/view-transitions/SKILL.md +43 -10
  135. package/src/__augment-tests__/augmented.check.ts +28 -0
  136. package/src/browser/event-controller.ts +44 -18
  137. package/src/browser/intercept-utils.ts +3 -2
  138. package/src/browser/merge-segment-loaders.ts +0 -85
  139. package/src/browser/navigation-bridge.ts +119 -98
  140. package/src/browser/navigation-client.ts +20 -15
  141. package/src/browser/navigation-store.ts +4 -2
  142. package/src/browser/navigation-transaction.ts +9 -8
  143. package/src/browser/partial-update.ts +90 -32
  144. package/src/browser/prefetch/cache.ts +8 -0
  145. package/src/browser/prefetch/fetch.ts +13 -3
  146. package/src/browser/rango-marker.ts +13 -0
  147. package/src/browser/react/NavigationProvider.tsx +25 -7
  148. package/src/browser/react/filter-segment-order.ts +13 -0
  149. package/src/browser/react/use-action.ts +9 -15
  150. package/src/browser/react/use-handle.ts +63 -71
  151. package/src/browser/react/use-href.tsx +24 -5
  152. package/src/browser/react/use-link-status.ts +1 -1
  153. package/src/browser/react/use-navigation.ts +30 -40
  154. package/src/browser/react/use-reverse.ts +4 -17
  155. package/src/browser/react/use-router.ts +37 -6
  156. package/src/browser/react/use-segments.ts +8 -12
  157. package/src/browser/response-adapter.ts +11 -5
  158. package/src/browser/rsc-router.tsx +3 -1
  159. package/src/browser/scroll-restoration.ts +34 -20
  160. package/src/browser/segment-reconciler.ts +11 -29
  161. package/src/browser/server-action-bridge.ts +52 -16
  162. package/src/browser/types.ts +40 -8
  163. package/src/build/generate-manifest.ts +12 -1
  164. package/src/build/route-types/codegen.ts +6 -2
  165. package/src/build/route-types/per-module-writer.ts +94 -61
  166. package/src/cache/cache-runtime.ts +9 -0
  167. package/src/cache/cache-scope.ts +2 -1
  168. package/src/cache/cf/cf-cache-store.ts +2 -0
  169. package/src/cache/cf/cf-shell-frame.ts +3 -0
  170. package/src/cache/types.ts +8 -0
  171. package/src/cache/vercel/vercel-cache-store.ts +6 -1
  172. package/src/client-urls/client-root.tsx +10 -5
  173. package/src/client-urls/client-urls.ts +246 -166
  174. package/src/client-urls/navigation.ts +45 -38
  175. package/src/client-urls/revalidate-registry.ts +12 -3
  176. package/src/client-urls/server-projection.ts +286 -75
  177. package/src/client-urls/types.ts +40 -16
  178. package/src/client.rsc.tsx +11 -1
  179. package/src/client.tsx +9 -1
  180. package/src/decode-loader-results.ts +64 -31
  181. package/src/href-client.ts +3 -1
  182. package/src/index.rsc.ts +1 -0
  183. package/src/index.ts +12 -4
  184. package/src/loader-redirect.tsx +2 -3
  185. package/src/outlet-context.ts +7 -6
  186. package/src/prefetch-markers.ts +15 -5
  187. package/src/reverse.ts +3 -3
  188. package/src/route-content-wrapper.tsx +19 -17
  189. package/src/route-definition/dsl-helpers.ts +27 -7
  190. package/src/route-definition/helpers-types.ts +7 -2
  191. package/src/route-definition/resolve-handler-use.ts +23 -10
  192. package/src/route-definition/rewrite.ts +86 -19
  193. package/src/route-types.ts +1 -2
  194. package/src/router/find-match.ts +37 -1
  195. package/src/router/handler-context.ts +85 -40
  196. package/src/router/intercept-resolution.ts +161 -307
  197. package/src/router/lazy-includes.ts +43 -27
  198. package/src/router/loader-resolution.ts +21 -1
  199. package/src/router/manifest.ts +6 -8
  200. package/src/router/match-api.ts +11 -1
  201. package/src/router/match-middleware/background-revalidation.ts +2 -4
  202. package/src/router/match-middleware/cache-lookup.ts +16 -20
  203. package/src/router/match-middleware/intercept-resolution.ts +41 -60
  204. package/src/router/match-result.ts +31 -2
  205. package/src/router/middleware.ts +6 -147
  206. package/src/router/navigation-snapshot.ts +38 -2
  207. package/src/router/prefetch-cache-ttl.ts +10 -18
  208. package/src/router/prerender-match.ts +2 -1
  209. package/src/router/revalidation.ts +24 -3
  210. package/src/router/router-context.ts +5 -18
  211. package/src/router/router-interfaces.ts +0 -7
  212. package/src/router/router-options.ts +4 -7
  213. package/src/router/segment-resolution/fresh.ts +6 -2
  214. package/src/router/segment-resolution/helpers.ts +7 -1
  215. package/src/router/segment-resolution/prefetch-deferral.ts +62 -12
  216. package/src/router/segment-resolution/revalidation.ts +75 -35
  217. package/src/router/segment-resolution.ts +0 -1
  218. package/src/router/segment-wrappers.ts +8 -13
  219. package/src/router/substitute-pattern-params.ts +37 -2
  220. package/src/router/tracing.ts +2 -2
  221. package/src/router.ts +2 -9
  222. package/src/rsc/full-payload.ts +26 -4
  223. package/src/rsc/handler.ts +83 -41
  224. package/src/rsc/helpers.ts +13 -0
  225. package/src/rsc/manifest-init.ts +56 -3
  226. package/src/rsc/prefetch-render-error.ts +38 -0
  227. package/src/rsc/progressive-enhancement.ts +161 -211
  228. package/src/rsc/rewrite-passes.ts +56 -9
  229. package/src/rsc/rsc-rendering.ts +79 -23
  230. package/src/rsc/server-action.ts +25 -2
  231. package/src/rsc/shell-capture-constants.ts +22 -0
  232. package/src/rsc/shell-capture.ts +11 -0
  233. package/src/rsc/types.ts +5 -0
  234. package/src/segment-system.tsx +125 -119
  235. package/src/server/context.ts +96 -5
  236. package/src/server/handle-store.ts +3 -2
  237. package/src/server/request-context.ts +42 -3
  238. package/src/server/request-headers.ts +32 -0
  239. package/src/server/rewrite-trail.ts +55 -9
  240. package/src/ssr/ssr-root.tsx +6 -10
  241. package/src/testing/dispatch.ts +4 -5
  242. package/src/testing/e2e/index.ts +1 -0
  243. package/src/testing/flight.entry.ts +1 -0
  244. package/src/testing/index.ts +1 -0
  245. package/src/testing/internal/client-urls-projection.ts +31 -11
  246. package/src/testing/render-route.tsx +83 -40
  247. package/src/testing/run-loader.ts +26 -2
  248. package/src/testing/run-middleware.ts +10 -0
  249. package/src/testing/run-revalidate.ts +13 -6
  250. package/src/testing/serve-shell-request.ts +77 -4
  251. package/src/testing/shell-status.ts +40 -0
  252. package/src/testing/vitest-stubs/version.ts +1 -0
  253. package/src/testing/vitest.ts +74 -7
  254. package/src/thenable-status.ts +0 -11
  255. package/src/types/handler-context.ts +12 -1
  256. package/src/types/loader-types.ts +4 -3
  257. package/src/types/route-entry.ts +2 -1
  258. package/src/urls/include-helper.ts +38 -11
  259. package/src/urls/path-helper-types.ts +4 -2
  260. package/src/urls/path-helper.ts +16 -3
  261. package/src/vite/discovery/client-urls-projection.ts +205 -16
  262. package/src/vite/discovery/discover-routers.ts +2 -2
  263. package/src/vite/discovery/prerender-collection.ts +2 -6
  264. package/src/vite/discovery/state.ts +26 -0
  265. package/src/vite/plugins/client-urls-revalidate-hoist.ts +190 -77
  266. package/src/vite/plugins/expose-internal-ids.ts +46 -40
  267. package/src/vite/plugins/hoist/source.ts +8 -7
  268. package/src/vite/plugins/transition-when-hoist.ts +1 -0
  269. package/src/vite/plugins/version-plugin.ts +7 -0
  270. package/src/vite/plugins/version.d.ts +7 -0
  271. package/src/vite/plugins/virtual-entries.ts +20 -0
  272. package/src/vite/rango.ts +4 -1
  273. package/src/vite/router-discovery.ts +39 -7
  274. package/src/vite/utils/prerender-utils.ts +17 -0
@@ -1,3 +1,4 @@
1
+ import { toShouldRevalidateFn } from "../router/revalidation.js";
1
2
  import type { ShouldRevalidateFn } from "../types.js";
2
3
 
3
4
  /**
@@ -6,7 +7,9 @@ import type { ShouldRevalidateFn } from "../types.js";
6
7
  * group's module loads in the RSC realm
7
8
  * (vite/plugins/client-urls-revalidate-hoist.ts); materialized loader stubs
8
9
  * read it (server-projection.ts). A key hashes the file and the predicate's
9
- * syntax, so a re-registration under one key is the same predicate.
10
+ * syntax, so a re-registration under one key is the same predicate. A
11
+ * hoisted boolean (`revalidate(FLAG)`) is stored as its constant predicate;
12
+ * any other value is left unregistered for materialization to name.
10
13
  *
11
14
  * Server-only build output, not router state: no router hook reads it
12
15
  * (AGENTS.md Hard rule 8).
@@ -14,9 +17,15 @@ import type { ShouldRevalidateFn } from "../types.js";
14
17
  const revalidators = new Map<string, ShouldRevalidateFn<any, any>>();
15
18
 
16
19
  export function registerClientUrlRevalidators(
17
- fns: Readonly<Record<string, ShouldRevalidateFn<any, any>>>,
20
+ fns: Readonly<Record<string, ShouldRevalidateFn<any, any> | boolean>>,
18
21
  ): void {
19
- for (const [key, fn] of Object.entries(fns)) revalidators.set(key, fn);
22
+ for (const [key, fn] of Object.entries(fns)) {
23
+ if (typeof fn === "function" || typeof fn === "boolean") {
24
+ revalidators.set(key, toShouldRevalidateFn(fn));
25
+ } else {
26
+ revalidators.delete(key);
27
+ }
28
+ }
20
29
  }
21
30
 
22
31
  export function getClientUrlRevalidator(
@@ -21,6 +21,7 @@ import type {
21
21
  import type { PathHelpers } from "../urls/path-helper-types.js";
22
22
  import type { AllUseItems } from "../route-types.js";
23
23
  import { urls } from "../urls/urls-function.js";
24
+ import { toShouldRevalidateFn } from "../router/revalidation.js";
24
25
  import { isHoistedRevalidateRef } from "./hoisted-revalidate.js";
25
26
  import { getClientUrlRevalidator } from "./revalidate-registry.js";
26
27
  import { setLayoutLoaderCopies } from "./layout-loader-copies.js";
@@ -32,6 +33,13 @@ import {
32
33
  ClientUrlsRoot,
33
34
  } from "./client-root.js";
34
35
  import { createClientUrlsWhenRef } from "../transition-when-ref.js";
36
+ import type { TrieLeaf, TrieNode } from "../router/route-trie-builder.js";
37
+ import { tryTrieMatch } from "../router/trie-matching.js";
38
+ import { sameMountInstance } from "../router/substitute-pattern-params.js";
39
+ import { getRouterTrie } from "../route-map-builder.js";
40
+ import { _getRequestContext } from "../server/request-context.js";
41
+ import { routeKeyOf } from "../urls/path-helper.js";
42
+ import { buildClientUrlsTrie } from "./client-urls.js";
35
43
  import type {
36
44
  ClientTransitionConfig,
37
45
  ClientUrlInterceptRecord,
@@ -105,11 +113,11 @@ export interface ClientUrlProjectionRoute {
105
113
  * loader(), so a prefetch of the route defers them. Absent (= none) in
106
114
  * projections serialized before the option existed. */
107
115
  readonly deferrableLoaderIndices?: readonly number[];
108
- /** Per loader (aligned with loaderIds), the keys of its hoisted
109
- * revalidate() predicates in declaration order; materialization attaches
110
- * the registered predicates (revalidate-registry.ts). Absent when no
111
- * loader declares one. */
112
- readonly loaderRevalidate?: readonly (readonly string[])[];
116
+ /** Per loader (aligned with loaderIds), its revalidate() arguments in
117
+ * declaration order: a hoisted predicate's key, whose registration
118
+ * materialization attaches (revalidate-registry.ts), or a boolean
119
+ * literal. Absent when no loader declares one. */
120
+ readonly loaderRevalidate?: readonly (readonly (string | boolean)[])[];
113
121
  /** One per leading loader its layout()s declare
114
122
  * (ClientUrlRouteRecord.layoutLoaders): the record's ordinal in the
115
123
  * definition. Two routes whose loader at an index carries the same ordinal
@@ -130,6 +138,10 @@ export interface ClientUrlProjectionIntercept {
130
138
  readonly targetName: string;
131
139
  readonly loaderIds: readonly string[];
132
140
  readonly hasLoading: boolean;
141
+ /** As on a route (ClientUrlProjectionRoute.deferrableLoaderIndices). */
142
+ readonly deferrableLoaderIndices?: readonly number[];
143
+ /** As on a route (ClientUrlProjectionRoute.loaderRevalidate). */
144
+ readonly loaderRevalidate?: readonly (readonly (string | boolean)[])[];
133
145
  }
134
146
 
135
147
  export interface ClientUrlProjection {
@@ -325,29 +337,10 @@ function serializeRoute(
325
337
  return loader.$$id;
326
338
  });
327
339
 
328
- const loaderRevalidate = route.loaders.map(({ loader, revalidate }) =>
329
- revalidate.flatMap((entry) => {
330
- if (isHoistedRevalidateRef(entry)) return [entry.$$rangoRevalidate];
331
- if (options.hoisted) {
332
- throw new Error(
333
- `clientUrls() revalidate() on loader "${loader.$$id}" of route ` +
334
- `"${route.pattern}" was not hoisted to the server: call the ` +
335
- "builder's revalidate() helper with an inline function or an " +
336
- "imported predicate.",
337
- );
338
- }
339
- return [];
340
- }),
341
- );
342
-
343
340
  const awaitedLoaderIndices = indicesWhere(
344
341
  route.loaders,
345
342
  ({ ssr }) => ssr === false,
346
343
  );
347
- const deferrableLoaderIndices = indicesWhere(
348
- route.loaders,
349
- ({ prefetch }) => prefetch === false,
350
- );
351
344
 
352
345
  const transition = serializeTransition(route);
353
346
  return Object.freeze({
@@ -360,6 +353,55 @@ function serializeRoute(
360
353
  ...(awaitedLoaderIndices.length > 0
361
354
  ? { awaitedLoaderIndices: Object.freeze(awaitedLoaderIndices) }
362
355
  : {}),
356
+ ...serializeLoaderOptions(
357
+ route.loaders,
358
+ `route "${route.pattern}"`,
359
+ options,
360
+ ),
361
+ ...(layoutLoaders.length > 0
362
+ ? { layoutLoaders: Object.freeze(layoutLoaders) }
363
+ : {}),
364
+ ...(transition ? { transition } : {}),
365
+ ...(typeof route.transition?.when === "function"
366
+ ? { transitionWhen: true as const }
367
+ : {}),
368
+ });
369
+ }
370
+
371
+ /**
372
+ * The loader options a route and an intercept project alike: the `prefetch:
373
+ * false` indices and each loader's revalidate() keys and boolean literals.
374
+ * Under `options.hoisted` a predicate the build left in place fails loudly.
375
+ */
376
+ function serializeLoaderOptions(
377
+ loaders: readonly ClientUrlLoaderRecord[],
378
+ owner: string,
379
+ options: SerializeClientUrlPatternsOptions,
380
+ ): Pick<
381
+ ClientUrlProjectionRoute,
382
+ "deferrableLoaderIndices" | "loaderRevalidate"
383
+ > {
384
+ const loaderRevalidate = loaders.map(({ loader, revalidate }) =>
385
+ revalidate.flatMap((entry): (string | boolean)[] => {
386
+ if (isHoistedRevalidateRef(entry)) return [entry.$$rangoRevalidate];
387
+ if (typeof entry === "boolean") return [entry];
388
+ if (options.hoisted) {
389
+ throw new Error(
390
+ `clientUrls() revalidate() on loader "${loader.$$id}" of ${owner} ` +
391
+ "was not hoisted to the server: call revalidate() (the builder's " +
392
+ 'helper, or the one "@rangojs/router/client" exports for a ' +
393
+ "component's .use) with an inline function, an imported " +
394
+ "predicate or a boolean.",
395
+ );
396
+ }
397
+ return [];
398
+ }),
399
+ );
400
+ const deferrableLoaderIndices = indicesWhere(
401
+ loaders,
402
+ ({ prefetch }) => prefetch === false,
403
+ );
404
+ return {
363
405
  ...(deferrableLoaderIndices.length > 0
364
406
  ? { deferrableLoaderIndices: Object.freeze(deferrableLoaderIndices) }
365
407
  : {}),
@@ -370,19 +412,13 @@ function serializeRoute(
370
412
  ),
371
413
  }
372
414
  : {}),
373
- ...(layoutLoaders.length > 0
374
- ? { layoutLoaders: Object.freeze(layoutLoaders) }
375
- : {}),
376
- ...(transition ? { transition } : {}),
377
- ...(typeof route.transition?.when === "function"
378
- ? { transitionWhen: true as const }
379
- : {}),
380
- });
415
+ };
381
416
  }
382
417
 
383
418
  function serializeIntercept(
384
419
  record: ClientUrlInterceptRecord,
385
420
  index: number,
421
+ options: SerializeClientUrlPatternsOptions,
386
422
  ): ClientUrlProjectionIntercept {
387
423
  const loaderIds = record.loaders.map(({ loader }, loaderIndex) => {
388
424
  if (typeof loader.$$id !== "string" || loader.$$id.length === 0) {
@@ -399,6 +435,11 @@ function serializeIntercept(
399
435
  targetName: record.targetName,
400
436
  loaderIds: Object.freeze(loaderIds),
401
437
  hasLoading: record.loading !== undefined,
438
+ ...serializeLoaderOptions(
439
+ record.loaders,
440
+ `intercept ".${record.targetName}"`,
441
+ options,
442
+ ),
402
443
  });
403
444
  }
404
445
 
@@ -413,7 +454,9 @@ export function serializeClientUrlPatterns(
413
454
  patterns.routes.map((route) => serializeRoute(route, options, ordinals)),
414
455
  ),
415
456
  intercepts: Object.freeze(
416
- (patterns.intercepts ?? []).map(serializeIntercept),
457
+ (patterns.intercepts ?? []).map((record, index) =>
458
+ serializeIntercept(record, index, options),
459
+ ),
417
460
  ),
418
461
  });
419
462
  }
@@ -529,28 +572,48 @@ export function clearClientUrlProjections(): void {
529
572
  clientUrlProjections.clear();
530
573
  }
531
574
 
575
+ /** The projected loaders of a route or an intercept. */
576
+ interface ProjectedLoaders {
577
+ readonly projected: Pick<
578
+ ClientUrlProjectionRoute,
579
+ | "loaderIds"
580
+ | "loaderRevalidate"
581
+ | "awaitedLoaderIndices"
582
+ | "deferrableLoaderIndices"
583
+ >;
584
+ /** Names the owner in an error: `route "/a"`, `intercept ".b"`. */
585
+ readonly label: string;
586
+ /** The owner's loader records in a definition object. */
587
+ readonly inline: (
588
+ patterns: ClientUrlPatterns,
589
+ ) => readonly ClientUrlLoaderRecord[] | undefined;
590
+ }
591
+
532
592
  /**
533
- * The revalidate() predicates of one materialized loader. A definition
534
- * object (unit tests, the testing primitives) carries its functions. A
535
- * client reference resolves the projection's content keys in the registry
536
- * the group's hoisted module filled when it loaded; a key with no
537
- * registration fails loudly here, at materialization, never at a request.
593
+ * The revalidate() predicates of one materialized loader, a boolean as its
594
+ * constant predicate. A definition object (unit tests, the testing
595
+ * primitives) carries its functions. A client reference resolves the
596
+ * projection's content keys in the registry the group's hoisted module
597
+ * filled when it loaded; a key with no registration fails loudly here, at
598
+ * materialization, never at a request.
538
599
  */
539
600
  function loaderRevalidators(
540
601
  reference: ClientUrlDefinitionSource,
541
- route: ClientUrlProjectionRoute,
602
+ { projected, label, inline }: ProjectedLoaders,
542
603
  loaderIndex: number,
543
604
  ): ShouldRevalidateFn<any, any>[] {
544
605
  const fromRegistry = (key: string): ShouldRevalidateFn<any, any> => {
545
606
  if (!getClientUrlRevalidator(key)) {
546
607
  const id = isClientUrlReference(reference) ? reference.$$id : "(inline)";
547
608
  throw new Error(
548
- `clientUrls() revalidate() on loader "${route.loaderIds[loaderIndex]}" ` +
549
- `of route "${route.pattern}" in "${id}" has no server ` +
609
+ `clientUrls() revalidate() on loader "${projected.loaderIds[loaderIndex]}" ` +
610
+ `of ${label} in "${id}" has no server ` +
550
611
  `registration (key "${key}"). The rango Vite plugin registers ` +
551
612
  "hoisted predicates when the group's module loads in the RSC " +
552
- "environment; a transform that changed the predicate's syntax " +
553
- "in one environment only also causes this.",
613
+ "environment, with those of the modules route discovery found " +
614
+ "its keys in (a component's .use). An argument that evaluated to " +
615
+ "neither a function nor a boolean there, or a transform that " +
616
+ "changed its syntax in one environment only, causes this.",
554
617
  );
555
618
  }
556
619
  // Looked up per call: a dev re-evaluation of the hoisted module
@@ -558,14 +621,17 @@ function loaderRevalidators(
558
621
  return (args) => getClientUrlRevalidator(key)!(args);
559
622
  };
560
623
  if (isClientUrlPatterns(reference)) {
561
- const record = reference.routes.find(({ id }) => id === route.id);
562
- return (record?.loaders[loaderIndex]?.revalidate ?? []).map((entry) =>
624
+ return (inline(reference)?.[loaderIndex]?.revalidate ?? []).map((entry) =>
563
625
  isHoistedRevalidateRef(entry)
564
626
  ? fromRegistry(entry.$$rangoRevalidate)
565
- : entry,
627
+ : toShouldRevalidateFn(entry),
566
628
  );
567
629
  }
568
- return (route.loaderRevalidate?.[loaderIndex] ?? []).map(fromRegistry);
630
+ return (projected.loaderRevalidate?.[loaderIndex] ?? []).map((entry) =>
631
+ toShouldRevalidateFn(
632
+ typeof entry === "boolean" ? entry : fromRegistry(entry),
633
+ ),
634
+ );
569
635
  }
570
636
 
571
637
  function createLoaderStub(id: string): LoaderDefinition<unknown> {
@@ -607,6 +673,100 @@ function materializedPathOptions(
607
673
  };
608
674
  }
609
675
 
676
+ /** A param segment on a walked path; the leaf's `pa` names it. */
677
+ const PARAM = "\0";
678
+
679
+ /**
680
+ * The patterns of the router's routes, outside the group, that a URL of the
681
+ * group matches too: the trie walked in step with the group's (its patterns
682
+ * joined to the mount), static against static, a param against either, a
683
+ * catch-all against everything below. Constraints are not compared, so the
684
+ * list may hold a route no URL of the group reaches: the client trie, which
685
+ * gets the patterns as they are, never lets one win a URL it does not match.
686
+ * `own` are the group's route keys in the router's trie.
687
+ */
688
+ export function competingPatterns(
689
+ router: TrieNode,
690
+ group: TrieNode,
691
+ own: ReadonlySet<string>,
692
+ ): string[] {
693
+ const found = new Set<string>();
694
+ const add = (leaf: TrieLeaf, path: readonly string[], tail = ""): void => {
695
+ if (own.has(leaf.n)) return;
696
+ let index = 0;
697
+ const pattern = path.map((piece) => {
698
+ if (piece[0] !== PARAM) return piece;
699
+ const name = leaf.pa?.[index++] ?? `p${index}`;
700
+ const allowed = leaf.cv?.[name];
701
+ return `/:${name}${allowed ? `(${allowed.join("|")})` : ""}${piece.slice(1)}`;
702
+ });
703
+ found.add(pattern.join("") + tail || "/");
704
+ };
705
+ const catchAll = (node: TrieNode, path: readonly string[]): void => {
706
+ const leaf = node.w!;
707
+ add(
708
+ leaf,
709
+ path,
710
+ leaf.pn === "*" ? "/*" : `/:${leaf.pn}${leaf.w1 ? "+" : "*"}`,
711
+ );
712
+ };
713
+ const everything = (node: TrieNode, path: readonly string[]): void => {
714
+ if (node.r) add(node.r, path);
715
+ if (node.w) catchAll(node, path);
716
+ for (const [key, child] of Object.entries(node.s ?? {})) {
717
+ everything(child, [...path, `/${key}`]);
718
+ }
719
+ if (node.p) everything(node.p.c, [...path, PARAM]);
720
+ for (const [suffix, child] of Object.entries(node.xp ?? {})) {
721
+ everything(child.c, [...path, PARAM + suffix]);
722
+ }
723
+ };
724
+ // Every child of the group's node that a segment `matches` lets through.
725
+ const groupSteps = (
726
+ node: TrieNode,
727
+ matches: (key: string) => boolean,
728
+ ): TrieNode[] => [
729
+ ...Object.entries(node.s ?? {}).flatMap(([key, child]) =>
730
+ matches(key) ? [child] : [],
731
+ ),
732
+ ...(node.p ? [node.p.c] : []),
733
+ ...Object.values(node.xp ?? {}).map((child) => child.c),
734
+ ];
735
+ const walk = (g: TrieNode, r: TrieNode, path: readonly string[]): void => {
736
+ if (g.w) return everything(r, path);
737
+ if (r.r && g.r) add(r.r, path);
738
+ if (r.w && (g.r || g.s || g.p || g.xp)) catchAll(r, path);
739
+ for (const [key, child] of Object.entries(r.s ?? {})) {
740
+ const next = [...path, `/${key}`];
741
+ if (g.s?.[key]) walk(g.s[key], child, next);
742
+ if (g.p) walk(g.p.c, child, next);
743
+ for (const [suffix, gx] of Object.entries(g.xp ?? {})) {
744
+ if (key.endsWith(suffix)) walk(gx.c, child, next);
745
+ }
746
+ }
747
+ if (r.p) {
748
+ for (const step of groupSteps(g, () => true)) {
749
+ walk(step, r.p.c, [...path, PARAM]);
750
+ }
751
+ }
752
+ for (const [suffix, child] of Object.entries(r.xp ?? {})) {
753
+ for (const step of groupSteps(g, (key) => key.endsWith(suffix))) {
754
+ walk(step, child.c, [...path, PARAM + suffix]);
755
+ }
756
+ }
757
+ };
758
+ walk(group, router, []);
759
+ return [...found];
760
+ }
761
+
762
+ /** competingPatterns per router trie, per group (name prefix and mount). */
763
+ const COMPETING = new WeakMap<TrieNode, Map<string, readonly string[]>>();
764
+
765
+ function currentRouterTrie(): TrieNode | undefined {
766
+ const routerId = _getRequestContext()?._routerId;
767
+ return routerId ? getRouterTrie(routerId) : undefined;
768
+ }
769
+
610
770
  function materializeRouteItems(
611
771
  reference: ClientUrlDefinitionSource,
612
772
  projection: ClientUrlProjection,
@@ -627,6 +787,52 @@ function materializeRouteItems(
627
787
  // One key per group MOUNT (the include's URL prefix): renderSegments keys
628
788
  // every route segment of the group by it (ResolvedSegment.clientGroup).
629
789
  const clientGroup = getUrlPrefix() || "/";
790
+ // The group's routes as the router's trie names them.
791
+ const own = new Set(
792
+ projection.routes.map((route) =>
793
+ routeKeyOf(namePrefix, route.pattern, route.name ?? undefined),
794
+ ),
795
+ );
796
+ // ClientUrlsRoot's `foreign`: the router's routes that compete with the
797
+ // group's for a URL, so the browser presents only what the server routes
798
+ // here (a `/:slug` route under `/:locale?` would claim a root `/login`).
799
+ const foreignProp = (): { foreign?: readonly string[] } => {
800
+ const router = currentRouterTrie();
801
+ if (!router) return {};
802
+ let byGroup = COMPETING.get(router);
803
+ if (!byGroup) COMPETING.set(router, (byGroup = new Map()));
804
+ const key = `${namePrefix} ${clientGroup}`;
805
+ let foreign = byGroup.get(key);
806
+ if (!foreign) {
807
+ foreign = competingPatterns(
808
+ router,
809
+ buildClientUrlsTrie(projection.routes, clientGroup),
810
+ own,
811
+ );
812
+ byGroup.set(key, foreign);
813
+ }
814
+ return foreign.length ? { foreign } : {};
815
+ };
816
+
817
+ // The loader() items of a route's or an intercept's projected loaders: each
818
+ // stub with its `ssr: false` and `prefetch: false` flags and its
819
+ // revalidate() predicates.
820
+ const materializeLoaders = (owner: ProjectedLoaders) =>
821
+ owner.projected.loaderIds.map((id, loaderIndex) => {
822
+ const revalidators = loaderRevalidators(reference, owner, loaderIndex);
823
+ return loader(
824
+ createLoaderStub(id),
825
+ {
826
+ ...(owner.projected.awaitedLoaderIndices?.includes(loaderIndex) && {
827
+ ssr: false,
828
+ }),
829
+ ...(owner.projected.deferrableLoaderIndices?.includes(
830
+ loaderIndex,
831
+ ) && { prefetch: false }),
832
+ },
833
+ () => revalidators.map((fn) => revalidate(fn)),
834
+ );
835
+ });
630
836
 
631
837
  // Helper calls attach to the CURRENT ctx.parent as they execute, so these
632
838
  // builders must run where the items belong: at the module top level for the
@@ -672,27 +878,15 @@ function materializeRouteItems(
672
878
  definition: reference as ClientUrlPatterns,
673
879
  routeId: route.id,
674
880
  namePrefix,
881
+ ...foreignProp(),
675
882
  }),
676
883
  materializedPathOptions(route, clientGroup),
677
884
  recordingCopies(routeIndex, () => [
678
- ...route.loaderIds.map((id, loaderIndex) => {
679
- const revalidators = loaderRevalidators(
680
- reference,
681
- route,
682
- loaderIndex,
683
- );
684
- return loader(
685
- createLoaderStub(id),
686
- {
687
- ...(route.awaitedLoaderIndices?.includes(loaderIndex) && {
688
- ssr: false,
689
- }),
690
- ...(route.deferrableLoaderIndices?.includes(loaderIndex) && {
691
- prefetch: false,
692
- }),
693
- },
694
- () => revalidators.map((fn) => revalidate(fn)),
695
- );
885
+ ...materializeLoaders({
886
+ projected: route,
887
+ label: `route "${route.pattern}"`,
888
+ inline: (patterns) =>
889
+ patterns.routes.find(({ id }) => id === route.id)?.loaders,
696
890
  }),
697
891
  ...(route.hasLoading
698
892
  ? [
@@ -744,15 +938,27 @@ function materializeRouteItems(
744
938
  // out-of-group origin the slot's host layout is not in the rendered
745
939
  // tree, so the modal would silently vanish. The declaration contract is
746
940
  // module-local, so materialization synthesizes the origin check the DSL
747
- // deliberately does not expose: activate only when the origin pathname
748
- // is inside this include's mount. This is server-generated code — no
749
- // function crosses the "use client" projection boundary.
750
- const mount = getUrlPrefix() || "/";
751
- const isInGroupOrigin = ({ from }: InterceptSelectorContext): boolean =>
752
- mount === "/"
753
- ? true
754
- : from.url.pathname === mount ||
755
- from.url.pathname.startsWith(`${mount}/`);
941
+ // deliberately does not expose: activate only when the origin is one of
942
+ // this group's routes, in the target's mount instance. The router
943
+ // matched the origin already: its route name, or for an unnamed one its
944
+ // key in the router's trie, says whether it is the group's (a URL
945
+ // prefix cannot: a `/:locale?` mount prefixes every path), and its
946
+ // params whether another locale is another instance, a full page
947
+ // (beginClientUrlNavigation in navigation.ts leaves the group for it
948
+ // too). This is server-generated code — no function crosses the
949
+ // "use client" projection boundary.
950
+ const isInGroupOrigin = ({ from, to }: InterceptSelectorContext): boolean => {
951
+ if (clientGroup === "/") return true;
952
+ const router = currentRouterTrie();
953
+ const key =
954
+ from.routeName ??
955
+ (router && tryTrieMatch(router, from.url.pathname)?.routeKey);
956
+ return (
957
+ !!key &&
958
+ own.has(key) &&
959
+ sameMountInstance(clientGroup, from.params, to.params)
960
+ );
961
+ };
756
962
  const slotNames = [...new Set(intercepts.map((record) => record.slotName))];
757
963
  const buildInterceptItems = (): AllUseItems[] =>
758
964
  intercepts.map(
@@ -767,7 +973,12 @@ function materializeRouteItems(
767
973
  }),
768
974
  { when: isInGroupOrigin },
769
975
  () => [
770
- ...record.loaderIds.map((id) => loader(createLoaderStub(id))),
976
+ ...materializeLoaders({
977
+ projected: record,
978
+ label: `intercept ".${record.targetName}"`,
979
+ inline: (patterns) =>
980
+ patterns.intercepts?.[interceptIndex]?.loaders,
981
+ }),
771
982
  ...(record.hasLoading
772
983
  ? [
773
984
  loading(
@@ -28,6 +28,18 @@ export type ClientUrlItems = readonly ClientUrlItemInput[];
28
28
 
29
29
  export type ClientUrlUse = () => ClientUrlItems;
30
30
 
31
+ /**
32
+ * A component path(), layout() or intercept() mounts. Its optional `use`
33
+ * carries its own items, as a server handler's `.use` does, built with the
34
+ * helpers `@rangojs/router/client` exports: they come before the mount
35
+ * site's explicit items (an explicit loading() or transition() wins, loaders
36
+ * accumulate). A page component may carry loader(), loading() and
37
+ * transition(); a layout or intercept component loader() and loading().
38
+ */
39
+ export type ClientUrlComponent = ComponentType & {
40
+ readonly use?: ClientUrlUse;
41
+ };
42
+
31
43
  export type ClientPathOptions<
32
44
  TName extends string = string,
33
45
  TSearch extends SearchSchema = SearchSchema,
@@ -42,13 +54,13 @@ export type ClientPathFn = <
42
54
  const TSearch extends SearchSchema = {},
43
55
  >(
44
56
  pattern: TPattern,
45
- component: ComponentType,
57
+ component: ClientUrlComponent,
46
58
  optionsOrUse?: ClientPathOptions<TName, TSearch> | ClientUrlUse,
47
59
  use?: ClientUrlUse,
48
60
  ) => ClientUrlItem & TypedRouteItem<TName, TPattern, unknown, TSearch>;
49
61
 
50
62
  export type ClientLayoutFn = <const TItems extends ClientUrlItems>(
51
- component: ComponentType,
63
+ component: ClientUrlComponent,
52
64
  children: () => TItems,
53
65
  ) => ClientUrlItem & TypedLayoutItem<ExtractRoutes<TItems>>;
54
66
 
@@ -57,11 +69,13 @@ export interface ClientUrlLoaderRecord {
57
69
  /**
58
70
  * Per-loader revalidate() predicates, run on the server; empty = the
59
71
  * router's defaults. Under the rango Vite plugin the browser and SSR copies
60
- * hold the marker the build left in place of each hoisted predicate.
72
+ * hold the marker the build left in place of each hoisted predicate; a
73
+ * boolean literal stays in place.
61
74
  */
62
75
  readonly revalidate: readonly (
63
76
  | ShouldRevalidateFn<any, DefaultEnv>
64
77
  | HoistedRevalidateRef
78
+ | boolean
65
79
  )[];
66
80
  /**
67
81
  * loader(Def, { ssr: false }): document renders await this loader before
@@ -71,11 +85,10 @@ export interface ClientUrlLoaderRecord {
71
85
  */
72
86
  readonly ssr?: false;
73
87
  /**
74
- * loader(Def, { prefetch: false }): a prefetch of the route defers this
75
- * loader and the click that adopts it runs it in its fill request (see
76
- * {@link LoaderOptions}). Projected onto the materialized stub. Inside
77
- * intercept() it is not recorded: intercept loaders ignore the flag, as in
78
- * urls().
88
+ * loader(Def, { prefetch: false }): a prefetch of the route, or of an
89
+ * intercept's target from its source, defers this loader and the click
90
+ * that adopts it runs it in its fill request (see {@link LoaderOptions}).
91
+ * Projected onto the materialized stub.
79
92
  */
80
93
  readonly prefetch?: false;
81
94
  }
@@ -126,7 +139,7 @@ export interface ClientUrlRouteRecord {
126
139
 
127
140
  /**
128
141
  * A restricted intercept declared inside clientUrls(). Compared to the server
129
- * intercept() there is no `when` selector, no middleware, and the target must
142
+ * intercept() there is no `when` selector, and the target must
130
143
  * be a dot-local NAMED route in the same definition — every field is
131
144
  * JSON-projectable, which is what makes the declaration legal in a
132
145
  * "use client" module. `targetName` is stored bare (no leading dot).
@@ -160,8 +173,8 @@ export interface ClientUrlHelpers {
160
173
  * the destination at once, commits the adopted prefetch with the other
161
174
  * loaders' data, and runs only the flagged loaders in its one fill
162
175
  * request. A `layout()` loader with the flag is flagged on every route
163
- * below it. Inside intercept() the flag is accepted and ignored, as in
164
- * urls().
176
+ * below it. Inside intercept() it defers the loader in a prefetch of the
177
+ * intercept from its source, as in urls().
165
178
  */
166
179
  readonly loader: <TData>(
167
180
  definition: LoaderDefinition<TData>,
@@ -176,10 +189,11 @@ export interface ClientUrlHelpers {
176
189
  * names, ...): the build moves it out of this "use client" module. It may
177
190
  * reference imports and globals only; a binding of this module, `this`,
178
191
  * `import()`, a browser global, an `async` function or an import from a
179
- * "use client" module fails the build with its file and line.
192
+ * "use client" module fails the build with its file and line. A boolean
193
+ * is shorthand for `() => true` / `() => false`.
180
194
  */
181
195
  readonly revalidate: (
182
- fn: ShouldRevalidateFn<any, DefaultEnv>,
196
+ fn: ShouldRevalidateFn<any, DefaultEnv> | boolean,
183
197
  ) => ClientUrlItem;
184
198
  /**
185
199
  * Declare an intercept for a named route in THIS definition. The target is
@@ -190,7 +204,7 @@ export interface ClientUrlHelpers {
190
204
  readonly intercept: (
191
205
  slotName: `@${string}`,
192
206
  targetName: `.${string}`,
193
- component: ComponentType,
207
+ component: ClientUrlComponent,
194
208
  use?: ClientUrlUse,
195
209
  ) => ClientUrlItem;
196
210
  /**
@@ -198,7 +212,8 @@ export interface ClientUrlHelpers {
198
212
  * holds previous content instead of re-streaming the loading() fallback,
199
213
  * and on experimental React the config's ViewTransition classes apply.
200
214
  * Data-only — no `when` gate (server-executed; declare it in the server
201
- * tree). Valid inside a path() use callback only.
215
+ * tree). Valid inside a path() use callback, or a page component's
216
+ * `.use`.
202
217
  */
203
218
  readonly transition: (config: ClientTransitionConfig) => ClientUrlItem;
204
219
  }
@@ -215,5 +230,14 @@ export interface ClientUrlPatterns<
215
230
  readonly intercepts: readonly ClientUrlInterceptRecord[];
216
231
  readonly [CLIENT_URL_PATTERNS_BRAND]: void;
217
232
  readonly _routes?: TRoutes;
218
- match(pathname: string): TrieMatchResult | null;
233
+ /**
234
+ * Match an absolute pathname against the routes joined to `mount`, the
235
+ * include() mount pattern (default "/"); params include the mount's. A
236
+ * `foreign` pattern that wins is a match whose routeKey is that pattern.
237
+ */
238
+ match(
239
+ pathname: string,
240
+ mount?: string,
241
+ foreign?: readonly string[],
242
+ ): TrieMatchResult | null;
219
243
  }