@rangojs/router 0.0.0-experimental.20 → 0.0.0-experimental.20dbba0c
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/AGENTS.md +4 -0
- package/README.md +172 -50
- package/dist/bin/rango.js +138 -50
- package/dist/vite/index.js +1160 -508
- package/dist/vite/index.js.bak +5448 -0
- package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
- package/package.json +17 -16
- package/skills/breadcrumbs/SKILL.md +252 -0
- package/skills/cache-guide/SKILL.md +32 -0
- package/skills/caching/SKILL.md +49 -8
- package/skills/document-cache/SKILL.md +2 -2
- package/skills/handler-use/SKILL.md +362 -0
- package/skills/hooks/SKILL.md +61 -51
- package/skills/host-router/SKILL.md +218 -0
- package/skills/intercept/SKILL.md +20 -0
- package/skills/layout/SKILL.md +22 -0
- package/skills/links/SKILL.md +91 -17
- package/skills/loader/SKILL.md +107 -24
- package/skills/middleware/SKILL.md +34 -3
- package/skills/migrate-nextjs/SKILL.md +560 -0
- package/skills/migrate-react-router/SKILL.md +765 -0
- package/skills/parallel/SKILL.md +185 -0
- package/skills/prerender/SKILL.md +112 -70
- package/skills/rango/SKILL.md +24 -23
- package/skills/response-routes/SKILL.md +8 -0
- package/skills/route/SKILL.md +58 -4
- package/skills/router-setup/SKILL.md +95 -5
- package/skills/streams-and-websockets/SKILL.md +283 -0
- package/skills/typesafety/SKILL.md +38 -24
- package/src/__internal.ts +92 -0
- package/src/browser/app-shell.ts +52 -0
- package/src/browser/app-version.ts +14 -0
- package/src/browser/event-controller.ts +5 -0
- package/src/browser/link-interceptor.ts +4 -0
- package/src/browser/navigation-bridge.ts +175 -17
- package/src/browser/navigation-client.ts +177 -44
- package/src/browser/navigation-store.ts +68 -9
- package/src/browser/navigation-transaction.ts +11 -9
- package/src/browser/partial-update.ts +113 -17
- package/src/browser/prefetch/cache.ts +275 -28
- package/src/browser/prefetch/fetch.ts +191 -46
- package/src/browser/prefetch/policy.ts +6 -0
- package/src/browser/prefetch/queue.ts +123 -20
- package/src/browser/prefetch/resource-ready.ts +77 -0
- package/src/browser/rango-state.ts +53 -13
- package/src/browser/react/Link.tsx +98 -14
- package/src/browser/react/NavigationProvider.tsx +89 -14
- package/src/browser/react/context.ts +7 -2
- package/src/browser/react/use-handle.ts +9 -58
- package/src/browser/react/use-navigation.ts +22 -2
- package/src/browser/react/use-params.ts +11 -1
- package/src/browser/react/use-router.ts +29 -9
- package/src/browser/rsc-router.tsx +177 -66
- package/src/browser/scroll-restoration.ts +41 -42
- package/src/browser/segment-reconciler.ts +36 -9
- package/src/browser/server-action-bridge.ts +8 -6
- package/src/browser/types.ts +73 -5
- package/src/build/generate-manifest.ts +6 -6
- package/src/build/generate-route-types.ts +3 -0
- package/src/build/route-trie.ts +67 -25
- package/src/build/route-types/include-resolution.ts +8 -1
- package/src/build/route-types/router-processing.ts +223 -74
- package/src/build/route-types/scan-filter.ts +8 -1
- package/src/cache/cache-runtime.ts +15 -11
- package/src/cache/cache-scope.ts +48 -7
- package/src/cache/cf/cf-cache-store.ts +455 -15
- package/src/cache/cf/index.ts +5 -1
- package/src/cache/document-cache.ts +17 -7
- package/src/cache/index.ts +1 -0
- package/src/cache/taint.ts +55 -0
- package/src/client.rsc.tsx +2 -1
- package/src/client.tsx +85 -276
- package/src/context-var.ts +72 -2
- package/src/debug.ts +2 -2
- package/src/handle.ts +40 -0
- package/src/handles/breadcrumbs.ts +66 -0
- package/src/handles/index.ts +1 -0
- package/src/host/index.ts +0 -3
- package/src/index.rsc.ts +9 -36
- package/src/index.ts +79 -70
- package/src/outlet-context.ts +1 -1
- package/src/prerender/store.ts +57 -15
- package/src/prerender.ts +138 -77
- package/src/response-utils.ts +28 -0
- package/src/reverse.ts +27 -2
- package/src/route-definition/dsl-helpers.ts +240 -40
- package/src/route-definition/helpers-types.ts +67 -19
- package/src/route-definition/index.ts +3 -3
- package/src/route-definition/redirect.ts +11 -3
- package/src/route-definition/resolve-handler-use.ts +155 -0
- package/src/route-map-builder.ts +7 -1
- package/src/route-types.ts +18 -0
- package/src/router/content-negotiation.ts +100 -1
- package/src/router/find-match.ts +4 -2
- package/src/router/handler-context.ts +129 -26
- package/src/router/intercept-resolution.ts +11 -4
- package/src/router/lazy-includes.ts +10 -7
- package/src/router/loader-resolution.ts +160 -22
- package/src/router/logging.ts +5 -2
- package/src/router/manifest.ts +31 -16
- package/src/router/match-api.ts +128 -193
- package/src/router/match-middleware/background-revalidation.ts +30 -2
- package/src/router/match-middleware/cache-lookup.ts +94 -17
- package/src/router/match-middleware/cache-store.ts +53 -10
- package/src/router/match-middleware/intercept-resolution.ts +9 -7
- package/src/router/match-middleware/segment-resolution.ts +61 -5
- package/src/router/match-result.ts +103 -18
- package/src/router/metrics.ts +238 -13
- package/src/router/middleware-types.ts +48 -27
- package/src/router/middleware.ts +201 -86
- package/src/router/navigation-snapshot.ts +182 -0
- package/src/router/pattern-matching.ts +77 -11
- package/src/router/prerender-match.ts +114 -10
- package/src/router/preview-match.ts +30 -102
- package/src/router/request-classification.ts +310 -0
- package/src/router/revalidation.ts +27 -7
- package/src/router/route-snapshot.ts +245 -0
- package/src/router/router-context.ts +6 -1
- package/src/router/router-interfaces.ts +50 -5
- package/src/router/router-options.ts +50 -19
- package/src/router/segment-resolution/fresh.ts +215 -19
- package/src/router/segment-resolution/helpers.ts +30 -25
- package/src/router/segment-resolution/loader-cache.ts +1 -0
- package/src/router/segment-resolution/revalidation.ts +454 -301
- package/src/router/segment-wrappers.ts +2 -0
- package/src/router/trie-matching.ts +30 -6
- package/src/router/types.ts +1 -0
- package/src/router/url-params.ts +49 -0
- package/src/router.ts +89 -17
- package/src/rsc/handler.ts +563 -364
- package/src/rsc/helpers.ts +69 -41
- package/src/rsc/index.ts +0 -20
- package/src/rsc/loader-fetch.ts +23 -3
- package/src/rsc/manifest-init.ts +5 -1
- package/src/rsc/progressive-enhancement.ts +37 -10
- package/src/rsc/response-route-handler.ts +14 -1
- package/src/rsc/rsc-rendering.ts +47 -44
- package/src/rsc/server-action.ts +24 -10
- package/src/rsc/ssr-setup.ts +128 -0
- package/src/rsc/types.ts +11 -1
- package/src/search-params.ts +16 -13
- package/src/segment-content-promise.ts +67 -0
- package/src/segment-loader-promise.ts +122 -0
- package/src/segment-system.tsx +109 -23
- package/src/server/context.ts +174 -19
- package/src/server/handle-store.ts +19 -0
- package/src/server/loader-registry.ts +9 -8
- package/src/server/request-context.ts +218 -65
- package/src/server.ts +6 -0
- package/src/ssr/index.tsx +4 -0
- package/src/static-handler.ts +18 -6
- package/src/theme/index.ts +4 -13
- package/src/types/cache-types.ts +4 -4
- package/src/types/handler-context.ts +140 -72
- package/src/types/loader-types.ts +41 -15
- package/src/types/request-scope.ts +126 -0
- package/src/types/route-config.ts +17 -8
- package/src/types/route-entry.ts +19 -1
- package/src/types/segments.ts +2 -5
- package/src/urls/include-helper.ts +24 -14
- package/src/urls/path-helper-types.ts +39 -6
- package/src/urls/path-helper.ts +48 -13
- package/src/urls/pattern-types.ts +12 -0
- package/src/urls/response-types.ts +18 -16
- package/src/use-loader.tsx +77 -5
- package/src/vite/discovery/bundle-postprocess.ts +61 -89
- package/src/vite/discovery/discover-routers.ts +7 -4
- package/src/vite/discovery/prerender-collection.ts +162 -88
- package/src/vite/discovery/state.ts +17 -13
- package/src/vite/index.ts +8 -3
- package/src/vite/plugin-types.ts +51 -79
- package/src/vite/plugins/cloudflare-protocol-loader-hook.d.mts +23 -0
- package/src/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
- package/src/vite/plugins/cloudflare-protocol-stub.ts +214 -0
- package/src/vite/plugins/expose-action-id.ts +1 -3
- package/src/vite/plugins/expose-id-utils.ts +12 -0
- package/src/vite/plugins/expose-ids/handler-transform.ts +30 -0
- package/src/vite/plugins/expose-internal-ids.ts +257 -40
- package/src/vite/plugins/performance-tracks.ts +88 -0
- package/src/vite/plugins/refresh-cmd.ts +127 -0
- package/src/vite/plugins/version-plugin.ts +13 -1
- package/src/vite/rango.ts +190 -217
- package/src/vite/router-discovery.ts +241 -45
- package/src/vite/utils/banner.ts +4 -4
- package/src/vite/utils/package-resolution.ts +34 -1
- package/src/vite/utils/prerender-utils.ts +97 -5
- package/src/vite/utils/shared-utils.ts +3 -2
- package/skills/testing/SKILL.md +0 -226
- package/src/route-definition/route-function.ts +0 -119
|
@@ -204,6 +204,7 @@ export function createSegmentWrappers<TEnv = any>(
|
|
|
204
204
|
interceptResult: { intercept: InterceptEntry; entry: EntryData } | null,
|
|
205
205
|
localRouteName: string,
|
|
206
206
|
pathname: string,
|
|
207
|
+
stale?: boolean,
|
|
207
208
|
): ReturnType<typeof _resolveAllSegmentsWithRevalidation> {
|
|
208
209
|
return _resolveAllSegmentsWithRevalidation(
|
|
209
210
|
entries,
|
|
@@ -221,6 +222,7 @@ export function createSegmentWrappers<TEnv = any>(
|
|
|
221
222
|
localRouteName,
|
|
222
223
|
pathname,
|
|
223
224
|
segmentDeps,
|
|
225
|
+
stale,
|
|
224
226
|
);
|
|
225
227
|
}
|
|
226
228
|
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import type { TrieNode, TrieLeaf } from "../build/route-trie.js";
|
|
9
|
+
import { safeDecodeURIComponent } from "./url-params.js";
|
|
9
10
|
|
|
10
11
|
export interface TrieMatchResult {
|
|
11
12
|
/** Route name */
|
|
@@ -114,7 +115,25 @@ function walkTrie(
|
|
|
114
115
|
if (result) return result;
|
|
115
116
|
}
|
|
116
117
|
|
|
117
|
-
// Priority 2:
|
|
118
|
+
// Priority 2: Suffix-param match (e.g., :productId.html)
|
|
119
|
+
if (node.xp) {
|
|
120
|
+
for (const suffix in node.xp) {
|
|
121
|
+
if (segment.endsWith(suffix) && segment.length > suffix.length) {
|
|
122
|
+
const paramValue = segment.slice(0, -suffix.length);
|
|
123
|
+
paramValues.push(paramValue);
|
|
124
|
+
const result = walkTrie(
|
|
125
|
+
node.xp[suffix].c,
|
|
126
|
+
segments,
|
|
127
|
+
index + 1,
|
|
128
|
+
paramValues,
|
|
129
|
+
);
|
|
130
|
+
paramValues.pop();
|
|
131
|
+
if (result) return result;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Priority 3: Param match
|
|
118
137
|
if (node.p) {
|
|
119
138
|
paramValues.push(segment);
|
|
120
139
|
const result = walkTrie(node.p.c, segments, index + 1, paramValues);
|
|
@@ -122,7 +141,7 @@ function walkTrie(
|
|
|
122
141
|
if (result) return result;
|
|
123
142
|
}
|
|
124
143
|
|
|
125
|
-
// Priority
|
|
144
|
+
// Priority 4: Wildcard match (consumes rest)
|
|
126
145
|
if (node.w) {
|
|
127
146
|
const rest = joinRemainingSegments(segments, index);
|
|
128
147
|
return {
|
|
@@ -155,20 +174,25 @@ function validateAndBuild(
|
|
|
155
174
|
originalPathname: string,
|
|
156
175
|
pathnameHasTrailingSlash: boolean,
|
|
157
176
|
): TrieMatchResult | null {
|
|
158
|
-
// Build named params by zipping leaf.pa with positional paramValues
|
|
177
|
+
// Build named params by zipping leaf.pa with positional paramValues.
|
|
178
|
+
// Params are URL-decoded at this boundary so ctx.params holds the values
|
|
179
|
+
// apps expect (matching Express/React Router) and round-trip cleanly
|
|
180
|
+
// through ctx.reverse.
|
|
159
181
|
const params: Record<string, string> = {};
|
|
160
182
|
if (leaf.pa) {
|
|
161
183
|
for (let i = 0; i < leaf.pa.length && i < paramValues.length; i++) {
|
|
162
|
-
params[leaf.pa[i]] = paramValues[i];
|
|
184
|
+
params[leaf.pa[i]] = safeDecodeURIComponent(paramValues[i]);
|
|
163
185
|
}
|
|
164
186
|
}
|
|
165
187
|
|
|
166
188
|
// Add wildcard param (wildcard leaves have pn from TrieNode.w type)
|
|
167
189
|
if (wildcardValue !== undefined && "pn" in leaf) {
|
|
168
|
-
params[(leaf as TrieLeaf & { pn: string }).pn] =
|
|
190
|
+
params[(leaf as TrieLeaf & { pn: string }).pn] =
|
|
191
|
+
safeDecodeURIComponent(wildcardValue);
|
|
169
192
|
}
|
|
170
193
|
|
|
171
|
-
// Validate constraints
|
|
194
|
+
// Validate constraints against decoded values so constraint lists can be
|
|
195
|
+
// written in decoded form (e.g. ["en-GB", "en US"]).
|
|
172
196
|
if (leaf.cv) {
|
|
173
197
|
for (const paramName in leaf.cv) {
|
|
174
198
|
const allowed = leaf.cv[paramName]!;
|
package/src/router/types.ts
CHANGED
|
@@ -96,6 +96,7 @@ export interface SegmentResolutionDeps<TEnv = any> {
|
|
|
96
96
|
findNearestNotFoundBoundary: (
|
|
97
97
|
entry: EntryData | null,
|
|
98
98
|
) => ReactNode | NotFoundBoundaryHandler | null;
|
|
99
|
+
notFoundComponent?: ReactNode | ((props: { pathname: string }) => ReactNode);
|
|
99
100
|
callOnError: (error: unknown, phase: ErrorPhase, context: any) => void;
|
|
100
101
|
}
|
|
101
102
|
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* URL param encode/decode at the route boundary.
|
|
3
|
+
*
|
|
4
|
+
* Extraction (decode): regex/trie matchers keep param values URL-encoded;
|
|
5
|
+
* `safeDecodeURIComponent` turns them back into raw strings so `ctx.params`
|
|
6
|
+
* matches the contract apps expect (Express/React Router/Fastify/Koa) and
|
|
7
|
+
* round-trips through reverse stay stable. Malformed %-encoding is
|
|
8
|
+
* preserved as-is so a broken URL doesn't crash matching.
|
|
9
|
+
*
|
|
10
|
+
* Reversal (encode): `encodePathSegment` escapes only what RFC 3986
|
|
11
|
+
* requires for a path segment — `/`, `?`, `#`, space, control chars,
|
|
12
|
+
* non-ASCII — and leaves pchar sub-delims (`@ : $ & + , ; =` and friends)
|
|
13
|
+
* readable. `encodeURIComponent` over-encodes for path segments, which
|
|
14
|
+
* makes generated URLs harder for humans to read in the address bar
|
|
15
|
+
* (e.g. mailbox IDs like `ivo@example.com` would become
|
|
16
|
+
* `ivo%40example.com` even though `@` is path-legal).
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
export function safeDecodeURIComponent(raw: string): string {
|
|
20
|
+
if (raw === "" || raw.indexOf("%") === -1) return raw;
|
|
21
|
+
try {
|
|
22
|
+
return decodeURIComponent(raw);
|
|
23
|
+
} catch {
|
|
24
|
+
return raw;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// encodeURIComponent over-encodes for path segments. After running it,
|
|
29
|
+
// un-encode the pchar sub-delims + (`:` / `@`) so the resulting URL
|
|
30
|
+
// keeps human-readable characters that are legal in a path segment.
|
|
31
|
+
// Everything dangerous — `/ ? # %` and space/control/non-ASCII — stays
|
|
32
|
+
// encoded.
|
|
33
|
+
const PATH_SAFE_ESCAPES: Record<string, string> = {
|
|
34
|
+
"%3A": ":",
|
|
35
|
+
"%40": "@",
|
|
36
|
+
"%24": "$",
|
|
37
|
+
"%26": "&",
|
|
38
|
+
"%2B": "+",
|
|
39
|
+
"%2C": ",",
|
|
40
|
+
"%3B": ";",
|
|
41
|
+
"%3D": "=",
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export function encodePathSegment(value: string): string {
|
|
45
|
+
return encodeURIComponent(value).replace(
|
|
46
|
+
/%(?:3A|40|24|26|2B|2C|3B|3D)/gi,
|
|
47
|
+
(match) => PATH_SAFE_ESCAPES[match.toUpperCase()] ?? match,
|
|
48
|
+
);
|
|
49
|
+
}
|
package/src/router.ts
CHANGED
|
@@ -19,9 +19,10 @@ 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
|
-
EntryData,
|
|
24
|
-
InterceptSelectorContext,
|
|
25
|
+
type EntryData,
|
|
25
26
|
getContext,
|
|
26
27
|
RSCRouterContext,
|
|
27
28
|
type MetricsStore,
|
|
@@ -133,6 +134,7 @@ export function createRouter<TEnv = any>(
|
|
|
133
134
|
const {
|
|
134
135
|
id: userProvidedId,
|
|
135
136
|
$$id: injectedId,
|
|
137
|
+
basename: basenameOption,
|
|
136
138
|
debugPerformance = false,
|
|
137
139
|
document: documentOption,
|
|
138
140
|
defaultErrorBoundary,
|
|
@@ -147,7 +149,7 @@ export function createRouter<TEnv = any>(
|
|
|
147
149
|
$$sourceFile: injectedSourceFile,
|
|
148
150
|
nonce,
|
|
149
151
|
version,
|
|
150
|
-
|
|
152
|
+
prefetchCacheTTL: prefetchCacheTTLOption,
|
|
151
153
|
warmup: warmupOption,
|
|
152
154
|
allowDebugManifest: allowDebugManifestOption = false,
|
|
153
155
|
telemetry: telemetrySink,
|
|
@@ -158,6 +160,13 @@ export function createRouter<TEnv = any>(
|
|
|
158
160
|
originCheck: originCheckOption,
|
|
159
161
|
} = options;
|
|
160
162
|
|
|
163
|
+
// Normalize basename: ensure leading slash, strip trailing slash.
|
|
164
|
+
// A bare "/" is equivalent to no basename.
|
|
165
|
+
const basename =
|
|
166
|
+
basenameOption && basenameOption.replace(/^\/+|\/+$/g, "")
|
|
167
|
+
? "/" + basenameOption.replace(/^\/+|\/+$/g, "")
|
|
168
|
+
: undefined;
|
|
169
|
+
|
|
161
170
|
// Resolve telemetry sink (no-op when not configured)
|
|
162
171
|
const telemetry = resolveSink(telemetrySink);
|
|
163
172
|
|
|
@@ -200,11 +209,17 @@ export function createRouter<TEnv = any>(
|
|
|
200
209
|
const routerId =
|
|
201
210
|
userProvidedId ?? injectedId ?? `router_${nextRouterAutoId()}`;
|
|
202
211
|
|
|
203
|
-
// Resolve prefetch cache
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
212
|
+
// Resolve prefetch cache TTL (default: 300 seconds / 5 minutes)
|
|
213
|
+
// Clamp to a non-negative integer for valid Cache-Control max-age.
|
|
214
|
+
const rawTTL =
|
|
215
|
+
prefetchCacheTTLOption !== undefined ? prefetchCacheTTLOption : 300;
|
|
216
|
+
const prefetchCacheTTLSeconds =
|
|
217
|
+
rawTTL === false ? 0 : Math.max(0, Math.floor(rawTTL));
|
|
218
|
+
const prefetchCacheTTL = prefetchCacheTTLSeconds * 1000;
|
|
219
|
+
const prefetchCacheControl: string | false =
|
|
220
|
+
prefetchCacheTTLSeconds === 0
|
|
221
|
+
? false
|
|
222
|
+
: `private, max-age=${prefetchCacheTTLSeconds}`;
|
|
208
223
|
|
|
209
224
|
// Resolve warmup enabled flag (default: true)
|
|
210
225
|
const warmupEnabled = warmupOption !== false;
|
|
@@ -357,8 +372,18 @@ export function createRouter<TEnv = any>(
|
|
|
357
372
|
return precomputedByPrefix;
|
|
358
373
|
}
|
|
359
374
|
|
|
360
|
-
// Wrapper to pass debugPerformance to external createMetricsStore
|
|
361
|
-
|
|
375
|
+
// Wrapper to pass debugPerformance to external createMetricsStore.
|
|
376
|
+
// Also checks per-request flag set by ctx.debugPerformance() in middleware.
|
|
377
|
+
const getMetricsStore = () => {
|
|
378
|
+
const reqCtx = _getRequestContext();
|
|
379
|
+
const enabled = debugPerformance || !!reqCtx?._debugPerformance;
|
|
380
|
+
if (!enabled) return undefined;
|
|
381
|
+
if (!reqCtx) {
|
|
382
|
+
return createMetricsStore(true);
|
|
383
|
+
}
|
|
384
|
+
reqCtx._metricsStore ??= createMetricsStore(true);
|
|
385
|
+
return reqCtx._metricsStore;
|
|
386
|
+
};
|
|
362
387
|
|
|
363
388
|
// Wrapper to pass defaults to error/notFound boundary finders
|
|
364
389
|
const findNearestErrorBoundary = (entry: EntryData | null) =>
|
|
@@ -510,6 +535,7 @@ export function createRouter<TEnv = any>(
|
|
|
510
535
|
trackHandler,
|
|
511
536
|
findNearestErrorBoundary,
|
|
512
537
|
findNearestNotFoundBoundary,
|
|
538
|
+
notFoundComponent: notFound,
|
|
513
539
|
callOnError,
|
|
514
540
|
};
|
|
515
541
|
|
|
@@ -544,6 +570,7 @@ export function createRouter<TEnv = any>(
|
|
|
544
570
|
mergedRouteMap,
|
|
545
571
|
nextMountIndex: () => mountIndex++,
|
|
546
572
|
getPrecomputedByPrefix,
|
|
573
|
+
routerId,
|
|
547
574
|
};
|
|
548
575
|
|
|
549
576
|
function evaluateLazyEntry(entry: RouteEntry<TEnv>): void {
|
|
@@ -597,6 +624,8 @@ export function createRouter<TEnv = any>(
|
|
|
597
624
|
params: Record<string, string>,
|
|
598
625
|
buildVars?: Record<string, any>,
|
|
599
626
|
isPassthroughRoute?: boolean,
|
|
627
|
+
buildEnv?: TEnv,
|
|
628
|
+
devMode?: boolean,
|
|
600
629
|
) {
|
|
601
630
|
return _matchForPrerender(
|
|
602
631
|
pathname,
|
|
@@ -604,6 +633,8 @@ export function createRouter<TEnv = any>(
|
|
|
604
633
|
prerenderDeps,
|
|
605
634
|
buildVars,
|
|
606
635
|
isPassthroughRoute,
|
|
636
|
+
buildEnv,
|
|
637
|
+
devMode,
|
|
607
638
|
);
|
|
608
639
|
}
|
|
609
640
|
|
|
@@ -611,12 +642,16 @@ export function createRouter<TEnv = any>(
|
|
|
611
642
|
handler: Function,
|
|
612
643
|
handlerId: string,
|
|
613
644
|
routeName?: string,
|
|
645
|
+
buildEnv?: TEnv,
|
|
646
|
+
devMode?: boolean,
|
|
614
647
|
) {
|
|
615
648
|
return _renderStaticSegment<TEnv>(
|
|
616
649
|
handler,
|
|
617
650
|
handlerId,
|
|
618
651
|
mergedRouteMap,
|
|
619
652
|
routeName,
|
|
653
|
+
buildEnv,
|
|
654
|
+
devMode,
|
|
620
655
|
);
|
|
621
656
|
}
|
|
622
657
|
|
|
@@ -641,8 +676,15 @@ export function createRouter<TEnv = any>(
|
|
|
641
676
|
const router: RSCRouterInternal<TEnv, {}> = {
|
|
642
677
|
__brand: RSC_ROUTER_BRAND,
|
|
643
678
|
id: routerId,
|
|
679
|
+
basename,
|
|
680
|
+
|
|
681
|
+
routes(patternsOrBuilder: UrlPatterns<TEnv> | UrlBuilder<TEnv>): any {
|
|
682
|
+
// Wrap builder functions in urls() automatically
|
|
683
|
+
const urlPatterns: UrlPatterns<TEnv> =
|
|
684
|
+
typeof patternsOrBuilder === "function"
|
|
685
|
+
? (urls(patternsOrBuilder) as UrlPatterns<TEnv>)
|
|
686
|
+
: patternsOrBuilder;
|
|
644
687
|
|
|
645
|
-
routes(urlPatterns: UrlPatterns<TEnv>): any {
|
|
646
688
|
// Store reference for runtime manifest generation
|
|
647
689
|
storedUrlPatterns = urlPatterns;
|
|
648
690
|
const currentMountIndex = mountIndex++;
|
|
@@ -673,7 +715,7 @@ export function createRouter<TEnv = any>(
|
|
|
673
715
|
errorBoundary: [],
|
|
674
716
|
notFoundBoundary: [],
|
|
675
717
|
layout: [],
|
|
676
|
-
parallel:
|
|
718
|
+
parallel: {},
|
|
677
719
|
intercept: [],
|
|
678
720
|
loader: [],
|
|
679
721
|
};
|
|
@@ -690,6 +732,10 @@ export function createRouter<TEnv = any>(
|
|
|
690
732
|
counters: {},
|
|
691
733
|
mountIndex: currentMountIndex,
|
|
692
734
|
cacheProfiles: resolvedCacheProfiles,
|
|
735
|
+
// basename sets the initial URL prefix so all path() patterns
|
|
736
|
+
// are registered with the prefix (e.g. "/admin" + "/users" = "/admin/users").
|
|
737
|
+
// No namePrefix — route names stay unprefixed.
|
|
738
|
+
...(basename ? { urlPrefix: basename } : {}),
|
|
693
739
|
},
|
|
694
740
|
() => {
|
|
695
741
|
handlerResult = urlPatterns.handler() as AllUseItems[];
|
|
@@ -709,7 +755,7 @@ export function createRouter<TEnv = any>(
|
|
|
709
755
|
if (entry.type === "route" && entry.isPrerender) {
|
|
710
756
|
if (!prerenderRouteKeys) prerenderRouteKeys = new Set();
|
|
711
757
|
prerenderRouteKeys.add(name);
|
|
712
|
-
if (entry.
|
|
758
|
+
if (entry.isPassthrough === true) {
|
|
713
759
|
if (!passthroughRouteKeys) passthroughRouteKeys = new Set();
|
|
714
760
|
passthroughRouteKeys.add(name);
|
|
715
761
|
}
|
|
@@ -735,6 +781,7 @@ export function createRouter<TEnv = any>(
|
|
|
735
781
|
trailingSlash: trailingSlashConfig,
|
|
736
782
|
handler: urlPatterns.handler,
|
|
737
783
|
mountIndex: currentMountIndex,
|
|
784
|
+
routerId,
|
|
738
785
|
cacheProfiles: resolvedCacheProfiles,
|
|
739
786
|
...(prerenderRouteKeys ? { prerenderRouteKeys } : {}),
|
|
740
787
|
...(passthroughRouteKeys ? { passthroughRouteKeys } : {}),
|
|
@@ -754,6 +801,7 @@ export function createRouter<TEnv = any>(
|
|
|
754
801
|
trailingSlash: trailingSlashConfig,
|
|
755
802
|
handler: urlPatterns.handler,
|
|
756
803
|
mountIndex: currentMountIndex,
|
|
804
|
+
routerId,
|
|
757
805
|
cacheProfiles: resolvedCacheProfiles,
|
|
758
806
|
...(prerenderRouteKeys ? { prerenderRouteKeys } : {}),
|
|
759
807
|
...(passthroughRouteKeys ? { passthroughRouteKeys } : {}),
|
|
@@ -797,6 +845,7 @@ export function createRouter<TEnv = any>(
|
|
|
797
845
|
trailingSlash: trailingSlashConfig,
|
|
798
846
|
handler: urlPatterns.handler,
|
|
799
847
|
mountIndex: mountIndex++,
|
|
848
|
+
routerId,
|
|
800
849
|
// Lazy evaluation fields
|
|
801
850
|
lazy: true,
|
|
802
851
|
lazyPatterns: lazyInclude.patterns,
|
|
@@ -835,8 +884,18 @@ export function createRouter<TEnv = any>(
|
|
|
835
884
|
patternOrMiddleware: string | MiddlewareFn<TEnv>,
|
|
836
885
|
middleware?: MiddlewareFn<TEnv>,
|
|
837
886
|
): any {
|
|
838
|
-
//
|
|
839
|
-
|
|
887
|
+
// Auto-prefix pattern with basename so router-level middleware
|
|
888
|
+
// patterns are router-relative (e.g. "/users/*" matches "/app/users/*").
|
|
889
|
+
if (basename && typeof patternOrMiddleware === "string") {
|
|
890
|
+
const pattern = patternOrMiddleware;
|
|
891
|
+
const prefixed =
|
|
892
|
+
pattern === "/*" || pattern === "*"
|
|
893
|
+
? `${basename}/*`
|
|
894
|
+
: `${basename}${pattern}`;
|
|
895
|
+
addMiddleware(prefixed, middleware, null);
|
|
896
|
+
} else {
|
|
897
|
+
addMiddleware(patternOrMiddleware, middleware, null);
|
|
898
|
+
}
|
|
840
899
|
return router;
|
|
841
900
|
},
|
|
842
901
|
|
|
@@ -869,12 +928,16 @@ export function createRouter<TEnv = any>(
|
|
|
869
928
|
// Expose resolved cache profiles for per-request resolution
|
|
870
929
|
cacheProfiles: resolvedCacheProfiles,
|
|
871
930
|
|
|
872
|
-
// Expose prefetch cache
|
|
931
|
+
// Expose prefetch cache settings
|
|
873
932
|
prefetchCacheControl,
|
|
933
|
+
prefetchCacheTTL,
|
|
874
934
|
|
|
875
935
|
// Expose warmup enabled flag for handler and client
|
|
876
936
|
warmupEnabled,
|
|
877
937
|
|
|
938
|
+
// Expose router-wide performance debugging for request-level metrics setup
|
|
939
|
+
debugPerformance,
|
|
940
|
+
|
|
878
941
|
// Expose debug manifest flag for handler
|
|
879
942
|
allowDebugManifest: allowDebugManifestOption,
|
|
880
943
|
|
|
@@ -933,6 +996,9 @@ export function createRouter<TEnv = any>(
|
|
|
933
996
|
// Expose source file for per-router type generation
|
|
934
997
|
__sourceFile,
|
|
935
998
|
|
|
999
|
+
// Expose basename for runtime manifest generation
|
|
1000
|
+
__basename: basename,
|
|
1001
|
+
|
|
936
1002
|
// RSC request handler (lazily created on first call)
|
|
937
1003
|
fetch: (() => {
|
|
938
1004
|
// Handler is created on first call and reused
|
|
@@ -966,6 +1032,10 @@ export function createRouter<TEnv = any>(
|
|
|
966
1032
|
};
|
|
967
1033
|
})(),
|
|
968
1034
|
|
|
1035
|
+
// Low-level route matching for request classification
|
|
1036
|
+
findMatch: (pathname: string, metricsStore?: any) =>
|
|
1037
|
+
findMatch(pathname, metricsStore),
|
|
1038
|
+
|
|
969
1039
|
// Debug utility for manifest inspection
|
|
970
1040
|
debugManifest: () => buildDebugManifest<TEnv>(routesEntries),
|
|
971
1041
|
};
|
|
@@ -974,7 +1044,9 @@ export function createRouter<TEnv = any>(
|
|
|
974
1044
|
RouterRegistry.set(routerId, router);
|
|
975
1045
|
|
|
976
1046
|
// If urls option was provided, auto-register them
|
|
977
|
-
if (urlsOption) {
|
|
1047
|
+
if (typeof urlsOption === "function") {
|
|
1048
|
+
return router.routes(urlsOption) as RSCRouter<TEnv, {}>;
|
|
1049
|
+
} else if (urlsOption) {
|
|
978
1050
|
return router.routes(urlsOption) as RSCRouter<TEnv, {}>;
|
|
979
1051
|
}
|
|
980
1052
|
|