@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.
- package/dist/bin/rango.js +128 -46
- package/dist/vite/index.js +119 -43
- package/package.json +1 -1
- package/skills/links/SKILL.md +3 -1
- package/skills/middleware/SKILL.md +2 -0
- package/skills/router-setup/SKILL.md +35 -0
- package/src/browser/navigation-bridge.ts +6 -0
- package/src/browser/navigation-client.ts +4 -0
- package/src/browser/navigation-store.ts +43 -8
- package/src/browser/partial-update.ts +17 -1
- package/src/browser/prefetch/fetch.ts +8 -2
- package/src/browser/react/Link.tsx +43 -8
- package/src/browser/react/NavigationProvider.tsx +7 -0
- package/src/browser/react/context.ts +6 -0
- package/src/browser/react/use-router.ts +20 -8
- package/src/browser/rsc-router.tsx +8 -0
- package/src/browser/server-action-bridge.ts +5 -0
- package/src/browser/types.ts +18 -4
- package/src/build/generate-manifest.ts +3 -0
- package/src/build/generate-route-types.ts +3 -0
- package/src/build/route-types/include-resolution.ts +8 -1
- package/src/build/route-types/router-processing.ts +211 -72
- package/src/route-definition/redirect.ts +9 -1
- package/src/router/handler-context.ts +5 -9
- package/src/router/intercept-resolution.ts +6 -2
- package/src/router/loader-resolution.ts +3 -2
- package/src/router/middleware-types.ts +0 -6
- package/src/router/middleware.ts +0 -3
- package/src/router/prerender-match.ts +2 -2
- package/src/router/router-interfaces.ts +25 -4
- package/src/router/router-options.ts +37 -11
- package/src/router.ts +40 -4
- package/src/rsc/handler.ts +10 -1
- package/src/rsc/manifest-init.ts +5 -1
- package/src/rsc/progressive-enhancement.ts +4 -0
- package/src/rsc/rsc-rendering.ts +5 -0
- package/src/rsc/server-action.ts +2 -0
- package/src/rsc/ssr-setup.ts +1 -1
- package/src/rsc/types.ts +5 -0
- package/src/server/request-context.ts +8 -4
- package/src/ssr/index.tsx +3 -0
- package/src/types/cache-types.ts +4 -4
- package/src/types/handler-context.ts +5 -9
- package/src/types/loader-types.ts +0 -1
- package/src/urls/pattern-types.ts +12 -0
- 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
|
-
*
|
|
341
|
-
* directly
|
|
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
|
-
*
|
|
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
|
-
//
|
|
860
|
-
|
|
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
|
|
package/src/rsc/handler.ts
CHANGED
|
@@ -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
|
-
|
|
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: [],
|
package/src/rsc/manifest-init.ts
CHANGED
|
@@ -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(
|
|
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,
|
package/src/rsc/rsc-rendering.ts
CHANGED
|
@@ -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,
|
package/src/rsc/server-action.ts
CHANGED
|
@@ -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,
|
package/src/rsc/ssr-setup.ts
CHANGED
|
@@ -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()?.
|
|
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
|
-
/**
|
|
73
|
-
|
|
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
|
-
|
|
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.
|
package/src/types/cache-types.ts
CHANGED
|
@@ -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
|
-
*
|
|
9
|
-
*
|
|
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
|
|
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
|
|
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 (
|
|
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.
|
|
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
|
-
*
|
|
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. */
|
|
@@ -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(
|
|
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;
|