@rangojs/router 0.0.0-experimental.10 → 0.0.0-experimental.100
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 +1037 -4
- package/dist/bin/rango.js +1619 -157
- package/dist/vite/index.js +5762 -2301
- package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
- package/package.json +71 -63
- package/skills/breadcrumbs/SKILL.md +252 -0
- package/skills/cache-guide/SKILL.md +294 -0
- package/skills/caching/SKILL.md +93 -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/handler-use/SKILL.md +364 -0
- package/skills/hooks/SKILL.md +367 -71
- package/skills/host-router/SKILL.md +218 -0
- package/skills/i18n/SKILL.md +276 -0
- package/skills/intercept/SKILL.md +176 -8
- package/skills/layout/SKILL.md +124 -3
- package/skills/links/SKILL.md +304 -25
- package/skills/loader/SKILL.md +474 -47
- package/skills/middleware/SKILL.md +207 -37
- package/skills/migrate-nextjs/SKILL.md +562 -0
- package/skills/migrate-react-router/SKILL.md +769 -0
- package/skills/mime-routes/SKILL.md +15 -11
- package/skills/parallel/SKILL.md +272 -1
- package/skills/prerender/SKILL.md +467 -65
- package/skills/rango/SKILL.md +89 -21
- package/skills/response-routes/SKILL.md +152 -91
- package/skills/route/SKILL.md +305 -14
- package/skills/router-setup/SKILL.md +210 -32
- package/skills/server-actions/SKILL.md +739 -0
- package/skills/streams-and-websockets/SKILL.md +283 -0
- package/skills/theme/SKILL.md +9 -8
- package/skills/typesafety/SKILL.md +333 -86
- package/skills/use-cache/SKILL.md +324 -0
- package/skills/view-transitions/SKILL.md +212 -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/app-shell.ts +52 -0
- package/src/browser/app-version.ts +14 -0
- package/src/browser/event-controller.ts +136 -68
- 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 +55 -0
- package/src/browser/merge-segment-loaders.ts +20 -12
- package/src/browser/navigation-bridge.ts +374 -561
- package/src/browser/navigation-client.ts +228 -70
- package/src/browser/navigation-store.ts +97 -55
- package/src/browser/navigation-transaction.ts +297 -0
- package/src/browser/network-error-handler.ts +61 -0
- package/src/browser/partial-update.ts +376 -315
- package/src/browser/prefetch/cache.ts +314 -0
- package/src/browser/prefetch/fetch.ts +282 -0
- package/src/browser/prefetch/observer.ts +65 -0
- package/src/browser/prefetch/policy.ts +48 -0
- package/src/browser/prefetch/queue.ts +191 -0
- package/src/browser/prefetch/resource-ready.ts +77 -0
- package/src/browser/rango-state.ts +152 -0
- package/src/browser/react/Link.tsx +255 -71
- package/src/browser/react/NavigationProvider.tsx +152 -24
- package/src/browser/react/context.ts +11 -0
- package/src/browser/react/filter-segment-order.ts +55 -0
- package/src/browser/react/index.ts +15 -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 +30 -120
- package/src/browser/react/use-link-status.ts +6 -5
- package/src/browser/react/use-navigation.ts +44 -65
- package/src/browser/react/use-params.ts +78 -0
- package/src/browser/react/use-pathname.ts +47 -0
- package/src/browser/react/use-reverse.ts +99 -0
- package/src/browser/react/use-router.ts +83 -0
- package/src/browser/react/use-search-params.ts +56 -0
- package/src/browser/react/use-segments.ts +85 -99
- package/src/browser/response-adapter.ts +73 -0
- package/src/browser/rsc-router.tsx +246 -64
- package/src/browser/scroll-restoration.ts +127 -52
- package/src/browser/segment-reconciler.ts +243 -0
- package/src/browser/segment-structure-assert.ts +16 -0
- package/src/browser/server-action-bridge.ts +510 -603
- package/src/browser/shallow.ts +6 -1
- package/src/browser/types.ts +158 -48
- package/src/browser/validate-redirect-origin.ts +29 -0
- package/src/build/generate-manifest.ts +84 -23
- package/src/build/generate-route-types.ts +39 -828
- package/src/build/index.ts +4 -5
- package/src/build/route-trie.ts +85 -32
- 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 +418 -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 +618 -0
- package/src/build/route-types/scan-filter.ts +85 -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 +167 -307
- package/src/cache/cf/cf-cache-store.ts +573 -21
- 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 +153 -0
- package/src/cache/types.ts +72 -122
- package/src/client.rsc.tsx +6 -1
- package/src/client.tsx +118 -302
- package/src/component-utils.ts +4 -4
- package/src/components/DefaultDocument.tsx +5 -1
- package/src/context-var.ts +156 -0
- package/src/debug.ts +19 -9
- package/src/errors.ts +77 -7
- package/src/handle.ts +55 -10
- 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 +138 -21
- package/src/index.ts +206 -51
- package/src/internal-debug.ts +11 -0
- package/src/loader.rsc.ts +25 -143
- package/src/loader.ts +27 -10
- package/src/network-error-thrower.tsx +3 -1
- package/src/outlet-context.ts +1 -1
- package/src/outlet-provider.tsx +45 -0
- package/src/prerender/param-hash.ts +4 -2
- package/src/prerender/store.ts +159 -13
- package/src/prerender.ts +397 -29
- package/src/response-utils.ts +28 -0
- package/src/reverse.ts +231 -121
- package/src/root-error-boundary.tsx +41 -29
- package/src/route-content-wrapper.tsx +7 -4
- package/src/route-definition/dsl-helpers.ts +1134 -0
- package/src/route-definition/helper-factories.ts +200 -0
- package/src/route-definition/helpers-types.ts +483 -0
- package/src/route-definition/index.ts +55 -0
- package/src/route-definition/redirect.ts +101 -0
- package/src/route-definition/resolve-handler-use.ts +155 -0
- package/src/route-definition.ts +1 -1431
- package/src/route-map-builder.ts +162 -123
- package/src/route-name.ts +53 -0
- package/src/route-types.ts +66 -9
- package/src/router/content-negotiation.ts +215 -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 +418 -86
- package/src/router/intercept-resolution.ts +35 -20
- package/src/router/lazy-includes.ts +237 -0
- package/src/router/loader-resolution.ts +359 -128
- package/src/router/logging.ts +251 -0
- package/src/router/manifest.ts +98 -32
- package/src/router/match-api.ts +196 -261
- package/src/router/match-context.ts +4 -2
- package/src/router/match-handlers.ts +441 -0
- package/src/router/match-middleware/background-revalidation.ts +108 -93
- package/src/router/match-middleware/cache-lookup.ts +415 -86
- package/src/router/match-middleware/cache-store.ts +91 -29
- package/src/router/match-middleware/intercept-resolution.ts +48 -21
- package/src/router/match-middleware/segment-resolution.ts +73 -9
- package/src/router/match-pipelines.ts +10 -45
- package/src/router/match-result.ts +154 -35
- package/src/router/metrics.ts +240 -15
- package/src/router/middleware-cookies.ts +55 -0
- package/src/router/middleware-types.ts +209 -0
- package/src/router/middleware.ts +373 -371
- package/src/router/navigation-snapshot.ts +182 -0
- package/src/router/pattern-matching.ts +292 -52
- package/src/router/prerender-match.ts +502 -0
- package/src/router/preview-match.ts +98 -0
- package/src/router/request-classification.ts +310 -0
- package/src/router/revalidation.ts +152 -39
- package/src/router/route-snapshot.ts +245 -0
- package/src/router/router-context.ts +41 -21
- package/src/router/router-interfaces.ts +484 -0
- package/src/router/router-options.ts +618 -0
- package/src/router/router-registry.ts +24 -0
- package/src/router/segment-resolution/fresh.ts +756 -0
- package/src/router/segment-resolution/helpers.ts +268 -0
- package/src/router/segment-resolution/loader-cache.ts +199 -0
- package/src/router/segment-resolution/revalidation.ts +1407 -0
- package/src/router/segment-resolution/static-store.ts +67 -0
- package/src/router/segment-resolution.ts +21 -1315
- package/src/router/segment-wrappers.ts +291 -0
- package/src/router/substitute-pattern-params.ts +56 -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 +111 -39
- package/src/router/types.ts +17 -9
- package/src/router/url-params.ts +49 -0
- package/src/router.ts +642 -2011
- package/src/rsc/handler-context.ts +45 -0
- package/src/rsc/handler.ts +864 -1114
- package/src/rsc/helpers.ts +181 -19
- package/src/rsc/index.ts +0 -20
- package/src/rsc/loader-fetch.ts +229 -0
- package/src/rsc/manifest-init.ts +90 -0
- package/src/rsc/nonce.ts +14 -0
- package/src/rsc/origin-guard.ts +141 -0
- package/src/rsc/progressive-enhancement.ts +395 -0
- package/src/rsc/response-error.ts +37 -0
- package/src/rsc/response-route-handler.ts +360 -0
- package/src/rsc/rsc-rendering.ts +256 -0
- package/src/rsc/runtime-warnings.ts +42 -0
- package/src/rsc/server-action.ts +360 -0
- package/src/rsc/ssr-setup.ts +128 -0
- package/src/rsc/types.ts +52 -11
- package/src/search-params.ts +230 -0
- package/src/segment-content-promise.ts +67 -0
- package/src/segment-loader-promise.ts +122 -0
- package/src/segment-system.tsx +187 -38
- package/src/server/context.ts +333 -59
- package/src/server/cookie-store.ts +190 -0
- package/src/server/fetchable-loader-store.ts +37 -0
- package/src/server/handle-store.ts +113 -15
- package/src/server/loader-registry.ts +24 -64
- package/src/server/request-context.ts +603 -109
- package/src/server.ts +35 -155
- package/src/ssr/index.tsx +107 -30
- package/src/static-handler.ts +126 -0
- 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 +764 -0
- package/src/types/index.ts +88 -0
- package/src/types/loader-types.ts +209 -0
- package/src/types/request-scope.ts +126 -0
- package/src/types/route-config.ts +170 -0
- package/src/types/route-entry.ts +120 -0
- package/src/types/segments.ts +167 -0
- package/src/types.ts +1 -1757
- package/src/urls/include-helper.ts +207 -0
- package/src/urls/index.ts +53 -0
- package/src/urls/path-helper-types.ts +372 -0
- package/src/urls/path-helper.ts +364 -0
- package/src/urls/pattern-types.ts +107 -0
- package/src/urls/response-types.ts +108 -0
- package/src/urls/type-extraction.ts +372 -0
- package/src/urls/urls-function.ts +98 -0
- package/src/urls.ts +1 -1282
- package/src/use-loader.tsx +161 -81
- package/src/vite/debug.ts +184 -0
- package/src/vite/discovery/bundle-postprocess.ts +181 -0
- package/src/vite/discovery/discover-routers.ts +376 -0
- package/src/vite/discovery/gate-state.ts +171 -0
- package/src/vite/discovery/prerender-collection.ts +486 -0
- package/src/vite/discovery/route-types-writer.ts +258 -0
- package/src/vite/discovery/self-gen-tracking.ts +73 -0
- package/src/vite/discovery/state.ts +117 -0
- package/src/vite/discovery/virtual-module-codegen.ts +203 -0
- package/src/vite/index.ts +15 -2063
- package/src/vite/plugin-types.ts +103 -0
- package/src/vite/plugins/cjs-to-esm.ts +98 -0
- package/src/vite/plugins/client-ref-dedup.ts +131 -0
- package/src/vite/plugins/client-ref-hashing.ts +117 -0
- 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/{expose-action-id.ts → plugins/expose-action-id.ts} +107 -64
- package/src/vite/plugins/expose-id-utils.ts +299 -0
- package/src/vite/plugins/expose-ids/export-analysis.ts +296 -0
- package/src/vite/plugins/expose-ids/handler-transform.ts +209 -0
- package/src/vite/plugins/expose-ids/loader-transform.ts +74 -0
- package/src/vite/plugins/expose-ids/router-transform.ts +127 -0
- package/src/vite/plugins/expose-ids/types.ts +45 -0
- package/src/vite/plugins/expose-internal-ids.ts +816 -0
- package/src/vite/plugins/performance-tracks.ts +96 -0
- package/src/vite/plugins/refresh-cmd.ts +127 -0
- package/src/vite/plugins/use-cache-transform.ts +336 -0
- package/src/vite/plugins/version-injector.ts +109 -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 +497 -0
- package/src/vite/router-discovery.ts +1423 -0
- package/src/vite/utils/ast-handler-extract.ts +517 -0
- 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/utils/package-resolution.ts +161 -0
- package/src/vite/utils/prerender-utils.ts +222 -0
- package/src/vite/utils/shared-utils.ts +170 -0
- package/CLAUDE.md +0 -43
- 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/urls.gen.ts +0 -8
- package/src/vite/expose-handle-id.ts +0 -209
- package/src/vite/expose-loader-id.ts +0 -426
- package/src/vite/expose-location-state-id.ts +0 -177
- package/src/vite/expose-prerender-handler-id.ts +0 -429
- package/src/vite/package-resolution.ts +0 -125
- /package/src/vite/{version.d.ts → plugins/version.d.ts} +0 -0
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
|
+
import { INTERNAL_RANGO_DEBUG } from "../internal-debug.js";
|
|
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
|
+
|
|
41
|
+
interface RouterLogContext {
|
|
42
|
+
requestId: string;
|
|
43
|
+
transactionId: string;
|
|
44
|
+
depth: number;
|
|
45
|
+
revalidationTrace?: RevalidationTrace;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
interface RouterLogOptions {
|
|
49
|
+
request: Request;
|
|
50
|
+
transaction: string;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
interface LogDetails {
|
|
54
|
+
[key: string]: unknown;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const routerLogContext = new AsyncLocalStorage<RouterLogContext>();
|
|
58
|
+
const requestIds = new WeakMap<Request, string>();
|
|
59
|
+
|
|
60
|
+
let requestCounter = 0;
|
|
61
|
+
let transactionCounter = 0;
|
|
62
|
+
|
|
63
|
+
function nextId(prefix: string, counter: number): string {
|
|
64
|
+
return `${prefix}${counter.toString(36)}`;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function getHeaderRequestId(request: Request): string | null {
|
|
68
|
+
const candidate =
|
|
69
|
+
request.headers.get("x-rsc-router-request-id") ??
|
|
70
|
+
request.headers.get("x-request-id") ??
|
|
71
|
+
request.headers.get("cf-ray");
|
|
72
|
+
if (!candidate) return null;
|
|
73
|
+
const trimmed = candidate.trim();
|
|
74
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function getOrCreateRequestId(request: Request): string {
|
|
78
|
+
const existing = requestIds.get(request);
|
|
79
|
+
if (existing) return existing;
|
|
80
|
+
|
|
81
|
+
const fromHeaders = getHeaderRequestId(request);
|
|
82
|
+
if (fromHeaders) {
|
|
83
|
+
requestIds.set(request, fromHeaders);
|
|
84
|
+
return fromHeaders;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
requestCounter += 1;
|
|
88
|
+
const generated = nextId("req-", requestCounter);
|
|
89
|
+
requestIds.set(request, generated);
|
|
90
|
+
return generated;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function runWithRouterLogContext<T>(
|
|
94
|
+
options: RouterLogOptions,
|
|
95
|
+
fn: () => T,
|
|
96
|
+
): T {
|
|
97
|
+
if (!INTERNAL_RANGO_DEBUG) {
|
|
98
|
+
return fn();
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const requestId = getOrCreateRequestId(options.request);
|
|
102
|
+
transactionCounter += 1;
|
|
103
|
+
const transactionId = `${options.transaction}-${nextId("tx-", transactionCounter)}`;
|
|
104
|
+
|
|
105
|
+
return routerLogContext.run(
|
|
106
|
+
{
|
|
107
|
+
requestId,
|
|
108
|
+
transactionId,
|
|
109
|
+
depth: 0,
|
|
110
|
+
},
|
|
111
|
+
fn,
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export function withRouterLogScope<T>(
|
|
116
|
+
label: string,
|
|
117
|
+
fn: () => Promise<T>,
|
|
118
|
+
): Promise<T>;
|
|
119
|
+
export function withRouterLogScope<T>(label: string, fn: () => T): T;
|
|
120
|
+
export function withRouterLogScope<T>(
|
|
121
|
+
label: string,
|
|
122
|
+
fn: () => Promise<T> | T,
|
|
123
|
+
): Promise<T> | T {
|
|
124
|
+
const ctx = routerLogContext.getStore();
|
|
125
|
+
if (!INTERNAL_RANGO_DEBUG || !ctx) {
|
|
126
|
+
return fn();
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
debugLog(label, "start");
|
|
130
|
+
|
|
131
|
+
return routerLogContext.run({ ...ctx, depth: ctx.depth + 1 }, () => {
|
|
132
|
+
try {
|
|
133
|
+
const result = fn();
|
|
134
|
+
if (result && typeof (result as Promise<T>).then === "function") {
|
|
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
|
+
);
|
|
145
|
+
}
|
|
146
|
+
debugLog(label, "end");
|
|
147
|
+
return result;
|
|
148
|
+
} catch (error) {
|
|
149
|
+
debugLog(label, "error", { error: String(error) });
|
|
150
|
+
throw error;
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export function isRouterDebugEnabled(): boolean {
|
|
156
|
+
return INTERNAL_RANGO_DEBUG && !!routerLogContext.getStore();
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function formatPrefix(scope: string): string {
|
|
160
|
+
const ctx = routerLogContext.getStore();
|
|
161
|
+
if (!ctx) return `[Router][${scope}]`;
|
|
162
|
+
const indent = " ".repeat(ctx.depth);
|
|
163
|
+
return `[Router][req:${ctx.requestId}][tx:${ctx.transactionId}] ${indent}[${scope}]`;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export function debugLog(
|
|
167
|
+
scope: string,
|
|
168
|
+
message: string,
|
|
169
|
+
details?: LogDetails,
|
|
170
|
+
): void {
|
|
171
|
+
if (!isRouterDebugEnabled()) return;
|
|
172
|
+
|
|
173
|
+
const prefix = formatPrefix(scope);
|
|
174
|
+
if (details) {
|
|
175
|
+
console.log(`${prefix} ${message}`, details);
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
console.log(`${prefix} ${message}`);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export function debugWarn(
|
|
183
|
+
scope: string,
|
|
184
|
+
message: string,
|
|
185
|
+
details?: LogDetails,
|
|
186
|
+
): void {
|
|
187
|
+
if (!isRouterDebugEnabled()) return;
|
|
188
|
+
|
|
189
|
+
const prefix = formatPrefix(scope);
|
|
190
|
+
if (details) {
|
|
191
|
+
console.warn(`${prefix} ${message}`, details);
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
console.warn(`${prefix} ${message}`);
|
|
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,55 @@ 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;
|
|
123
|
+
|
|
124
|
+
// For lazy entries, merge captured counters from include() so the
|
|
125
|
+
// handler's entries get shortCode indices after sibling entries that
|
|
126
|
+
// were created during pattern extraction. This prevents shortCode
|
|
127
|
+
// collisions between lazy and non-lazy entries under the same parent
|
|
128
|
+
// (e.g., ArticlesLayout and BlogLayout both under NavLayout).
|
|
129
|
+
if (lazyContext?.counters) {
|
|
130
|
+
for (const [key, value] of Object.entries(lazyContext.counters)) {
|
|
131
|
+
Store.counters[key] = Math.max(Store.counters[key] ?? 0, value);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Propagate cache profiles for DSL-time cache("profileName") resolution.
|
|
136
|
+
// Non-lazy entries carry profiles directly; lazy entries carry them
|
|
137
|
+
// in the captured lazyContext from include() time. Always write
|
|
138
|
+
// (including clearing to undefined) so a prior lazy build's profile
|
|
139
|
+
// map cannot leak into a later non-lazy build on the same ALS-backed
|
|
140
|
+
// Store — which would otherwise let cache("name") resolve a profile
|
|
141
|
+
// from an unrelated entry.
|
|
142
|
+
Store.cacheProfiles = entry.cacheProfiles ?? lazyContext?.cacheProfiles;
|
|
143
|
+
|
|
144
|
+
// Propagate rootScoped from lazyContext so that routes inside
|
|
145
|
+
// nested { name: "sub" } under { name: "" } keep inherited root scope
|
|
146
|
+
// when the manifest is rebuilt on each request. Always write
|
|
147
|
+
// (including clearing to undefined, which makes getRootScoped()
|
|
148
|
+
// return its true default) so a prior lazy build's scope cannot leak
|
|
149
|
+
// into a later non-lazy build on the same ALS-backed Store — which
|
|
150
|
+
// would otherwise mis-register plain routes as non-root-scoped and
|
|
151
|
+
// break dot-local reverse resolution.
|
|
152
|
+
Store.rootScoped = lazyContext?.rootScoped;
|
|
153
|
+
|
|
154
|
+
// Propagate includeScope from lazyContext so that direct-descendant
|
|
155
|
+
// shortCodes of this include use the correct scoped counter namespace
|
|
156
|
+
// on every manifest rebuild. Always write (including clearing to
|
|
157
|
+
// undefined) so a prior lazy build's scope cannot leak into a later
|
|
158
|
+
// non-lazy build on the same ALS-backed Store.
|
|
159
|
+
Store.includeScope = lazyContext?.includeScope;
|
|
111
160
|
|
|
112
161
|
const handlerExecStart = performance.now();
|
|
113
162
|
const useItems = await getContext().runWithStore(
|
|
@@ -118,25 +167,23 @@ export async function loadManifest(
|
|
|
118
167
|
// Create helpers for lazy-loaded handlers that need them
|
|
119
168
|
const helpers = createRouteHelpers();
|
|
120
169
|
|
|
121
|
-
// For lazy entries, use lazyPatterns.handler() with proper prefixes
|
|
170
|
+
// For lazy entries, use lazyPatterns.handler() with proper prefixes.
|
|
171
|
+
// Do NOT wrap in MapRootLayout here: the captured parent chain from
|
|
172
|
+
// pattern extraction already includes the synthetic MapRootLayout
|
|
173
|
+
// parent, so adding another would create an extra level that does
|
|
174
|
+
// not exist in the non-lazy (root handler) path and would produce
|
|
175
|
+
// mismatched shortCodes.
|
|
122
176
|
if (entry.lazy && entry.lazyPatterns) {
|
|
123
177
|
const lazyPatterns = entry.lazyPatterns as UrlPatterns<any>;
|
|
124
178
|
const includePrefix = (entry as any)._lazyPrefix || "";
|
|
125
179
|
const fullPrefix = (lazyContext?.urlPrefix || "") + includePrefix;
|
|
126
180
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
() => lazyPatterns.handler()
|
|
134
|
-
);
|
|
135
|
-
}
|
|
136
|
-
return lazyPatterns.handler();
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
return [wrappedItems].flat(3);
|
|
181
|
+
if (fullPrefix || lazyContext?.namePrefix) {
|
|
182
|
+
return runWithPrefixes(fullPrefix, lazyContext?.namePrefix, () =>
|
|
183
|
+
lazyPatterns.handler(),
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
return lazyPatterns.handler();
|
|
140
187
|
}
|
|
141
188
|
|
|
142
189
|
// Wrap handler execution in root layout so routes get correct parent
|
|
@@ -162,36 +209,55 @@ export async function loadManifest(
|
|
|
162
209
|
"default" in load
|
|
163
210
|
) {
|
|
164
211
|
// Promise<{ default: () => Array }> - e.g., dynamic import
|
|
165
|
-
|
|
212
|
+
if (typeof load.default !== "function") {
|
|
213
|
+
throw new Error(
|
|
214
|
+
`[@rangojs/router] Unsupported async handler: { default } must be a function, ` +
|
|
215
|
+
`got ${typeof load.default}. Use () => import('./urls') for lazy loading.`,
|
|
216
|
+
);
|
|
217
|
+
}
|
|
166
218
|
return (load.default as (h?: any) => any)(helpers);
|
|
167
219
|
}
|
|
168
220
|
if (typeof load === "function") {
|
|
169
221
|
// Promise<() => Array>
|
|
170
222
|
return (load as (h?: any) => any)(helpers);
|
|
171
223
|
}
|
|
172
|
-
//
|
|
173
|
-
|
|
224
|
+
// Reject unsupported async handler results. Supported shapes are:
|
|
225
|
+
// Promise<{ default: fn }> — dynamic import
|
|
226
|
+
// Promise<fn> — lazy function
|
|
227
|
+
// Direct Promise<Array> is not supported; use a function wrapper.
|
|
228
|
+
throw new Error(
|
|
229
|
+
`[@rangojs/router] Unsupported async handler result (${typeof load}). ` +
|
|
230
|
+
`Lazy route handlers must resolve to a function or { default: fn }, ` +
|
|
231
|
+
`not a direct array. Wrap your handler: () => import('./urls') or ` +
|
|
232
|
+
`() => Promise.resolve((h) => [...])`,
|
|
233
|
+
);
|
|
174
234
|
}
|
|
175
235
|
|
|
176
236
|
// Inline handler - routes were registered with correct parent inside layout
|
|
177
237
|
return [wrappedItems].flat(3);
|
|
178
|
-
}
|
|
238
|
+
},
|
|
179
239
|
);
|
|
180
240
|
pushMetric?.("manifest:handler-exec", handlerExecStart);
|
|
181
241
|
|
|
182
242
|
const validationStart = performance.now();
|
|
183
243
|
invariant(
|
|
184
244
|
useItems && useItems.length > 0,
|
|
185
|
-
"Did not receive any handler from router.map()"
|
|
186
|
-
);
|
|
187
|
-
invariant(
|
|
188
|
-
useItems.some((item: { type: string }) => item.type === "layout"),
|
|
189
|
-
"Top-level handler must be a layout"
|
|
245
|
+
"Did not receive any handler from router.map()",
|
|
190
246
|
);
|
|
247
|
+
// For non-lazy entries the root handler is wrapped in MapRootLayout,
|
|
248
|
+
// so the result always contains a layout item. Lazy entries run the
|
|
249
|
+
// included patterns handler directly (no MapRootLayout wrapper) so
|
|
250
|
+
// we skip this check -- the layout is in the captured parent chain.
|
|
251
|
+
if (!lazyContext) {
|
|
252
|
+
invariant(
|
|
253
|
+
useItems.some((item: { type: string }) => item.type === "layout"),
|
|
254
|
+
"Top-level handler must be a layout",
|
|
255
|
+
);
|
|
256
|
+
}
|
|
191
257
|
|
|
192
258
|
invariant(
|
|
193
259
|
Store.manifest.has(routeKey),
|
|
194
|
-
`Route must be registered for ${routeKey}
|
|
260
|
+
`Route must be registered for ${routeKey}`,
|
|
195
261
|
);
|
|
196
262
|
pushMetric?.("manifest:validation", validationStart);
|
|
197
263
|
|
|
@@ -210,7 +276,7 @@ export async function loadManifest(
|
|
|
210
276
|
routeKey,
|
|
211
277
|
},
|
|
212
278
|
},
|
|
213
|
-
}
|
|
279
|
+
},
|
|
214
280
|
);
|
|
215
281
|
}
|
|
216
282
|
}
|