@rangojs/router 0.0.0-experimental.55 → 0.0.0-experimental.56

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 (46) hide show
  1. package/dist/bin/rango.js +128 -46
  2. package/dist/vite/index.js +119 -43
  3. package/package.json +1 -1
  4. package/skills/links/SKILL.md +3 -1
  5. package/skills/middleware/SKILL.md +2 -0
  6. package/skills/router-setup/SKILL.md +35 -0
  7. package/src/browser/navigation-bridge.ts +6 -0
  8. package/src/browser/navigation-client.ts +4 -0
  9. package/src/browser/navigation-store.ts +43 -8
  10. package/src/browser/partial-update.ts +17 -1
  11. package/src/browser/prefetch/fetch.ts +8 -2
  12. package/src/browser/react/Link.tsx +43 -8
  13. package/src/browser/react/NavigationProvider.tsx +7 -0
  14. package/src/browser/react/context.ts +6 -0
  15. package/src/browser/react/use-router.ts +20 -8
  16. package/src/browser/rsc-router.tsx +8 -0
  17. package/src/browser/server-action-bridge.ts +5 -0
  18. package/src/browser/types.ts +18 -4
  19. package/src/build/generate-manifest.ts +3 -0
  20. package/src/build/generate-route-types.ts +3 -0
  21. package/src/build/route-types/include-resolution.ts +8 -1
  22. package/src/build/route-types/router-processing.ts +211 -72
  23. package/src/route-definition/redirect.ts +9 -1
  24. package/src/router/handler-context.ts +5 -9
  25. package/src/router/intercept-resolution.ts +6 -2
  26. package/src/router/loader-resolution.ts +3 -2
  27. package/src/router/middleware-types.ts +0 -6
  28. package/src/router/middleware.ts +0 -3
  29. package/src/router/prerender-match.ts +2 -2
  30. package/src/router/router-interfaces.ts +25 -4
  31. package/src/router/router-options.ts +37 -11
  32. package/src/router.ts +40 -4
  33. package/src/rsc/handler.ts +10 -1
  34. package/src/rsc/manifest-init.ts +5 -1
  35. package/src/rsc/progressive-enhancement.ts +4 -0
  36. package/src/rsc/rsc-rendering.ts +5 -0
  37. package/src/rsc/server-action.ts +2 -0
  38. package/src/rsc/ssr-setup.ts +1 -1
  39. package/src/rsc/types.ts +5 -0
  40. package/src/server/request-context.ts +8 -4
  41. package/src/ssr/index.tsx +3 -0
  42. package/src/types/cache-types.ts +4 -4
  43. package/src/types/handler-context.ts +5 -9
  44. package/src/types/loader-types.ts +0 -1
  45. package/src/urls/pattern-types.ts +12 -0
  46. package/src/vite/discovery/discover-routers.ts +5 -1
