@rangojs/router 0.0.0-experimental.13 → 0.0.0-experimental.13221847
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 +9 -0
- package/README.md +884 -4
- package/dist/bin/rango.js +1531 -212
- package/dist/vite/index.js +3995 -2489
- package/package.json +57 -52
- package/skills/breadcrumbs/SKILL.md +250 -0
- package/skills/cache-guide/SKILL.md +262 -0
- package/skills/caching/SKILL.md +85 -23
- package/skills/composability/SKILL.md +172 -0
- package/skills/debug-manifest/SKILL.md +12 -8
- package/skills/document-cache/SKILL.md +18 -16
- package/skills/fonts/SKILL.md +6 -4
- package/skills/hooks/SKILL.md +328 -70
- package/skills/host-router/SKILL.md +218 -0
- package/skills/intercept/SKILL.md +131 -8
- package/skills/layout/SKILL.md +100 -3
- package/skills/links/SKILL.md +62 -15
- package/skills/loader/SKILL.md +368 -42
- package/skills/middleware/SKILL.md +171 -34
- package/skills/mime-routes/SKILL.md +14 -10
- package/skills/parallel/SKILL.md +137 -1
- package/skills/prerender/SKILL.md +366 -28
- package/skills/rango/SKILL.md +85 -21
- package/skills/response-routes/SKILL.md +136 -83
- package/skills/route/SKILL.md +195 -21
- package/skills/router-setup/SKILL.md +123 -30
- package/skills/theme/SKILL.md +9 -8
- package/skills/typesafety/SKILL.md +240 -102
- package/skills/use-cache/SKILL.md +324 -0
- package/src/__internal.ts +102 -4
- package/src/bin/rango.ts +312 -15
- package/src/browser/action-coordinator.ts +97 -0
- package/src/browser/action-response-classifier.ts +99 -0
- package/src/browser/event-controller.ts +92 -64
- package/src/browser/history-state.ts +80 -0
- package/src/browser/intercept-utils.ts +52 -0
- package/src/browser/link-interceptor.ts +24 -4
- package/src/browser/logging.ts +11 -0
- package/src/browser/merge-segment-loaders.ts +20 -12
- package/src/browser/navigation-bridge.ts +266 -558
- package/src/browser/navigation-client.ts +132 -75
- package/src/browser/navigation-store.ts +33 -50
- package/src/browser/navigation-transaction.ts +297 -0
- package/src/browser/network-error-handler.ts +61 -0
- package/src/browser/partial-update.ts +303 -309
- package/src/browser/prefetch/cache.ts +206 -0
- package/src/browser/prefetch/fetch.ts +144 -0
- package/src/browser/prefetch/observer.ts +65 -0
- package/src/browser/prefetch/policy.ts +48 -0
- package/src/browser/prefetch/queue.ts +128 -0
- package/src/browser/rango-state.ts +112 -0
- package/src/browser/react/Link.tsx +190 -70
- package/src/browser/react/NavigationProvider.tsx +78 -11
- package/src/browser/react/context.ts +6 -0
- package/src/browser/react/filter-segment-order.ts +11 -0
- package/src/browser/react/index.ts +12 -12
- package/src/browser/react/location-state-shared.ts +95 -53
- package/src/browser/react/location-state.ts +60 -15
- package/src/browser/react/mount-context.ts +6 -1
- package/src/browser/react/nonce-context.ts +23 -0
- package/src/browser/react/shallow-equal.ts +27 -0
- package/src/browser/react/use-action.ts +29 -51
- package/src/browser/react/use-client-cache.ts +5 -3
- package/src/browser/react/use-handle.ts +29 -70
- package/src/browser/react/use-link-status.ts +6 -5
- package/src/browser/react/use-navigation.ts +22 -63
- package/src/browser/react/use-params.ts +65 -0
- package/src/browser/react/use-pathname.ts +47 -0
- package/src/browser/react/use-router.ts +63 -0
- package/src/browser/react/use-search-params.ts +56 -0
- package/src/browser/react/use-segments.ts +80 -97
- package/src/browser/response-adapter.ts +73 -0
- package/src/browser/rsc-router.tsx +188 -57
- package/src/browser/scroll-restoration.ts +117 -44
- package/src/browser/segment-reconciler.ts +221 -0
- package/src/browser/segment-structure-assert.ts +16 -0
- package/src/browser/server-action-bridge.ts +488 -606
- package/src/browser/shallow.ts +6 -1
- package/src/browser/types.ts +116 -47
- package/src/browser/validate-redirect-origin.ts +29 -0
- package/src/build/generate-manifest.ts +63 -21
- package/src/build/generate-route-types.ts +36 -1038
- package/src/build/index.ts +2 -5
- package/src/build/route-trie.ts +38 -12
- package/src/build/route-types/ast-helpers.ts +25 -0
- package/src/build/route-types/ast-route-extraction.ts +98 -0
- package/src/build/route-types/codegen.ts +102 -0
- package/src/build/route-types/include-resolution.ts +411 -0
- package/src/build/route-types/param-extraction.ts +48 -0
- package/src/build/route-types/per-module-writer.ts +128 -0
- package/src/build/route-types/router-processing.ts +479 -0
- package/src/build/route-types/scan-filter.ts +78 -0
- package/src/build/runtime-discovery.ts +231 -0
- package/src/cache/background-task.ts +34 -0
- package/src/cache/cache-key-utils.ts +44 -0
- package/src/cache/cache-policy.ts +125 -0
- package/src/cache/cache-runtime.ts +342 -0
- package/src/cache/cache-scope.ts +122 -303
- package/src/cache/cf/cf-cache-store.ts +571 -17
- package/src/cache/cf/index.ts +13 -3
- package/src/cache/document-cache.ts +116 -77
- package/src/cache/handle-capture.ts +81 -0
- package/src/cache/handle-snapshot.ts +41 -0
- package/src/cache/index.ts +1 -15
- package/src/cache/memory-segment-store.ts +191 -13
- package/src/cache/profile-registry.ts +73 -0
- package/src/cache/read-through-swr.ts +134 -0
- package/src/cache/segment-codec.ts +256 -0
- package/src/cache/taint.ts +98 -0
- package/src/cache/types.ts +72 -122
- package/src/client.rsc.tsx +3 -1
- package/src/client.tsx +84 -126
- package/src/component-utils.ts +4 -4
- package/src/components/DefaultDocument.tsx +5 -1
- package/src/context-var.ts +86 -0
- package/src/debug.ts +19 -9
- package/src/errors.ts +77 -7
- package/src/handle.ts +12 -7
- package/src/handles/MetaTags.tsx +73 -20
- package/src/handles/breadcrumbs.ts +66 -0
- package/src/handles/index.ts +1 -0
- package/src/handles/meta.ts +30 -13
- package/src/host/cookie-handler.ts +21 -15
- package/src/host/errors.ts +8 -8
- package/src/host/index.ts +4 -7
- package/src/host/pattern-matcher.ts +27 -27
- package/src/host/router.ts +61 -39
- package/src/host/testing.ts +8 -8
- package/src/host/types.ts +15 -7
- package/src/host/utils.ts +1 -1
- package/src/href-client.ts +65 -45
- package/src/index.rsc.ts +104 -40
- package/src/index.ts +122 -67
- package/src/internal-debug.ts +9 -3
- package/src/loader.rsc.ts +18 -93
- package/src/loader.ts +26 -9
- package/src/network-error-thrower.tsx +3 -1
- package/src/outlet-provider.tsx +45 -0
- package/src/prerender/param-hash.ts +4 -2
- package/src/prerender/store.ts +121 -17
- package/src/prerender.ts +325 -20
- package/src/reverse.ts +144 -124
- package/src/root-error-boundary.tsx +41 -29
- package/src/route-content-wrapper.tsx +7 -4
- package/src/route-definition/dsl-helpers.ts +959 -0
- package/src/route-definition/helper-factories.ts +200 -0
- package/src/route-definition/helpers-types.ts +430 -0
- package/src/route-definition/index.ts +52 -0
- package/src/route-definition/redirect.ts +93 -0
- package/src/route-definition.ts +1 -1450
- package/src/route-map-builder.ts +87 -133
- package/src/route-name.ts +53 -0
- package/src/route-types.ts +41 -6
- package/src/router/content-negotiation.ts +116 -0
- package/src/router/debug-manifest.ts +72 -0
- package/src/router/error-handling.ts +9 -9
- package/src/router/find-match.ts +160 -0
- package/src/router/handler-context.ts +324 -116
- package/src/router/intercept-resolution.ts +11 -4
- package/src/router/lazy-includes.ts +237 -0
- package/src/router/loader-resolution.ts +179 -133
- package/src/router/logging.ts +112 -6
- package/src/router/manifest.ts +58 -19
- package/src/router/match-api.ts +89 -88
- package/src/router/match-context.ts +4 -2
- package/src/router/match-handlers.ts +440 -0
- package/src/router/match-middleware/background-revalidation.ts +86 -89
- package/src/router/match-middleware/cache-lookup.ts +295 -49
- package/src/router/match-middleware/cache-store.ts +56 -13
- package/src/router/match-middleware/intercept-resolution.ts +45 -22
- package/src/router/match-middleware/segment-resolution.ts +20 -9
- package/src/router/match-pipelines.ts +10 -45
- package/src/router/match-result.ts +44 -21
- package/src/router/metrics.ts +240 -15
- package/src/router/middleware-cookies.ts +55 -0
- package/src/router/middleware-types.ts +222 -0
- package/src/router/middleware.ts +327 -369
- package/src/router/pattern-matching.ts +169 -31
- package/src/router/prerender-match.ts +402 -0
- package/src/router/preview-match.ts +170 -0
- package/src/router/revalidation.ts +105 -14
- package/src/router/router-context.ts +40 -21
- package/src/router/router-interfaces.ts +452 -0
- package/src/router/router-options.ts +592 -0
- package/src/router/router-registry.ts +24 -0
- package/src/router/segment-resolution/fresh.ts +677 -0
- package/src/router/segment-resolution/helpers.ts +263 -0
- package/src/router/segment-resolution/loader-cache.ts +199 -0
- package/src/router/segment-resolution/revalidation.ts +1296 -0
- package/src/router/segment-resolution/static-store.ts +67 -0
- package/src/router/segment-resolution.ts +21 -1354
- package/src/router/segment-wrappers.ts +291 -0
- package/src/router/telemetry-otel.ts +299 -0
- package/src/router/telemetry.ts +300 -0
- package/src/router/timeout.ts +148 -0
- package/src/router/trie-matching.ts +96 -29
- package/src/router/types.ts +15 -9
- package/src/router.ts +642 -2366
- package/src/rsc/handler-context.ts +45 -0
- package/src/rsc/handler.ts +639 -1027
- package/src/rsc/helpers.ts +140 -6
- package/src/rsc/index.ts +0 -20
- package/src/rsc/loader-fetch.ts +209 -0
- package/src/rsc/manifest-init.ts +86 -0
- package/src/rsc/nonce.ts +14 -0
- package/src/rsc/origin-guard.ts +141 -0
- package/src/rsc/progressive-enhancement.ts +379 -0
- package/src/rsc/response-error.ts +37 -0
- package/src/rsc/response-route-handler.ts +347 -0
- package/src/rsc/rsc-rendering.ts +237 -0
- package/src/rsc/runtime-warnings.ts +42 -0
- package/src/rsc/server-action.ts +348 -0
- package/src/rsc/ssr-setup.ts +128 -0
- package/src/rsc/types.ts +38 -11
- package/src/search-params.ts +66 -54
- package/src/segment-system.tsx +165 -17
- package/src/server/context.ts +237 -54
- package/src/server/cookie-store.ts +190 -0
- package/src/server/fetchable-loader-store.ts +11 -6
- package/src/server/handle-store.ts +94 -15
- package/src/server/loader-registry.ts +15 -56
- package/src/server/request-context.ts +438 -71
- package/src/server.ts +26 -164
- package/src/ssr/index.tsx +101 -31
- package/src/static-handler.ts +22 -4
- package/src/theme/ThemeProvider.tsx +21 -15
- package/src/theme/ThemeScript.tsx +5 -5
- package/src/theme/constants.ts +5 -2
- package/src/theme/index.ts +4 -14
- package/src/theme/theme-context.ts +4 -30
- package/src/theme/theme-script.ts +21 -18
- package/src/types/boundaries.ts +158 -0
- package/src/types/cache-types.ts +198 -0
- package/src/types/error-types.ts +192 -0
- package/src/types/global-namespace.ts +100 -0
- package/src/types/handler-context.ts +773 -0
- package/src/types/index.ts +88 -0
- package/src/types/loader-types.ts +183 -0
- package/src/types/route-config.ts +170 -0
- package/src/types/route-entry.ts +109 -0
- package/src/types/segments.ts +150 -0
- package/src/types.ts +1 -1795
- package/src/urls/include-helper.ts +197 -0
- package/src/urls/index.ts +53 -0
- package/src/urls/path-helper-types.ts +339 -0
- package/src/urls/path-helper.ts +329 -0
- package/src/urls/pattern-types.ts +95 -0
- package/src/urls/response-types.ts +106 -0
- package/src/urls/type-extraction.ts +372 -0
- package/src/urls/urls-function.ts +98 -0
- package/src/urls.ts +1 -1323
- package/src/use-loader.tsx +85 -77
- package/src/vite/discovery/bundle-postprocess.ts +184 -0
- package/src/vite/discovery/discover-routers.ts +344 -0
- package/src/vite/discovery/prerender-collection.ts +385 -0
- package/src/vite/discovery/route-types-writer.ts +258 -0
- package/src/vite/discovery/self-gen-tracking.ts +47 -0
- package/src/vite/discovery/state.ts +108 -0
- package/src/vite/discovery/virtual-module-codegen.ts +203 -0
- package/src/vite/index.ts +11 -2259
- package/src/vite/plugin-types.ts +48 -0
- package/src/vite/plugins/cjs-to-esm.ts +93 -0
- package/src/vite/plugins/client-ref-dedup.ts +115 -0
- package/src/vite/plugins/client-ref-hashing.ts +105 -0
- package/src/vite/{expose-action-id.ts → plugins/expose-action-id.ts} +72 -47
- package/src/vite/{expose-id-utils.ts → plugins/expose-id-utils.ts} +8 -43
- package/src/vite/plugins/expose-ids/export-analysis.ts +296 -0
- package/src/vite/plugins/expose-ids/handler-transform.ts +179 -0
- package/src/vite/plugins/expose-ids/loader-transform.ts +74 -0
- package/src/vite/plugins/expose-ids/router-transform.ts +110 -0
- package/src/vite/plugins/expose-ids/types.ts +45 -0
- package/src/vite/plugins/expose-internal-ids.ts +569 -0
- package/src/vite/plugins/refresh-cmd.ts +65 -0
- package/src/vite/plugins/use-cache-transform.ts +323 -0
- package/src/vite/plugins/version-injector.ts +83 -0
- package/src/vite/plugins/version-plugin.ts +266 -0
- package/src/vite/{virtual-entries.ts → plugins/virtual-entries.ts} +23 -14
- package/src/vite/plugins/virtual-stub-plugin.ts +29 -0
- package/src/vite/rango.ts +445 -0
- package/src/vite/router-discovery.ts +777 -0
- package/src/vite/{ast-handler-extract.ts → utils/ast-handler-extract.ts} +181 -9
- package/src/vite/utils/banner.ts +36 -0
- package/src/vite/utils/bundle-analysis.ts +137 -0
- package/src/vite/utils/manifest-utils.ts +70 -0
- package/src/vite/{package-resolution.ts → utils/package-resolution.ts} +25 -29
- package/src/vite/utils/prerender-utils.ts +189 -0
- package/src/vite/utils/shared-utils.ts +169 -0
- package/CLAUDE.md +0 -43
- package/dist/vite/index.named-routes.gen.ts +0 -103
- package/src/browser/lru-cache.ts +0 -69
- package/src/browser/request-controller.ts +0 -164
- package/src/cache/memory-store.ts +0 -253
- package/src/href-context.ts +0 -33
- package/src/router.gen.ts +0 -6
- package/src/static-handler.gen.ts +0 -5
- package/src/urls.gen.ts +0 -8
- package/src/vite/expose-internal-ids.ts +0 -1167
- /package/src/vite/{version.d.ts → plugins/version.d.ts} +0 -0
package/src/router/logging.ts
CHANGED
|
@@ -1,10 +1,48 @@
|
|
|
1
1
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
2
|
import { INTERNAL_RANGO_DEBUG } from "../internal-debug.js";
|
|
3
3
|
|
|
4
|
+
// -- Revalidation trace types --
|
|
5
|
+
|
|
6
|
+
export interface RevalidationTraceEntry {
|
|
7
|
+
segmentId: string;
|
|
8
|
+
segmentType: string;
|
|
9
|
+
belongsToRoute: boolean;
|
|
10
|
+
source:
|
|
11
|
+
| "segment-resolution"
|
|
12
|
+
| "cache-hit"
|
|
13
|
+
| "loader"
|
|
14
|
+
| "parallel"
|
|
15
|
+
| "orphan-layout"
|
|
16
|
+
| "route-handler"
|
|
17
|
+
| "layout-handler"
|
|
18
|
+
| "intercept-loader";
|
|
19
|
+
defaultShouldRevalidate: boolean;
|
|
20
|
+
finalShouldRevalidate: boolean;
|
|
21
|
+
reason: string;
|
|
22
|
+
customRevalidators?: number;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface RevalidationTraceMeta {
|
|
26
|
+
method: string;
|
|
27
|
+
prevUrl: string;
|
|
28
|
+
nextUrl: string;
|
|
29
|
+
routeKey: string;
|
|
30
|
+
isAction: boolean;
|
|
31
|
+
stale?: boolean;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface RevalidationTrace {
|
|
35
|
+
meta: RevalidationTraceMeta;
|
|
36
|
+
entries: RevalidationTraceEntry[];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// -- Log context --
|
|
40
|
+
|
|
4
41
|
interface RouterLogContext {
|
|
5
42
|
requestId: string;
|
|
6
43
|
transactionId: string;
|
|
7
44
|
depth: number;
|
|
45
|
+
revalidationTrace?: RevalidationTrace;
|
|
8
46
|
}
|
|
9
47
|
|
|
10
48
|
interface RouterLogOptions {
|
|
@@ -36,7 +74,7 @@ function getHeaderRequestId(request: Request): string | null {
|
|
|
36
74
|
return trimmed.length > 0 ? trimmed : null;
|
|
37
75
|
}
|
|
38
76
|
|
|
39
|
-
function getOrCreateRequestId(request: Request): string {
|
|
77
|
+
export function getOrCreateRequestId(request: Request): string {
|
|
40
78
|
const existing = requestIds.get(request);
|
|
41
79
|
if (existing) return existing;
|
|
42
80
|
|
|
@@ -74,9 +112,15 @@ export function runWithRouterLogContext<T>(
|
|
|
74
112
|
);
|
|
75
113
|
}
|
|
76
114
|
|
|
77
|
-
export function withRouterLogScope<T>(
|
|
115
|
+
export function withRouterLogScope<T>(
|
|
116
|
+
label: string,
|
|
117
|
+
fn: () => Promise<T>,
|
|
118
|
+
): Promise<T>;
|
|
78
119
|
export function withRouterLogScope<T>(label: string, fn: () => T): T;
|
|
79
|
-
export function withRouterLogScope<T>(
|
|
120
|
+
export function withRouterLogScope<T>(
|
|
121
|
+
label: string,
|
|
122
|
+
fn: () => Promise<T> | T,
|
|
123
|
+
): Promise<T> | T {
|
|
80
124
|
const ctx = routerLogContext.getStore();
|
|
81
125
|
if (!INTERNAL_RANGO_DEBUG || !ctx) {
|
|
82
126
|
return fn();
|
|
@@ -88,9 +132,16 @@ export function withRouterLogScope<T>(label: string, fn: () => Promise<T> | T):
|
|
|
88
132
|
try {
|
|
89
133
|
const result = fn();
|
|
90
134
|
if (result && typeof (result as Promise<T>).then === "function") {
|
|
91
|
-
return (result as Promise<T>).
|
|
92
|
-
|
|
93
|
-
|
|
135
|
+
return (result as Promise<T>).then(
|
|
136
|
+
(value) => {
|
|
137
|
+
debugLog(label, "end");
|
|
138
|
+
return value;
|
|
139
|
+
},
|
|
140
|
+
(error) => {
|
|
141
|
+
debugLog(label, "error", { error: String(error) });
|
|
142
|
+
throw error;
|
|
143
|
+
},
|
|
144
|
+
);
|
|
94
145
|
}
|
|
95
146
|
debugLog(label, "end");
|
|
96
147
|
return result;
|
|
@@ -143,3 +194,58 @@ export function debugWarn(
|
|
|
143
194
|
|
|
144
195
|
console.warn(`${prefix} ${message}`);
|
|
145
196
|
}
|
|
197
|
+
|
|
198
|
+
// -- Revalidation trace helpers --
|
|
199
|
+
|
|
200
|
+
export function isTraceActive(): boolean {
|
|
201
|
+
if (!INTERNAL_RANGO_DEBUG) return false;
|
|
202
|
+
const ctx = routerLogContext.getStore();
|
|
203
|
+
return !!ctx?.revalidationTrace;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
export function startRevalidationTrace(meta: RevalidationTraceMeta): void {
|
|
207
|
+
const ctx = routerLogContext.getStore();
|
|
208
|
+
if (!ctx || !INTERNAL_RANGO_DEBUG) return;
|
|
209
|
+
ctx.revalidationTrace = { meta, entries: [] };
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
export function pushRevalidationTraceEntry(
|
|
213
|
+
entry: RevalidationTraceEntry,
|
|
214
|
+
): void {
|
|
215
|
+
const ctx = routerLogContext.getStore();
|
|
216
|
+
if (!ctx?.revalidationTrace) return;
|
|
217
|
+
ctx.revalidationTrace.entries.push(entry);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
export function flushRevalidationTrace(): RevalidationTrace | null {
|
|
221
|
+
const ctx = routerLogContext.getStore();
|
|
222
|
+
if (!ctx?.revalidationTrace) return null;
|
|
223
|
+
const trace = ctx.revalidationTrace;
|
|
224
|
+
ctx.revalidationTrace = undefined;
|
|
225
|
+
|
|
226
|
+
if (trace.entries.length === 0) return trace;
|
|
227
|
+
|
|
228
|
+
const revalidated = trace.entries.filter((e) => e.finalShouldRevalidate);
|
|
229
|
+
const skipped = trace.entries.filter((e) => !e.finalShouldRevalidate);
|
|
230
|
+
|
|
231
|
+
debugLog("revalidation-trace", "flush", {
|
|
232
|
+
method: trace.meta.method,
|
|
233
|
+
routeKey: trace.meta.routeKey,
|
|
234
|
+
isAction: trace.meta.isAction,
|
|
235
|
+
stale: trace.meta.stale,
|
|
236
|
+
prevUrl: trace.meta.prevUrl,
|
|
237
|
+
nextUrl: trace.meta.nextUrl,
|
|
238
|
+
total: trace.entries.length,
|
|
239
|
+
revalidated: revalidated.length,
|
|
240
|
+
skipped: skipped.length,
|
|
241
|
+
entries: trace.entries.map((e) => ({
|
|
242
|
+
segmentId: e.segmentId,
|
|
243
|
+
type: e.segmentType,
|
|
244
|
+
source: e.source,
|
|
245
|
+
revalidate: e.finalShouldRevalidate,
|
|
246
|
+
reason: e.reason,
|
|
247
|
+
})),
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
return trace;
|
|
251
|
+
}
|
package/src/router/manifest.ts
CHANGED
|
@@ -6,7 +6,13 @@
|
|
|
6
6
|
|
|
7
7
|
import { invariant, RouteNotFoundError } from "../errors";
|
|
8
8
|
import { createRouteHelpers } from "../route-definition";
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
getContext,
|
|
11
|
+
runWithPrefixes,
|
|
12
|
+
getIsolatedLazyParent,
|
|
13
|
+
type EntryData,
|
|
14
|
+
type MetricsStore,
|
|
15
|
+
} from "../server/context";
|
|
10
16
|
import MapRootLayout from "../server/root-layout";
|
|
11
17
|
import type { RouteEntry } from "../types";
|
|
12
18
|
import type { UrlPatterns } from "../urls";
|
|
@@ -60,7 +66,9 @@ export async function loadManifest(
|
|
|
60
66
|
const mountIndex = entry.mountIndex;
|
|
61
67
|
|
|
62
68
|
// Check module-level cache (persists across requests within same isolate)
|
|
63
|
-
|
|
69
|
+
// Include routerId so multi-router setups (host routing) don't share cached
|
|
70
|
+
// EntryData across routers with overlapping mountIndex + routeKey combinations.
|
|
71
|
+
const cacheKey = `${VERSION}:${entry.routerId ?? ""}:${mountIndex ?? ""}:${routeKey}:${isSSR ? 1 : 0}`;
|
|
64
72
|
const cached = manifestModuleCache.get(cacheKey);
|
|
65
73
|
if (cached) {
|
|
66
74
|
const cacheStart = performance.now();
|
|
@@ -100,14 +108,18 @@ export async function loadManifest(
|
|
|
100
108
|
|
|
101
109
|
try {
|
|
102
110
|
// Include mountIndex in namespace to ensure unique cache keys per mount
|
|
103
|
-
const namespaceWithMount =
|
|
104
|
-
? `#router.M${mountIndex}`
|
|
105
|
-
: "#router";
|
|
111
|
+
const namespaceWithMount =
|
|
112
|
+
mountIndex !== undefined ? `#router.M${mountIndex}` : "#router";
|
|
106
113
|
|
|
107
114
|
// For lazy entries, use the captured parent from include() context
|
|
108
115
|
// This ensures routes are registered under the correct layout hierarchy
|
|
109
|
-
const lazyContext =
|
|
110
|
-
|
|
116
|
+
const lazyContext =
|
|
117
|
+
entry.lazy && entry.lazyPatterns ? entry.lazyContext : null;
|
|
118
|
+
const parentForContext = lazyContext
|
|
119
|
+
? getIsolatedLazyParent(
|
|
120
|
+
(lazyContext.parent as EntryData | null) ?? Store.parent,
|
|
121
|
+
)
|
|
122
|
+
: Store.parent;
|
|
111
123
|
|
|
112
124
|
// For lazy entries, merge captured counters from include() so the
|
|
113
125
|
// handler's entries get shortCode indices after sibling entries that
|
|
@@ -121,6 +133,22 @@ export async function loadManifest(
|
|
|
121
133
|
}
|
|
122
134
|
}
|
|
123
135
|
|
|
136
|
+
// Propagate cache profiles for DSL-time cache("profileName") resolution.
|
|
137
|
+
// Non-lazy entries carry profiles directly; lazy entries carry them
|
|
138
|
+
// in the captured lazyContext from include() time.
|
|
139
|
+
const entryProfiles =
|
|
140
|
+
entry.cacheProfiles ?? (lazyContext as any)?.cacheProfiles;
|
|
141
|
+
if (entryProfiles) {
|
|
142
|
+
Store.cacheProfiles = entryProfiles;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Propagate rootScoped from lazyContext so that routes inside
|
|
146
|
+
// nested { name: "sub" } under { name: "" } keep inherited root scope
|
|
147
|
+
// when the manifest is rebuilt on each request.
|
|
148
|
+
if (lazyContext && (lazyContext as any).rootScoped !== undefined) {
|
|
149
|
+
Store.rootScoped = (lazyContext as any).rootScoped;
|
|
150
|
+
}
|
|
151
|
+
|
|
124
152
|
const handlerExecStart = performance.now();
|
|
125
153
|
const useItems = await getContext().runWithStore(
|
|
126
154
|
Store,
|
|
@@ -142,10 +170,8 @@ export async function loadManifest(
|
|
|
142
170
|
const fullPrefix = (lazyContext?.urlPrefix || "") + includePrefix;
|
|
143
171
|
|
|
144
172
|
if (fullPrefix || lazyContext?.namePrefix) {
|
|
145
|
-
return runWithPrefixes(
|
|
146
|
-
|
|
147
|
-
lazyContext?.namePrefix,
|
|
148
|
-
() => lazyPatterns.handler()
|
|
173
|
+
return runWithPrefixes(fullPrefix, lazyContext?.namePrefix, () =>
|
|
174
|
+
lazyPatterns.handler(),
|
|
149
175
|
);
|
|
150
176
|
}
|
|
151
177
|
return lazyPatterns.handler();
|
|
@@ -174,27 +200,40 @@ export async function loadManifest(
|
|
|
174
200
|
"default" in load
|
|
175
201
|
) {
|
|
176
202
|
// Promise<{ default: () => Array }> - e.g., dynamic import
|
|
177
|
-
|
|
203
|
+
if (typeof load.default !== "function") {
|
|
204
|
+
throw new Error(
|
|
205
|
+
`[@rangojs/router] Unsupported async handler: { default } must be a function, ` +
|
|
206
|
+
`got ${typeof load.default}. Use () => import('./urls') for lazy loading.`,
|
|
207
|
+
);
|
|
208
|
+
}
|
|
178
209
|
return (load.default as (h?: any) => any)(helpers);
|
|
179
210
|
}
|
|
180
211
|
if (typeof load === "function") {
|
|
181
212
|
// Promise<() => Array>
|
|
182
213
|
return (load as (h?: any) => any)(helpers);
|
|
183
214
|
}
|
|
184
|
-
//
|
|
185
|
-
|
|
215
|
+
// Reject unsupported async handler results. Supported shapes are:
|
|
216
|
+
// Promise<{ default: fn }> — dynamic import
|
|
217
|
+
// Promise<fn> — lazy function
|
|
218
|
+
// Direct Promise<Array> is not supported; use a function wrapper.
|
|
219
|
+
throw new Error(
|
|
220
|
+
`[@rangojs/router] Unsupported async handler result (${typeof load}). ` +
|
|
221
|
+
`Lazy route handlers must resolve to a function or { default: fn }, ` +
|
|
222
|
+
`not a direct array. Wrap your handler: () => import('./urls') or ` +
|
|
223
|
+
`() => Promise.resolve((h) => [...])`,
|
|
224
|
+
);
|
|
186
225
|
}
|
|
187
226
|
|
|
188
227
|
// Inline handler - routes were registered with correct parent inside layout
|
|
189
228
|
return [wrappedItems].flat(3);
|
|
190
|
-
}
|
|
229
|
+
},
|
|
191
230
|
);
|
|
192
231
|
pushMetric?.("manifest:handler-exec", handlerExecStart);
|
|
193
232
|
|
|
194
233
|
const validationStart = performance.now();
|
|
195
234
|
invariant(
|
|
196
235
|
useItems && useItems.length > 0,
|
|
197
|
-
"Did not receive any handler from router.map()"
|
|
236
|
+
"Did not receive any handler from router.map()",
|
|
198
237
|
);
|
|
199
238
|
// For non-lazy entries the root handler is wrapped in MapRootLayout,
|
|
200
239
|
// so the result always contains a layout item. Lazy entries run the
|
|
@@ -203,13 +242,13 @@ export async function loadManifest(
|
|
|
203
242
|
if (!lazyContext) {
|
|
204
243
|
invariant(
|
|
205
244
|
useItems.some((item: { type: string }) => item.type === "layout"),
|
|
206
|
-
"Top-level handler must be a layout"
|
|
245
|
+
"Top-level handler must be a layout",
|
|
207
246
|
);
|
|
208
247
|
}
|
|
209
248
|
|
|
210
249
|
invariant(
|
|
211
250
|
Store.manifest.has(routeKey),
|
|
212
|
-
`Route must be registered for ${routeKey}
|
|
251
|
+
`Route must be registered for ${routeKey}`,
|
|
213
252
|
);
|
|
214
253
|
pushMetric?.("manifest:validation", validationStart);
|
|
215
254
|
|
|
@@ -228,7 +267,7 @@ export async function loadManifest(
|
|
|
228
267
|
routeKey,
|
|
229
268
|
},
|
|
230
269
|
},
|
|
231
|
-
}
|
|
270
|
+
},
|
|
232
271
|
);
|
|
233
272
|
}
|
|
234
273
|
}
|
package/src/router/match-api.ts
CHANGED
|
@@ -6,15 +6,16 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { CacheScope, createCacheScope } from "../cache/cache-scope.js";
|
|
9
|
-
import {
|
|
10
|
-
RouteNotFoundError,
|
|
11
|
-
} from "../errors";
|
|
9
|
+
import { RouteNotFoundError } from "../errors";
|
|
12
10
|
import {
|
|
13
11
|
createErrorInfo,
|
|
14
12
|
createErrorSegment,
|
|
15
13
|
findNearestErrorBoundary as findErrorBoundary,
|
|
16
14
|
} from "./error-handling.js";
|
|
17
|
-
import {
|
|
15
|
+
import {
|
|
16
|
+
createHandlerContext,
|
|
17
|
+
stripInternalParams,
|
|
18
|
+
} from "./handler-context.js";
|
|
18
19
|
import { setupLoaderAccess } from "./loader-resolution.js";
|
|
19
20
|
import { loadManifest, clearManifestCache } from "./manifest.js";
|
|
20
21
|
import { collectRouteMiddleware } from "./middleware.js";
|
|
@@ -25,21 +26,16 @@ import {
|
|
|
25
26
|
LoaderEntry,
|
|
26
27
|
getContext,
|
|
27
28
|
InterceptSelectorContext,
|
|
28
|
-
type MetricsStore,
|
|
29
29
|
} from "../server/context";
|
|
30
|
-
import type {
|
|
31
|
-
ErrorBoundaryHandler,
|
|
32
|
-
ErrorInfo,
|
|
33
|
-
HandlerContext,
|
|
34
|
-
MatchResult,
|
|
35
|
-
ResolvedSegment,
|
|
36
|
-
} from "../types";
|
|
30
|
+
import type { ErrorBoundaryHandler, ErrorInfo, MatchResult } from "../types";
|
|
37
31
|
import type { ReactNode } from "react";
|
|
38
|
-
import type { MatchContext
|
|
32
|
+
import type { MatchContext } from "./match-context.js";
|
|
39
33
|
import type { MatchApiDeps, ActionContext } from "./types.js";
|
|
40
|
-
import
|
|
41
|
-
|
|
42
|
-
|
|
34
|
+
import {
|
|
35
|
+
getRequestContext,
|
|
36
|
+
setRequestContextPrevRouteKey,
|
|
37
|
+
} from "../server/request-context.js";
|
|
38
|
+
import { isAutoGeneratedRouteName } from "../route-name.js";
|
|
43
39
|
import { debugLog, debugWarn } from "./logging.js";
|
|
44
40
|
|
|
45
41
|
/**
|
|
@@ -95,22 +91,32 @@ export async function createMatchContextForFull<TEnv>(
|
|
|
95
91
|
});
|
|
96
92
|
}
|
|
97
93
|
|
|
94
|
+
if (
|
|
95
|
+
manifestEntry.type === "route" &&
|
|
96
|
+
manifestEntry.prerenderDef?.options?.passthrough === true
|
|
97
|
+
) {
|
|
98
|
+
matched.pt = true;
|
|
99
|
+
}
|
|
100
|
+
|
|
98
101
|
const routeMiddleware = collectRouteMiddleware(
|
|
99
102
|
traverseBack(manifestEntry),
|
|
100
103
|
matched.params,
|
|
101
104
|
);
|
|
102
105
|
|
|
103
|
-
|
|
106
|
+
// Clean URL without internal _rsc* params for userland access
|
|
107
|
+
const cleanUrl = stripInternalParams(url);
|
|
104
108
|
|
|
105
109
|
const handlerContext = createHandlerContext(
|
|
106
110
|
matched.params,
|
|
107
111
|
request,
|
|
108
|
-
|
|
112
|
+
cleanUrl.searchParams,
|
|
109
113
|
pathname,
|
|
110
|
-
|
|
111
|
-
|
|
114
|
+
cleanUrl,
|
|
115
|
+
env,
|
|
112
116
|
deps.getRouteMap(),
|
|
113
117
|
matched.routeKey,
|
|
118
|
+
matched.responseType,
|
|
119
|
+
matched.pt === true,
|
|
114
120
|
);
|
|
115
121
|
|
|
116
122
|
const loaderPromises = new Map<string, Promise<any>>();
|
|
@@ -138,14 +144,13 @@ export async function createMatchContextForFull<TEnv>(
|
|
|
138
144
|
|
|
139
145
|
return {
|
|
140
146
|
request,
|
|
141
|
-
url,
|
|
147
|
+
url: cleanUrl,
|
|
142
148
|
pathname,
|
|
143
149
|
env,
|
|
144
|
-
bindings,
|
|
145
150
|
clientSegmentIds: [],
|
|
146
151
|
clientSegmentSet: new Set(),
|
|
147
152
|
stale: false,
|
|
148
|
-
prevUrl:
|
|
153
|
+
prevUrl: cleanUrl,
|
|
149
154
|
prevParams: {},
|
|
150
155
|
prevMatch: null,
|
|
151
156
|
matched,
|
|
@@ -162,12 +167,16 @@ export async function createMatchContextForFull<TEnv>(
|
|
|
162
167
|
Store,
|
|
163
168
|
interceptContextMatch: null,
|
|
164
169
|
interceptSelectorContext: {
|
|
165
|
-
from:
|
|
166
|
-
to:
|
|
170
|
+
from: cleanUrl,
|
|
171
|
+
to: cleanUrl,
|
|
167
172
|
params: matched.params,
|
|
168
173
|
request,
|
|
169
174
|
env,
|
|
170
175
|
segments: { path: [], ids: [] },
|
|
176
|
+
toRouteName:
|
|
177
|
+
matched.routeKey && !isAutoGeneratedRouteName(matched.routeKey)
|
|
178
|
+
? matched.routeKey
|
|
179
|
+
: undefined,
|
|
171
180
|
},
|
|
172
181
|
isSameRouteNavigation: false,
|
|
173
182
|
interceptResult: null,
|
|
@@ -193,7 +202,6 @@ export async function createMatchContextForPartial<TEnv>(
|
|
|
193
202
|
const url = new URL(request.url);
|
|
194
203
|
const pathname = url.pathname;
|
|
195
204
|
|
|
196
|
-
const requestStartTime = performance.now();
|
|
197
205
|
const metricsStore = deps.getMetricsStore();
|
|
198
206
|
|
|
199
207
|
const clientSegmentIds =
|
|
@@ -215,10 +223,21 @@ export async function createMatchContextForPartial<TEnv>(
|
|
|
215
223
|
return null;
|
|
216
224
|
}
|
|
217
225
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
226
|
+
let prevUrl: URL;
|
|
227
|
+
try {
|
|
228
|
+
prevUrl = new URL(previousUrl, url.origin);
|
|
229
|
+
} catch {
|
|
230
|
+
return null;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
let interceptContextUrl: URL;
|
|
234
|
+
try {
|
|
235
|
+
interceptContextUrl = interceptSourceUrl
|
|
236
|
+
? new URL(interceptSourceUrl, url.origin)
|
|
237
|
+
: prevUrl;
|
|
238
|
+
} catch {
|
|
239
|
+
interceptContextUrl = prevUrl;
|
|
240
|
+
}
|
|
222
241
|
|
|
223
242
|
const routeMatchStart = metricsStore ? performance.now() : 0;
|
|
224
243
|
const prevMatch = deps.findMatch(prevUrl.pathname);
|
|
@@ -270,21 +289,32 @@ export async function createMatchContextForPartial<TEnv>(
|
|
|
270
289
|
});
|
|
271
290
|
}
|
|
272
291
|
|
|
292
|
+
if (
|
|
293
|
+
manifestEntry.type === "route" &&
|
|
294
|
+
manifestEntry.prerenderDef?.options?.passthrough === true
|
|
295
|
+
) {
|
|
296
|
+
matched.pt = true;
|
|
297
|
+
}
|
|
298
|
+
|
|
273
299
|
const routeMiddleware = collectRouteMiddleware(
|
|
274
300
|
traverseBack(manifestEntry),
|
|
275
301
|
matched.params,
|
|
276
302
|
);
|
|
277
303
|
|
|
278
|
-
|
|
304
|
+
// Clean URL without internal _rsc* params for userland access
|
|
305
|
+
const cleanUrl = stripInternalParams(url);
|
|
306
|
+
|
|
279
307
|
const handlerContext = createHandlerContext(
|
|
280
308
|
matched.params,
|
|
281
309
|
request,
|
|
282
|
-
|
|
310
|
+
cleanUrl.searchParams,
|
|
283
311
|
pathname,
|
|
284
|
-
|
|
285
|
-
|
|
312
|
+
cleanUrl,
|
|
313
|
+
env,
|
|
286
314
|
deps.getRouteMap(),
|
|
287
315
|
matched.routeKey,
|
|
316
|
+
matched.responseType,
|
|
317
|
+
matched.pt === true,
|
|
288
318
|
);
|
|
289
319
|
|
|
290
320
|
const clientSegmentSet = new Set(clientSegmentIds);
|
|
@@ -308,8 +338,7 @@ export async function createMatchContextForPartial<TEnv>(
|
|
|
308
338
|
}
|
|
309
339
|
|
|
310
340
|
const isSameRouteNavigation = !!(
|
|
311
|
-
interceptContextMatch &&
|
|
312
|
-
interceptContextMatch.routeKey === matched.routeKey
|
|
341
|
+
interceptContextMatch && interceptContextMatch.routeKey === matched.routeKey
|
|
313
342
|
);
|
|
314
343
|
|
|
315
344
|
if (interceptSourceUrl) {
|
|
@@ -331,16 +360,35 @@ export async function createMatchContextForPartial<TEnv>(
|
|
|
331
360
|
if (/D\d+\./.test(id)) return false;
|
|
332
361
|
return true;
|
|
333
362
|
});
|
|
363
|
+
const effectiveFromUrl = interceptSourceUrl ? interceptContextUrl : prevUrl;
|
|
364
|
+
const effectiveFromMatch = interceptSourceUrl
|
|
365
|
+
? interceptContextMatch
|
|
366
|
+
: prevMatch;
|
|
367
|
+
|
|
368
|
+
// Store previous route key on the request context for revalidation
|
|
369
|
+
// fromRouteName. Uses effectiveFromMatch so intercept-source navigations
|
|
370
|
+
// see the intercept origin route, not the plain previous URL route.
|
|
371
|
+
setRequestContextPrevRouteKey(effectiveFromMatch?.routeKey);
|
|
372
|
+
|
|
334
373
|
const interceptSelectorContext: InterceptSelectorContext = {
|
|
335
|
-
from:
|
|
336
|
-
to:
|
|
374
|
+
from: effectiveFromUrl,
|
|
375
|
+
to: cleanUrl,
|
|
337
376
|
params: matched.params,
|
|
338
377
|
request,
|
|
339
378
|
env,
|
|
340
379
|
segments: {
|
|
341
|
-
path:
|
|
380
|
+
path: effectiveFromUrl.pathname.split("/").filter(Boolean),
|
|
342
381
|
ids: filteredSegmentIds,
|
|
343
382
|
},
|
|
383
|
+
fromRouteName:
|
|
384
|
+
effectiveFromMatch?.routeKey &&
|
|
385
|
+
!isAutoGeneratedRouteName(effectiveFromMatch.routeKey)
|
|
386
|
+
? effectiveFromMatch.routeKey
|
|
387
|
+
: undefined,
|
|
388
|
+
toRouteName:
|
|
389
|
+
matched.routeKey && !isAutoGeneratedRouteName(matched.routeKey)
|
|
390
|
+
? matched.routeKey
|
|
391
|
+
: undefined,
|
|
344
392
|
};
|
|
345
393
|
const isAction = !!actionContext;
|
|
346
394
|
|
|
@@ -389,10 +437,9 @@ export async function createMatchContextForPartial<TEnv>(
|
|
|
389
437
|
|
|
390
438
|
return {
|
|
391
439
|
request,
|
|
392
|
-
url,
|
|
440
|
+
url: cleanUrl,
|
|
393
441
|
pathname,
|
|
394
442
|
env,
|
|
395
|
-
bindings,
|
|
396
443
|
clientSegmentIds,
|
|
397
444
|
clientSegmentSet,
|
|
398
445
|
stale,
|
|
@@ -544,10 +591,7 @@ export async function matchError<TEnv>(
|
|
|
544
591
|
|
|
545
592
|
const reqCtx = getRequestContext();
|
|
546
593
|
if (reqCtx) {
|
|
547
|
-
reqCtx.
|
|
548
|
-
status: 500,
|
|
549
|
-
headers: reqCtx.res.headers,
|
|
550
|
-
});
|
|
594
|
+
reqCtx._setStatus(500);
|
|
551
595
|
}
|
|
552
596
|
|
|
553
597
|
const effectiveFallback = fallback || DefaultErrorFallback;
|
|
@@ -574,46 +618,3 @@ export async function matchError<TEnv>(
|
|
|
574
618
|
params: matched.params,
|
|
575
619
|
};
|
|
576
620
|
}
|
|
577
|
-
|
|
578
|
-
/**
|
|
579
|
-
* Preview match - returns route middleware without segment resolution.
|
|
580
|
-
*/
|
|
581
|
-
export async function previewMatch<TEnv>(
|
|
582
|
-
request: Request,
|
|
583
|
-
context: TEnv,
|
|
584
|
-
deps: MatchApiDeps<TEnv>,
|
|
585
|
-
): Promise<{
|
|
586
|
-
routeMiddleware?: Array<{
|
|
587
|
-
handler: import("./middleware.js").MiddlewareFn;
|
|
588
|
-
params: Record<string, string>;
|
|
589
|
-
}>;
|
|
590
|
-
} | null> {
|
|
591
|
-
const url = new URL(request.url);
|
|
592
|
-
const pathname = url.pathname;
|
|
593
|
-
|
|
594
|
-
const matched = deps.findMatch(pathname);
|
|
595
|
-
if (!matched) {
|
|
596
|
-
return null;
|
|
597
|
-
}
|
|
598
|
-
|
|
599
|
-
if (matched.redirectTo) {
|
|
600
|
-
return { routeMiddleware: undefined };
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
const manifestEntry = await loadManifest(
|
|
604
|
-
matched.entry,
|
|
605
|
-
matched.routeKey,
|
|
606
|
-
pathname,
|
|
607
|
-
undefined,
|
|
608
|
-
false,
|
|
609
|
-
);
|
|
610
|
-
|
|
611
|
-
const routeMiddleware = collectRouteMiddleware(
|
|
612
|
-
traverseBack(manifestEntry),
|
|
613
|
-
matched.params,
|
|
614
|
-
);
|
|
615
|
-
|
|
616
|
-
return {
|
|
617
|
-
routeMiddleware: routeMiddleware.length > 0 ? routeMiddleware : undefined,
|
|
618
|
-
};
|
|
619
|
-
}
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
* - request, url, pathname: The incoming HTTP request
|
|
46
46
|
*
|
|
47
47
|
* Environment:
|
|
48
|
-
* - env
|
|
48
|
+
* - env: Server environment (Cloudflare bindings, etc.)
|
|
49
49
|
*
|
|
50
50
|
* Client State (from RSC request headers):
|
|
51
51
|
* - clientSegmentIds: Segments the client currently has
|
|
@@ -140,7 +140,6 @@ export interface MatchContext<TEnv = any> {
|
|
|
140
140
|
|
|
141
141
|
// Environment
|
|
142
142
|
env: TEnv;
|
|
143
|
-
bindings: TEnv;
|
|
144
143
|
|
|
145
144
|
// Client state
|
|
146
145
|
clientSegmentIds: string[];
|
|
@@ -211,6 +210,9 @@ export interface MatchPipelineState {
|
|
|
211
210
|
// Whether cache should be revalidated (SWR)
|
|
212
211
|
shouldRevalidate?: boolean;
|
|
213
212
|
|
|
213
|
+
// Source of cache hit ("runtime" or "prerender")
|
|
214
|
+
cacheSource?: "runtime" | "prerender";
|
|
215
|
+
|
|
214
216
|
// Resolved segments from pipeline
|
|
215
217
|
segments: ResolvedSegment[];
|
|
216
218
|
matchedIds: string[];
|