@@ -8,6 +8,7 @@ import type {
8
8
  import type { NonceProvider } from "../rsc/types.js";
9
9
  import type { ExecutionContext } from "../server/request-context.js";
10
10
  import type { UrlPatterns } from "../urls.js";
11
+ import type { UrlBuilder } from "../urls/pattern-types.js";
11
12
  import type { NamedRouteEntry } from "./content-negotiation.js";
12
13
  import type { TelemetrySink } from "./telemetry.js";
13
14
  import type { RouterTimeouts, OnTimeoutCallback } from "./timeout.js";
@@ -95,6 +96,28 @@ export interface RSCRouterOptions<TEnv = any> {
95
96
  */
96
97
  $$sourceFile?: string;
97
98
 
99
+ /**
100
+ * URL prefix applied to all routes registered with this router.
101
+ *
102
+ * Useful when the app is served under a sub-path (e.g. `/admin` or `/v2`).
103
+ * All `path()` patterns are automatically prefixed and `reverse()` returns
104
+ * full paths including the basename. Route names are NOT prefixed.
105
+ *
106
+ * @example
107
+ * ```typescript
108
+ * const router = createRouter({
109
+ * basename: "/admin",
110
+ * }).routes(({ path }) => [
111
+ * path("/", Dashboard, { name: "home" }), // matches /admin
112
+ * path("/users", Users, { name: "users" }), // matches /admin/users
113
+ * ]);
114
+ *
115
+ * router.reverse("home"); // "/admin"
116
+ * router.reverse("users"); // "/admin/users"
117
+ * ```
118
+ */
119
+ basename?: string;
120
+
98
121
  /**
99
122
  * Enable performance metrics collection
100
123
  * When enabled, metrics are output to console and available via Server-Timing header
@@ -337,25 +360,28 @@ export interface RSCRouterOptions<TEnv = any> {
337
360
  /**
338
361
  * URL patterns to register with the router.
339
362
  *
340
- * Alternative to calling `.routes()` method - allows passing patterns
341
- * directly in the config for a more concise setup.
363
+ * Accepts either a `UrlPatterns` object from `urls()` or a builder function
364
+ * directly (urls() is called implicitly).
342
365
  *
343
366
  * @example
344
367
  * ```typescript
345
- * import { urls } from "@rangojs/router/server";
346
- *
347
- * const urlpatterns = urls(({ path, layout }) => [
348
- * path("/", HomePage, { name: "home" }),
349
- * path("/about", AboutPage, { name: "about" }),
350
- * ]);
351
- *
352
- * const router = createRouter<AppEnv>({
368
+ * // With urls()
369
+ * createRouter<AppEnv>({
353
370
  * document: Document,
354
371
  * urls: urlpatterns,
355
372
  * });
373
+ *
374
+ * // With builder function
375
+ * createRouter<AppEnv>({
376
+ * document: Document,
377
+ * urls: ({ path }) => [
378
+ * path("/", HomePage, { name: "home" }),
379
+ * path("/about", AboutPage, { name: "about" }),
380
+ * ],
381
+ * });
356
382
  * ```
357
383
  */
358
- urls?: UrlPatterns<TEnv, any>;
384
+ urls?: UrlPatterns<TEnv, any> | UrlBuilder<TEnv>;
359
385
 
360
386
  /**
361
387
  * Injected by the Vite transform at compile time.
package/src/router.ts CHANGED
@@ -19,6 +19,8 @@ import {
19
19
  import MapRootLayout from "./server/root-layout.js";
20
20
  import type { AllUseItems } from "./route-types.js";
21
21
  import type { UrlPatterns } from "./urls.js";
22
+ import type { UrlBuilder } from "./urls/pattern-types.js";
23
+ import { urls } from "./urls.js";
22
24
  import {
23
25
  EntryData,
24
26
  InterceptSelectorContext,
@@ -133,6 +135,7 @@ export function createRouter<TEnv = any>(
133
135
  const {
134
136
  id: userProvidedId,
135
137
  $$id: injectedId,
138
+ basename: basenameOption,
136
139
  debugPerformance = false,
137
140
  document: documentOption,
138
141
  defaultErrorBoundary,
@@ -158,6 +161,13 @@ export function createRouter<TEnv = any>(
158
161
  originCheck: originCheckOption,
159
162
  } = options;
160
163
 
164
+ // Normalize basename: ensure leading slash, strip trailing slash.
165
+ // A bare "/" is equivalent to no basename.
166
+ const basename =
167
+ basenameOption && basenameOption.replace(/^\/+|\/+$/g, "")
168
+ ? "/" + basenameOption.replace(/^\/+|\/+$/g, "")
169
+ : undefined;
170
+
161
171
  // Resolve telemetry sink (no-op when not configured)
162
172
  const telemetry = resolveSink(telemetrySink);
163
173
 
@@ -659,8 +669,15 @@ export function createRouter<TEnv = any>(
659
669
  const router: RSCRouterInternal<TEnv, {}> = {
660
670
  __brand: RSC_ROUTER_BRAND,
661
671
  id: routerId,
672
+ basename,
673
+
674
+ routes(patternsOrBuilder: UrlPatterns<TEnv> | UrlBuilder<TEnv>): any {
675
+ // Wrap builder functions in urls() automatically
676
+ const urlPatterns: UrlPatterns<TEnv> =
677
+ typeof patternsOrBuilder === "function"
678
+ ? (urls(patternsOrBuilder) as UrlPatterns<TEnv>)
679
+ : patternsOrBuilder;
662
680
 
663
- routes(urlPatterns: UrlPatterns<TEnv>): any {
664
681
  // Store reference for runtime manifest generation
665
682
  storedUrlPatterns = urlPatterns;
666
683
  const currentMountIndex = mountIndex++;
@@ -708,6 +725,10 @@ export function createRouter<TEnv = any>(
708
725
  counters: {},
709
726
  mountIndex: currentMountIndex,
710
727
  cacheProfiles: resolvedCacheProfiles,
728
+ // basename sets the initial URL prefix so all path() patterns
729
+ // are registered with the prefix (e.g. "/admin" + "/users" = "/admin/users").
730
+ // No namePrefix — route names stay unprefixed.
731
+ ...(basename ? { urlPrefix: basename } : {}),
711
732
  },
712
733
  () => {
713
734
  handlerResult = urlPatterns.handler() as AllUseItems[];
@@ -856,8 +877,18 @@ export function createRouter<TEnv = any>(
856
877
  patternOrMiddleware: string | MiddlewareFn<TEnv>,
857
878
  middleware?: MiddlewareFn<TEnv>,
858
879
  ): any {
859
- // Global middleware - no mount prefix
860
- addMiddleware(patternOrMiddleware, middleware, null);
880
+ // Auto-prefix pattern with basename so router-level middleware
881
+ // patterns are router-relative (e.g. "/users/*" matches "/app/users/*").
882
+ if (basename && typeof patternOrMiddleware === "string") {
883
+ const pattern = patternOrMiddleware;
884
+ const prefixed =
885
+ pattern === "/*" || pattern === "*"
886
+ ? `${basename}/*`
887
+ : `${basename}${pattern}`;
888
+ addMiddleware(prefixed, middleware, null);
889
+ } else {
890
+ addMiddleware(patternOrMiddleware, middleware, null);
891
+ }
861
892
  return router;
862
893
  },
863
894
 
@@ -958,6 +989,9 @@ export function createRouter<TEnv = any>(
958
989
  // Expose source file for per-router type generation
959
990
  __sourceFile,
960
991
 
992
+ // Expose basename for runtime manifest generation
993
+ __basename: basename,
994
+
961
995
  // RSC request handler (lazily created on first call)
962
996
  fetch: (() => {
963
997
  // Handler is created on first call and reused
@@ -999,7 +1033,9 @@ export function createRouter<TEnv = any>(
999
1033
  RouterRegistry.set(routerId, router);
1000
1034
 
1001
1035
  // If urls option was provided, auto-register them
1002
- if (urlsOption) {
1036
+ if (typeof urlsOption === "function") {
1037
+ return router.routes(urlsOption) as RSCRouter<TEnv, {}>;
1038
+ } else if (urlsOption) {
1003
1039
  return router.routes(urlsOption) as RSCRouter<TEnv, {}>;
1004
1040
  }
1005
1041
 
@@ -452,6 +452,9 @@ export function createRSCHandler<
452
452
  // - Server components during rendering
453
453
  // - Error boundaries
454
454
  // - Streaming
455
+ // Store basename on request context (scoped per-request via existing ALS)
456
+ requestContext._basename = router.basename;
457
+
455
458
  return runWithRequestContext(requestContext, async () => {
456
459
  // Core handler logic (wrapped by middleware)
457
460
  const coreHandler = async (): Promise<Response> => {
@@ -840,7 +843,11 @@ export function createRSCHandler<
840
843
  handleStore?: ReturnType<typeof requireRequestContext>["_handleStore"],
841
844
  actionContinuation?: ActionContinuation,
842
845
  ): Promise<Response> {
843
- const isPartial = url.searchParams.has("_rsc_partial");
846
+ // App switch detection: if the client's routerId doesn't match this
847
+ // router, downgrade to a full render so the entire tree is replaced.
848
+ const clientRouterId = url.searchParams.get("_rsc_rid");
849
+ const isAppSwitch = !!(clientRouterId && clientRouterId !== router.id);
850
+ const isPartial = url.searchParams.has("_rsc_partial") && !isAppSwitch;
844
851
  const isAction =
845
852
  request.headers.has("rsc-action") || url.searchParams.has("_rsc_action");
846
853
 
@@ -1025,6 +1032,8 @@ export function createRSCHandler<
1025
1032
  const payload: RscPayload = {
1026
1033
  metadata: {
1027
1034
  pathname: url.pathname,
1035
+ routerId: router.id,
1036
+ basename: router.basename,
1028
1037
  segments: [notFoundSegment],
1029
1038
  matched: [],
1030
1039
  diff: [],
@@ -31,7 +31,11 @@ export async function buildRouterTrieFromUrlpatterns(
31
31
  ): Promise<void> {
32
32
  const { generateManifestFull } =
33
33
  await import("../build/generate-manifest.js");
34
- const generated = generateManifestFull(router.urlpatterns);
34
+ const generated = generateManifestFull(
35
+ router.urlpatterns,
36
+ undefined,
37
+ router.basename ? { urlPrefix: router.basename } : undefined,
38
+ );
35
39
  if (
36
40
  generated._routeAncestry &&
37
41
  Object.keys(generated._routeAncestry).length > 0
@@ -243,6 +243,8 @@ export async function handleProgressiveEnhancement<TEnv>(
243
243
  const payload: RscPayload = {
244
244
  metadata: {
245
245
  pathname: url.pathname,
246
+ routerId: ctx.router.id,
247
+ basename: ctx.router.basename,
246
248
  segments: match.segments,
247
249
  matched: match.matched,
248
250
  diff: match.diff,
@@ -342,6 +344,8 @@ async function renderPeErrorBoundary<TEnv>(
342
344
  const payload: RscPayload = {
343
345
  metadata: {
344
346
  pathname: url.pathname,
347
+ routerId: ctx.router.id,
348
+ basename: ctx.router.basename,
345
349
  segments: errorResult.segments,
346
350
  matched: errorResult.matched,
347
351
  diff: errorResult.diff,
@@ -54,6 +54,8 @@ export async function handleRscRendering<TEnv>(
54
54
  payload = {
55
55
  metadata: {
56
56
  pathname: url.pathname,
57
+ routerId: ctx.router.id,
58
+ basename: ctx.router.basename,
57
59
  segments: match.segments,
58
60
  matched: match.matched,
59
61
  diff: match.diff,
@@ -75,6 +77,7 @@ export async function handleRscRendering<TEnv>(
75
77
  payload = {
76
78
  metadata: {
77
79
  pathname: url.pathname,
80
+ routerId: ctx.router.id,
78
81
  segments: result.segments,
79
82
  matched: result.matched,
80
83
  diff: result.diff,
@@ -136,6 +139,8 @@ export async function handleRscRendering<TEnv>(
136
139
 
137
140
  metadata: {
138
141
  pathname: url.pathname,
142
+ routerId: ctx.router.id,
143
+ basename: ctx.router.basename,
139
144
  segments: match.segments,
140
145
  matched: match.matched,
141
146
  diff: match.diff,
@@ -208,6 +208,7 @@ export async function executeServerAction<TEnv>(
208
208
  const payload: RscPayload = {
209
209
  metadata: {
210
210
  pathname: url.pathname,
211
+ routerId: ctx.router.id,
211
212
  segments: errorResult.segments,
212
213
  isPartial: true,
213
214
  matched: errorResult.matched,
@@ -314,6 +315,7 @@ export async function revalidateAfterAction<TEnv>(
314
315
  const payload: RscPayload = {
315
316
  metadata: {
316
317
  pathname: url.pathname,
318
+ routerId: ctx.router.id,
317
319
  segments: matchResult.segments,
318
320
  isPartial: true,
319
321
  matched: matchResult.matched,
@@ -77,7 +77,7 @@ export function getSSRSetup<TEnv>(
77
77
  url: URL,
78
78
  metricsStore: MetricsStore | undefined,
79
79
  ): Promise<SSRSetup> {
80
- const early = _getRequestContext()?.var?.[SSR_SETUP_VAR] as
80
+ const early = _getRequestContext()?._variables?.[SSR_SETUP_VAR] as
81
81
  | Promise<SSRSetup>
82
82
  | undefined;
83
83
  if (early) return early;
package/src/rsc/types.ts CHANGED
@@ -19,6 +19,9 @@ export interface RscPayload {
19
19
  metadata?: {
20
20
  pathname: string;
21
21
  segments: ResolvedSegment[];
22
+ /** Router instance ID. When this changes between navigations, the client
23
+ * discards cached segments and does a full tree replacement (app switch). */
24
+ routerId?: string;
22
25
  isPartial?: boolean;
23
26
  isError?: boolean;
24
27
  matched?: string[];
@@ -38,6 +41,8 @@ export interface RscPayload {
38
41
  themeConfig?: ResolvedThemeConfig | null;
39
42
  /** Initial theme from cookie (for SSR hydration) */
40
43
  initialTheme?: Theme;
44
+ /** URL prefix for all routes (from createRouter({ basename })). */
45
+ basename?: string;
41
46
  /** Whether connection warmup is enabled */
42
47
  warmupEnabled?: boolean;
43
48
  /** Server-side redirect with optional state (for partial requests) */
@@ -69,8 +69,8 @@ export interface RequestContext<
69
69
  pathname: string;
70
70
  /** URL search params (with internal `_rsc*` params stripped, same as `url.searchParams`) */
71
71
  searchParams: URLSearchParams;
72
- /** Variables set by middleware (same as ctx.var) */
73
- var: Record<string, any>;
72
+ /** @internal Shared variable backing store for ctx.get()/ctx.set(). */
73
+ _variables: Record<string, any>;
74
74
  /** Get a variable set by middleware */
75
75
  get: {
76
76
  <T>(contextVar: ContextVar<T>): T | undefined;
@@ -287,6 +287,9 @@ export interface RequestContext<
287
287
 
288
288
  /** @internal Request-scoped performance metrics store */
289
289
  _metricsStore?: MetricsStore;
290
+
291
+ /** @internal Router basename for this request (used by redirect()) */
292
+ _basename?: string;
290
293
  }
291
294
 
292
295
  /**
@@ -316,7 +319,9 @@ export type PublicRequestContext<
316
319
  | "_reportBackgroundError"
317
320
  | "_debugPerformance"
318
321
  | "_metricsStore"
322
+ | "_basename"
319
323
  | "_setStatus"
324
+ | "_variables"
320
325
  | "res"
321
326
  >;
322
327
 
@@ -591,7 +596,7 @@ export function createRequestContext<TEnv>(
591
596
  originalUrl: new URL(request.url),
592
597
  pathname: url.pathname,
593
598
  searchParams: cleanUrl.searchParams,
594
- var: variables,
599
+ _variables: variables,
595
600
  get: ((keyOrVar: any) => {
596
601
  if (isNonCacheable(variables, keyOrVar) && isInsideCacheScope()) {
597
602
  throw new Error(
@@ -923,7 +928,6 @@ export function createUseFunction<TEnv>(
923
928
  pathname: ctx.pathname,
924
929
  url: ctx.url,
925
930
  env: ctx.env as any,
926
- var: ctx.var as any,
927
931
  get: ctx.get as any,
928
932
  use: <TDep, TDepParams = any>(
929
933
  dep: LoaderDefinition<TDep, TDepParams>,
package/src/ssr/index.tsx CHANGED
@@ -129,6 +129,7 @@ interface RscPayload {
129
129
  matched?: string[];
130
130
  pathname?: string;
131
131
  params?: Record<string, string>;
132
+ basename?: string;
132
133
  themeConfig?: ResolvedThemeConfig | null;
133
134
  initialTheme?: Theme;
134
135
  version?: string;
@@ -261,6 +262,7 @@ export function createSSRHandler<TEnv = unknown>(deps: SSRDependencies<TEnv>) {
261
262
  function SsrRoot() {
262
263
  payload ??= createFromReadableStream<RscPayload>(rscStream1);
263
264
  const resolved = React.use(payload);
265
+
264
266
  const themeConfig = resolved.metadata?.themeConfig ?? null;
265
267
  const pathname = resolved.metadata?.pathname ?? "/";
266
268
 
@@ -286,6 +288,7 @@ export function createSSRHandler<TEnv = unknown>(deps: SSRDependencies<TEnv>) {
286
288
  navigate: async () => {},
287
289
  refresh: async () => {},
288
290
  version: resolved.metadata?.version,
291
+ basename: resolved.metadata?.basename,
289
292
  };
290
293
 
291
294
  // Build content tree from segments.
@@ -5,8 +5,8 @@
5
5
  * during cache key generation (before middleware runs).
6
6
  *
7
7
  * Note: While the full RequestContext is passed, middleware-set variables
8
- * (ctx.var, ctx.get()) may not be populated yet since cache lookup
9
- * happens before middleware execution.
8
+ * read via `ctx.get()` may not be populated yet since cache lookup happens
9
+ * before middleware execution.
10
10
  */
11
11
  export type { RequestContext as CacheContext } from "../server/request-context.js";
12
12
 
@@ -101,7 +101,7 @@ export interface CacheOptions<TEnv = unknown> {
101
101
  * Return false to skip cache for this request (always fetch fresh).
102
102
  *
103
103
  * Has access to full RequestContext including env, request, params, cookies, etc.
104
- * Note: Middleware-set variables (ctx.var) may not be populated yet.
104
+ * Note: Middleware-set variables read via `ctx.get()` may not be populated yet.
105
105
  *
106
106
  * @example
107
107
  * ```typescript
@@ -123,7 +123,7 @@ export interface CacheOptions<TEnv = unknown> {
123
123
  * Bypasses default key generation AND store's keyGenerator.
124
124
  *
125
125
  * Has access to full RequestContext including env, request, params, cookies, etc.
126
- * Note: Middleware-set variables (ctx.var) may not be populated yet.
126
+ * Note: Middleware-set variables read via `ctx.get()` may not be populated yet.
127
127
  *
128
128
  * @example
129
129
  * ```typescript
@@ -170,7 +170,7 @@ export type Handler<
170
170
  * - Cleaned route URL (`url`, `searchParams`, `pathname` — no `_rsc*` params)
171
171
  * - Original request (`request` — raw transport URL, headers, method, body)
172
172
  * - Platform bindings (env.DB, env.KV, env.SECRETS)
173
- * - Middleware variables (var.user, var.permissions)
173
+ * - Middleware variables (`get("user")`, `get("permissions")`)
174
174
  * - Getter/setter for variables (get('user'), set('user', ...))
175
175
  *
176
176
  * @example
@@ -178,8 +178,7 @@ export type Handler<
178
178
  * const handler = (ctx: HandlerContext<{ slug: string }, AppEnv>) => {
179
179
  * ctx.params.slug // Route param (string)
180
180
  * ctx.env.DB // Binding (D1Database)
181
- * ctx.var.user // Variable (User | undefined)
182
- * ctx.get('user') // Alternative getter
181
+ * ctx.get('user') // Variable (User | undefined)
183
182
  * ctx.set('user', {...}) // Setter
184
183
  * ctx.url // Clean URL (no _rsc* params)
185
184
  * ctx.searchParams // Clean params (no _rsc* params)
@@ -244,14 +243,9 @@ export type HandlerContext<
244
243
  * Access resources like `ctx.env.DB`, `ctx.env.KV`.
245
244
  */
246
245
  env: TEnv;
247
- /**
248
- * Middleware-injected variables.
249
- * Access values like `ctx.var.user`, `ctx.var.permissions`.
250
- */
251
- var: DefaultVars;
252
246
  /**
253
247
  * Type-safe getter for middleware variables.
254
- * Alternative to `ctx.var.key` with better autocomplete.
248
+ * Preferred way to read middleware-injected variables.
255
249
  *
256
250
  * @example
257
251
  * ```typescript
@@ -448,6 +442,8 @@ export type InternalHandlerContext<
448
442
  > = HandlerContext<TParams, TEnv, TSearch> & {
449
443
  /** @internal Stub response for collecting headers/cookies. */
450
444
  res: Response;
445
+ /** @internal Shared variable backing store for ctx.get()/ctx.set(). */
446
+ _variables: Record<string, any>;
451
447
  /** Prerender-only control flow helper, attached when the runtime context supports it. */
452
448
  passthrough?: () => unknown;
453
449
  /** Current segment ID for handle data attribution. */
@@ -53,7 +53,6 @@ export type LoaderContext<
53
53
  pathname: string;
54
54
  url: URL;
55
55
  env: TEnv;
56
- var: DefaultVars;
57
56
  get: {
58
57
  <T>(contextVar: ContextVar<T>): T | undefined;
59
58
  } & (<K extends keyof DefaultVars>(key: K) => DefaultVars[K]);
@@ -7,6 +7,18 @@ import type {
7
7
  } from "../route-types.js";
8
8
  import type { SearchSchema } from "../search-params.js";
9
9
  import { RESPONSE_TYPE } from "./response-types.js";
10
+ import type { DefaultEnv } from "../types.js";
11
+ import type { PathHelpers } from "./path-helper-types.js";
12
+
13
+ /**
14
+ * Builder function accepted by urls() and as a shorthand for routes()/urls option.
15
+ * When passed directly to routes() or createRouter({ urls }), it is wrapped in urls() automatically.
16
+ */
17
+ export type UrlBuilder<
18
+ TEnv = DefaultEnv,
19
+ TItems extends readonly (AllUseItems | readonly AllUseItems[])[] =
20
+ readonly AllUseItems[],
21
+ > = (helpers: PathHelpers<TEnv>) => TItems;
10
22
 
11
23
  /**
12
24
  * Sentinel type for unnamed routes.
@@ -135,7 +135,11 @@ export async function discoverRouters(
135
135
  continue;
136
136
  }
137
137
 
138
- const manifest = generateManifestFull(router.urlpatterns, routerMountIndex);
138
+ const manifest = generateManifestFull(
139
+ router.urlpatterns,
140
+ routerMountIndex,
141
+ router.__basename ? { urlPrefix: router.__basename } : undefined,
142
+ );
139
143
  routerMountIndex++;
140
144
  allManifests.push({ id, manifest });
141
145
  const routeCount = Object.keys(manifest.routeManifest).length;