@rangojs/router 0.0.0-experimental.77 → 0.0.0-experimental.77ed8945
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/README.md +120 -25
- package/dist/bin/rango.js +147 -57
- package/dist/vite/index.js +2103 -861
- package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
- package/package.json +13 -8
- package/skills/api-client/SKILL.md +211 -0
- package/skills/breadcrumbs/SKILL.md +3 -1
- package/skills/bundle-analysis/SKILL.md +159 -0
- package/skills/cache-guide/SKILL.md +220 -30
- package/skills/caching/SKILL.md +116 -8
- package/skills/composability/SKILL.md +27 -2
- package/skills/css/SKILL.md +76 -0
- package/skills/document-cache/SKILL.md +78 -55
- package/skills/handler-use/SKILL.md +3 -1
- package/skills/hooks/SKILL.md +229 -20
- package/skills/host-router/SKILL.md +66 -20
- package/skills/i18n/SKILL.md +276 -0
- package/skills/intercept/SKILL.md +26 -4
- package/skills/layout/SKILL.md +6 -7
- package/skills/links/SKILL.md +247 -17
- package/skills/loader/SKILL.md +219 -9
- package/skills/middleware/SKILL.md +47 -12
- package/skills/migrate-nextjs/SKILL.md +562 -0
- package/skills/migrate-react-router/SKILL.md +769 -0
- package/skills/mime-routes/SKILL.md +27 -0
- package/skills/observability/SKILL.md +137 -0
- package/skills/parallel/SKILL.md +12 -6
- package/skills/prerender/SKILL.md +14 -33
- package/skills/rango/SKILL.md +238 -22
- package/skills/react-compiler/SKILL.md +168 -0
- package/skills/response-routes/SKILL.md +122 -47
- package/skills/route/SKILL.md +33 -4
- package/skills/router-setup/SKILL.md +3 -3
- package/skills/server-actions/SKILL.md +751 -0
- package/skills/streams-and-websockets/SKILL.md +283 -0
- package/skills/tailwind/SKILL.md +27 -3
- package/skills/typesafety/SKILL.md +319 -27
- package/skills/use-cache/SKILL.md +34 -5
- package/skills/view-transitions/SKILL.md +294 -0
- package/src/__augment-tests__/augment.ts +81 -0
- package/src/__augment-tests__/augmented.check.ts +116 -0
- package/src/browser/action-coordinator.ts +53 -36
- package/src/browser/app-shell.ts +39 -0
- package/src/browser/event-controller.ts +86 -70
- package/src/browser/history-state.ts +21 -0
- package/src/browser/index.ts +3 -3
- package/src/browser/navigation-bridge.ts +29 -9
- package/src/browser/navigation-client.ts +99 -77
- package/src/browser/navigation-store.ts +7 -8
- package/src/browser/navigation-transaction.ts +10 -28
- package/src/browser/partial-update.ts +60 -40
- package/src/browser/prefetch/cache.ts +196 -49
- package/src/browser/prefetch/fetch.ts +203 -59
- package/src/browser/prefetch/queue.ts +36 -5
- package/src/browser/rango-state.ts +37 -13
- package/src/browser/react/Link.tsx +18 -13
- package/src/browser/react/NavigationProvider.tsx +75 -31
- package/src/browser/react/filter-segment-order.ts +51 -7
- package/src/browser/react/index.ts +3 -0
- package/src/browser/react/location-state-shared.ts +175 -4
- package/src/browser/react/location-state.ts +39 -13
- package/src/browser/react/use-handle.ts +17 -9
- package/src/browser/react/use-navigation.ts +22 -2
- package/src/browser/react/use-params.ts +20 -8
- package/src/browser/react/use-reverse.ts +106 -0
- package/src/browser/react/use-router.ts +23 -2
- package/src/browser/react/use-segments.ts +11 -8
- package/src/browser/response-adapter.ts +52 -1
- package/src/browser/rsc-router.tsx +71 -22
- package/src/browser/scroll-restoration.ts +22 -14
- package/src/browser/segment-reconciler.ts +10 -14
- package/src/browser/segment-structure-assert.ts +2 -2
- package/src/browser/server-action-bridge.ts +44 -30
- package/src/browser/types.ts +12 -2
- package/src/build/collect-fallback-refs.ts +107 -0
- package/src/build/generate-manifest.ts +60 -35
- package/src/build/generate-route-types.ts +2 -0
- package/src/build/index.ts +8 -1
- package/src/build/prefix-tree-utils.ts +123 -0
- package/src/build/route-trie.ts +45 -1
- package/src/build/route-types/codegen.ts +4 -4
- package/src/build/route-types/include-resolution.ts +1 -1
- package/src/build/route-types/per-module-writer.ts +7 -4
- package/src/build/route-types/router-processing.ts +55 -14
- package/src/build/route-types/scan-filter.ts +1 -1
- package/src/build/route-types/source-scan.ts +118 -0
- package/src/build/runtime-discovery.ts +9 -20
- package/src/cache/cache-runtime.ts +17 -5
- package/src/cache/cache-scope.ts +51 -49
- package/src/cache/cf/cf-cache-store.ts +502 -32
- package/src/cache/cf/index.ts +3 -0
- package/src/cache/handle-snapshot.ts +103 -0
- package/src/cache/index.ts +3 -0
- package/src/cache/memory-segment-store.ts +3 -2
- package/src/cache/types.ts +10 -6
- package/src/client.rsc.tsx +3 -0
- package/src/client.tsx +96 -205
- package/src/context-var.ts +5 -5
- package/src/decode-loader-results.ts +36 -0
- package/src/errors.ts +30 -4
- package/src/handle.ts +4 -6
- package/src/host/index.ts +2 -2
- package/src/host/router.ts +129 -57
- package/src/host/types.ts +31 -2
- package/src/host/utils.ts +1 -1
- package/src/href-client.ts +140 -21
- package/src/index.rsc.ts +10 -6
- package/src/index.ts +17 -8
- package/src/loader-store.ts +500 -0
- package/src/loader.rsc.ts +2 -5
- package/src/loader.ts +3 -10
- package/src/missing-id-error.ts +68 -0
- package/src/outlet-context.ts +1 -1
- package/src/prerender/store.ts +9 -7
- package/src/prerender.ts +4 -4
- package/src/response-utils.ts +37 -0
- package/src/reverse.ts +65 -39
- package/src/route-content-wrapper.tsx +6 -28
- package/src/route-definition/dsl-helpers.ts +253 -265
- package/src/route-definition/helper-factories.ts +29 -139
- package/src/route-definition/helpers-types.ts +43 -15
- package/src/route-definition/resolve-handler-use.ts +6 -0
- package/src/route-definition/use-item-types.ts +32 -0
- package/src/route-types.ts +26 -41
- package/src/router/content-negotiation.ts +15 -2
- package/src/router/error-handling.ts +1 -1
- package/src/router/find-match.ts +54 -6
- package/src/router/handler-context.ts +21 -41
- package/src/router/intercept-resolution.ts +4 -18
- package/src/router/lazy-includes.ts +41 -22
- package/src/router/loader-resolution.ts +82 -36
- package/src/router/manifest.ts +41 -19
- package/src/router/match-api.ts +4 -3
- package/src/router/match-handlers.ts +1 -0
- package/src/router/match-middleware/cache-lookup.ts +57 -95
- package/src/router/match-middleware/cache-store.ts +3 -2
- package/src/router/match-result.ts +53 -32
- package/src/router/metrics.ts +1 -1
- package/src/router/middleware-types.ts +15 -26
- package/src/router/middleware.ts +99 -84
- package/src/router/pattern-matching.ts +116 -19
- package/src/router/prerender-match.ts +40 -15
- package/src/router/preview-match.ts +3 -1
- package/src/router/request-classification.ts +40 -37
- package/src/router/revalidation.ts +58 -2
- package/src/router/router-interfaces.ts +51 -35
- package/src/router/router-options.ts +25 -1
- package/src/router/router-registry.ts +2 -5
- package/src/router/segment-resolution/fresh.ts +27 -6
- package/src/router/segment-resolution/revalidation.ts +147 -106
- package/src/router/segment-resolution/static-store.ts +19 -5
- package/src/router/segment-resolution/view-transition-default.ts +36 -0
- package/src/router/substitute-pattern-params.ts +56 -0
- package/src/router/trie-matching.ts +40 -16
- package/src/router/types.ts +8 -0
- package/src/router/url-params.ts +49 -0
- package/src/router.ts +37 -25
- package/src/rsc/handler-context.ts +2 -2
- package/src/rsc/handler.ts +58 -77
- package/src/rsc/helpers.ts +72 -43
- package/src/rsc/index.ts +1 -1
- package/src/rsc/manifest-init.ts +28 -41
- package/src/rsc/origin-guard.ts +30 -10
- package/src/rsc/progressive-enhancement.ts +4 -0
- package/src/rsc/response-error.ts +79 -12
- package/src/rsc/response-route-handler.ts +76 -61
- package/src/rsc/rsc-rendering.ts +45 -51
- package/src/rsc/runtime-warnings.ts +9 -10
- package/src/rsc/server-action.ts +33 -39
- package/src/rsc/ssr-setup.ts +16 -0
- package/src/rsc/types.ts +8 -2
- package/src/search-params.ts +4 -4
- package/src/segment-content-promise.ts +67 -0
- package/src/segment-loader-promise.ts +122 -0
- package/src/segment-system.tsx +132 -116
- package/src/serialize.ts +243 -0
- package/src/server/context.ts +175 -53
- package/src/server/cookie-store.ts +28 -4
- package/src/server/request-context.ts +57 -51
- package/src/ssr/index.tsx +5 -1
- package/src/static-handler.ts +1 -1
- package/src/types/global-namespace.ts +39 -26
- package/src/types/handler-context.ts +68 -50
- package/src/types/index.ts +1 -0
- package/src/types/loader-types.ts +11 -9
- package/src/types/request-scope.ts +126 -0
- package/src/types/route-entry.ts +11 -0
- package/src/types/segments.ts +35 -2
- package/src/urls/include-helper.ts +34 -67
- package/src/urls/index.ts +1 -5
- package/src/urls/path-helper-types.ts +17 -3
- package/src/urls/path-helper.ts +17 -52
- package/src/urls/pattern-types.ts +36 -19
- package/src/urls/response-types.ts +22 -29
- package/src/urls/type-extraction.ts +58 -139
- package/src/urls/urls-function.ts +1 -5
- package/src/use-loader.tsx +413 -42
- package/src/vite/debug.ts +185 -0
- package/src/vite/discovery/bundle-postprocess.ts +6 -6
- package/src/vite/discovery/discover-routers.ts +106 -75
- package/src/vite/discovery/discovery-errors.ts +194 -0
- package/src/vite/discovery/gate-state.ts +171 -0
- package/src/vite/discovery/prerender-collection.ts +72 -31
- package/src/vite/discovery/route-types-writer.ts +40 -84
- package/src/vite/discovery/self-gen-tracking.ts +27 -1
- package/src/vite/discovery/state.ts +33 -0
- package/src/vite/discovery/virtual-module-codegen.ts +13 -23
- package/src/vite/index.ts +2 -0
- package/src/vite/plugin-types.ts +67 -0
- package/src/vite/plugins/cjs-to-esm.ts +8 -7
- package/src/vite/plugins/client-ref-dedup.ts +16 -0
- package/src/vite/plugins/client-ref-hashing.ts +28 -5
- 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 +54 -30
- package/src/vite/plugins/expose-id-utils.ts +12 -8
- package/src/vite/plugins/expose-ids/export-analysis.ts +100 -20
- package/src/vite/plugins/expose-ids/handler-transform.ts +8 -61
- package/src/vite/plugins/expose-ids/loader-transform.ts +3 -5
- package/src/vite/plugins/expose-ids/router-transform.ts +20 -3
- package/src/vite/plugins/expose-internal-ids.ts +496 -486
- package/src/vite/plugins/performance-tracks.ts +29 -25
- package/src/vite/plugins/use-cache-transform.ts +65 -50
- package/src/vite/plugins/version-injector.ts +39 -23
- package/src/vite/plugins/version-plugin.ts +59 -2
- package/src/vite/plugins/virtual-entries.ts +2 -2
- package/src/vite/rango.ts +116 -29
- package/src/vite/router-discovery.ts +753 -104
- package/src/vite/utils/ast-handler-extract.ts +15 -15
- package/src/vite/utils/banner.ts +1 -1
- package/src/vite/utils/bundle-analysis.ts +4 -2
- package/src/vite/utils/client-chunks.ts +190 -0
- package/src/vite/utils/forward-user-plugins.ts +193 -0
- package/src/vite/utils/manifest-utils.ts +8 -59
- package/src/vite/utils/package-resolution.ts +41 -1
- package/src/vite/utils/prerender-utils.ts +5 -4
- package/src/vite/utils/shared-utils.ts +107 -26
- package/src/browser/action-response-classifier.ts +0 -99
|
@@ -18,6 +18,8 @@ import { isInsideCacheScope } from "../server/context.js";
|
|
|
18
18
|
import { NOCACHE_SYMBOL, assertNotInsideCacheExec } from "../cache/taint.js";
|
|
19
19
|
import { isAutoGeneratedRouteName } from "../route-name.js";
|
|
20
20
|
import { PRERENDER_PASSTHROUGH } from "../prerender.js";
|
|
21
|
+
import { substitutePatternParams } from "./substitute-pattern-params.js";
|
|
22
|
+
import { fireAndForgetWaitUntil } from "../types/request-scope.js";
|
|
21
23
|
|
|
22
24
|
/**
|
|
23
25
|
* Strip internal _rsc* query params from a URL.
|
|
@@ -158,51 +160,14 @@ export function createReverseFunction(
|
|
|
158
160
|
);
|
|
159
161
|
}
|
|
160
162
|
|
|
161
|
-
let result = pattern;
|
|
162
|
-
|
|
163
163
|
// Merge current request params as defaults, explicit params override
|
|
164
164
|
const effectiveParams = currentParams
|
|
165
165
|
? { ...currentParams, ...hrefParams }
|
|
166
166
|
: hrefParams;
|
|
167
167
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
let hadOmittedOptional = false;
|
|
172
|
-
// First pass: optional params (trailing ?)
|
|
173
|
-
result = result.replace(
|
|
174
|
-
/:([a-zA-Z_][a-zA-Z0-9_]*)(\([^)]*\))?(\?)/g,
|
|
175
|
-
(_, key) => {
|
|
176
|
-
const value = effectiveParams[key];
|
|
177
|
-
// Empty string is treated as omitted — the trie matcher fills
|
|
178
|
-
// unmatched optional params with "" (not undefined), so reverse
|
|
179
|
-
// must collapse those segments instead of leaving empty slots.
|
|
180
|
-
if (value === undefined || value === "") {
|
|
181
|
-
hadOmittedOptional = true;
|
|
182
|
-
return "";
|
|
183
|
-
}
|
|
184
|
-
return encodeURIComponent(value);
|
|
185
|
-
},
|
|
186
|
-
);
|
|
187
|
-
// Second pass: required params (no trailing ?)
|
|
188
|
-
result = result.replace(
|
|
189
|
-
/:([a-zA-Z_][a-zA-Z0-9_]*)(\([^)]*\))?(?!\?)/g,
|
|
190
|
-
(_, key) => {
|
|
191
|
-
const value = effectiveParams[key];
|
|
192
|
-
if (value === undefined) {
|
|
193
|
-
throw new Error(`Missing param "${key}" for route "${name}"`);
|
|
194
|
-
}
|
|
195
|
-
return encodeURIComponent(value);
|
|
196
|
-
},
|
|
197
|
-
);
|
|
198
|
-
// Clean up slashes only when an optional param was actually omitted,
|
|
199
|
-
// so intentional trailing-slash patterns like "/blog/" are preserved.
|
|
200
|
-
if (hadOmittedOptional) {
|
|
201
|
-
const hadTrailingSlash = pattern.length > 1 && pattern.endsWith("/");
|
|
202
|
-
result = result.replace(/\/\/+/g, "/").replace(/\/+$/, "") || "/";
|
|
203
|
-
if (hadTrailingSlash && !result.endsWith("/")) result += "/";
|
|
204
|
-
}
|
|
205
|
-
}
|
|
168
|
+
let result = effectiveParams
|
|
169
|
+
? substitutePatternParams(pattern, effectiveParams, name)
|
|
170
|
+
: pattern;
|
|
206
171
|
|
|
207
172
|
// Append search params as query string
|
|
208
173
|
if (search) {
|
|
@@ -281,8 +246,12 @@ export function createHandlerContext<TEnv>(
|
|
|
281
246
|
search: searchSchema ? resolvedSearchParams : {},
|
|
282
247
|
pathname,
|
|
283
248
|
url,
|
|
284
|
-
originalUrl: new URL(request.url),
|
|
249
|
+
originalUrl: requestContext?.originalUrl ?? new URL(request.url),
|
|
285
250
|
env: bindings,
|
|
251
|
+
waitUntil: requestContext
|
|
252
|
+
? requestContext.waitUntil.bind(requestContext)
|
|
253
|
+
: fireAndForgetWaitUntil,
|
|
254
|
+
executionContext: requestContext?.executionContext,
|
|
286
255
|
_variables: variables,
|
|
287
256
|
get: ((keyOrVar: any) => {
|
|
288
257
|
// Read-time guard: non-cacheable var inside cache() → throw.
|
|
@@ -387,6 +356,12 @@ export function createPrerenderContext<TEnv>(
|
|
|
387
356
|
"Configure buildEnv in your rango() plugin options to enable build-time env access.",
|
|
388
357
|
);
|
|
389
358
|
},
|
|
359
|
+
// Build-time prerender has no live request. waitUntil is a true no-op
|
|
360
|
+
// (running fn() here would fire side effects during build, which is
|
|
361
|
+
// incorrect — these are meant to outlive the live response).
|
|
362
|
+
// executionContext is absent for the same reason.
|
|
363
|
+
waitUntil: () => {},
|
|
364
|
+
executionContext: undefined,
|
|
390
365
|
_variables: variables,
|
|
391
366
|
get: ((keyOrVar: any) => contextGet(variables, keyOrVar)) as any,
|
|
392
367
|
set: ((keyOrVar: any, value: any) => {
|
|
@@ -476,6 +451,11 @@ export function createStaticContext<TEnv>(
|
|
|
476
451
|
"Configure buildEnv in your rango() plugin options to enable build-time env access.",
|
|
477
452
|
);
|
|
478
453
|
},
|
|
454
|
+
// Static() handlers have no live request. waitUntil is a true no-op
|
|
455
|
+
// (running fn() here would fire side effects during build, which is
|
|
456
|
+
// incorrect). executionContext is absent for the same reason.
|
|
457
|
+
waitUntil: () => {},
|
|
458
|
+
executionContext: undefined,
|
|
479
459
|
_variables: variables,
|
|
480
460
|
get: ((keyOrVar: any) => contextGet(variables, keyOrVar)) as any,
|
|
481
461
|
set: ((keyOrVar: any, value: any) => {
|
|
@@ -66,28 +66,14 @@ export function findInterceptForRoute(
|
|
|
66
66
|
let current: EntryData | null = fromEntry;
|
|
67
67
|
|
|
68
68
|
while (current) {
|
|
69
|
-
|
|
70
|
-
|
|
69
|
+
// current first, then its sibling layouts — same order as before.
|
|
70
|
+
for (const source of [current, ...current.layout]) {
|
|
71
|
+
for (const intercept of source.intercept) {
|
|
71
72
|
if (
|
|
72
73
|
intercept.routeName === targetRouteKey &&
|
|
73
74
|
evaluateInterceptWhen(intercept, selectorContext, isAction)
|
|
74
75
|
) {
|
|
75
|
-
return { intercept, entry:
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
if (current.layout && current.layout.length > 0) {
|
|
81
|
-
for (const siblingLayout of current.layout) {
|
|
82
|
-
if (siblingLayout.intercept && siblingLayout.intercept.length > 0) {
|
|
83
|
-
for (const intercept of siblingLayout.intercept) {
|
|
84
|
-
if (
|
|
85
|
-
intercept.routeName === targetRouteKey &&
|
|
86
|
-
evaluateInterceptWhen(intercept, selectorContext, isAction)
|
|
87
|
-
) {
|
|
88
|
-
return { intercept, entry: siblingLayout };
|
|
89
|
-
}
|
|
90
|
-
}
|
|
76
|
+
return { intercept, entry: source };
|
|
91
77
|
}
|
|
92
78
|
}
|
|
93
79
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { registerRouteMap } from "../route-map-builder.js";
|
|
2
|
-
import { extractStaticPrefix } from "./pattern-matching.js";
|
|
2
|
+
import { extractStaticPrefix, joinPrefix } from "./pattern-matching.js";
|
|
3
3
|
import {
|
|
4
|
-
EntryData,
|
|
5
|
-
|
|
4
|
+
type EntryData,
|
|
5
|
+
RangoContext,
|
|
6
6
|
runWithPrefixes,
|
|
7
7
|
getIsolatedLazyParent,
|
|
8
8
|
} from "../server/context";
|
|
@@ -81,11 +81,16 @@ export function evaluateLazyEntry<TEnv = any>(
|
|
|
81
81
|
// Check for pre-computed routes from build-time data.
|
|
82
82
|
// Only leaf nodes (no nested includes) are precomputed, so entries with
|
|
83
83
|
// nested lazy includes fall through to the handler below.
|
|
84
|
-
//
|
|
85
|
-
//
|
|
86
|
-
//
|
|
87
|
-
//
|
|
88
|
-
//
|
|
84
|
+
//
|
|
85
|
+
// The load-bearing protection against two includes sharing a staticPrefix
|
|
86
|
+
// lives UPSTREAM in buildPrecomputedByPrefix (build/prefix-tree-utils): a
|
|
87
|
+
// shared staticPrefix is omitted from the map entirely, so currentPrecomputed
|
|
88
|
+
// never returns routes for it and the shortcut is skipped. The live-count
|
|
89
|
+
// check below is a secondary guard only — it is TIMING-BLIND (it counts
|
|
90
|
+
// routesEntries, which cannot see a nested sibling that has not been spliced
|
|
91
|
+
// in yet), so it must NOT be relied on alone. Kept as defense-in-depth for the
|
|
92
|
+
// all-siblings-live case (e.g. several include("/", ...) placeholders created
|
|
93
|
+
// up front).
|
|
89
94
|
const currentPrecomputed = deps.getPrecomputedByPrefix();
|
|
90
95
|
if (currentPrecomputed) {
|
|
91
96
|
const routes = currentPrecomputed.get(entry.staticPrefix);
|
|
@@ -113,7 +118,15 @@ export function evaluateLazyEntry<TEnv = any>(
|
|
|
113
118
|
const lazyPatterns = entry.lazyPatterns as UrlPatterns<TEnv>;
|
|
114
119
|
const lazyContext = entry.lazyContext;
|
|
115
120
|
|
|
116
|
-
// Create a new context for evaluating the lazy patterns
|
|
121
|
+
// Create a new context for evaluating the lazy patterns.
|
|
122
|
+
// KNOWN REDUNDANCY (LP3, docs/internal/matching-and-lazy-discovery.md): this
|
|
123
|
+
// runs lazyPatterns.handler() purely to extract `patterns` (route name ->
|
|
124
|
+
// pattern) for matching, and DISCARDS the EntryData `manifest` it builds.
|
|
125
|
+
// loadManifest() then runs the SAME handler again on the first request to
|
|
126
|
+
// build the EntryData tree for rendering. Unifying the two runs is deferred
|
|
127
|
+
// (the two run in different contexts — see the LP3 todo in
|
|
128
|
+
// lazy-include-perf.test.ts). The precomputed-entries shortcut above avoids
|
|
129
|
+
// THIS run entirely for leaf includes.
|
|
117
130
|
const manifest = new Map<string, EntryData>();
|
|
118
131
|
const patterns = new Map<string, string>();
|
|
119
132
|
const patternsByPrefix = new Map<string, Map<string, string>>();
|
|
@@ -125,14 +138,13 @@ export function evaluateLazyEntry<TEnv = any>(
|
|
|
125
138
|
// Merge captured counters from include() to maintain consistent
|
|
126
139
|
// shortCode indices with sibling entries from pattern extraction
|
|
127
140
|
const lazyCounters: Record<string, number> = {};
|
|
128
|
-
if (lazyContext
|
|
129
|
-
const
|
|
130
|
-
for (const [key, value] of Object.entries(captured)) {
|
|
141
|
+
if (lazyContext?.counters) {
|
|
142
|
+
for (const [key, value] of Object.entries(lazyContext.counters)) {
|
|
131
143
|
lazyCounters[key] = value;
|
|
132
144
|
}
|
|
133
145
|
}
|
|
134
146
|
|
|
135
|
-
|
|
147
|
+
RangoContext.run(
|
|
136
148
|
{
|
|
137
149
|
manifest,
|
|
138
150
|
patterns,
|
|
@@ -141,14 +153,18 @@ export function evaluateLazyEntry<TEnv = any>(
|
|
|
141
153
|
namespace: "lazy",
|
|
142
154
|
parent: getIsolatedLazyParent(lazyContext?.parent as EntryData | null),
|
|
143
155
|
counters: lazyCounters,
|
|
144
|
-
cacheProfiles:
|
|
145
|
-
rootScoped:
|
|
156
|
+
cacheProfiles: lazyContext?.cacheProfiles,
|
|
157
|
+
rootScoped: lazyContext?.rootScoped,
|
|
158
|
+
includeScope: lazyContext?.includeScope,
|
|
146
159
|
},
|
|
147
160
|
() => {
|
|
148
|
-
// Run the lazy patterns handler with the original context prefixes
|
|
149
|
-
// The prefix comes from the IncludeItem stored in lazyPatterns
|
|
161
|
+
// Run the lazy patterns handler with the original context prefixes.
|
|
162
|
+
// The prefix comes from the IncludeItem stored in lazyPatterns. Use the
|
|
163
|
+
// slash-collapsing join so a trailing-slash parent prefix does not bake a
|
|
164
|
+
// double slash into the registered route patterns (entry.routes,
|
|
165
|
+
// reverse(), EntryData.pattern, mountPath) when the handler runs.
|
|
150
166
|
const includePrefix = (entry as any)._lazyPrefix || "";
|
|
151
|
-
const fullPrefix = (lazyContext?.urlPrefix
|
|
167
|
+
const fullPrefix = joinPrefix(lazyContext?.urlPrefix, includePrefix);
|
|
152
168
|
|
|
153
169
|
if (fullPrefix || lazyContext?.namePrefix) {
|
|
154
170
|
runWithPrefixes(fullPrefix, lazyContext?.namePrefix, () => {
|
|
@@ -190,10 +206,13 @@ export function evaluateLazyEntry<TEnv = any>(
|
|
|
190
206
|
// Detect nested lazy includes and register them as new entries
|
|
191
207
|
const nestedLazyIncludes = findLazyIncludes(handlerResult);
|
|
192
208
|
for (const lazyInclude of nestedLazyIncludes) {
|
|
193
|
-
// Compute the full URL prefix (combining parent prefix if any)
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
209
|
+
// Compute the full URL prefix (combining parent prefix if any). Use the
|
|
210
|
+
// slash-collapsing join so a trailing-slash parent prefix does not produce
|
|
211
|
+
// a double-slash staticPrefix the trie's sp can never match.
|
|
212
|
+
const fullPrefix = joinPrefix(
|
|
213
|
+
lazyInclude.context.urlPrefix,
|
|
214
|
+
lazyInclude.prefix,
|
|
215
|
+
);
|
|
197
216
|
|
|
198
217
|
const nestedEntry: RouteEntry<TEnv> & { _lazyPrefix?: string } = {
|
|
199
218
|
prefix: "",
|
|
@@ -24,7 +24,12 @@ import { isHandle, collectHandleData, type Handle } from "../handle.js";
|
|
|
24
24
|
import { buildHandleSnapshot } from "../server/handle-store.js";
|
|
25
25
|
import { getFetchableLoader } from "../server/fetchable-loader-store.js";
|
|
26
26
|
import { _getRequestContext } from "../server/request-context.js";
|
|
27
|
-
import {
|
|
27
|
+
import {
|
|
28
|
+
isInsideLoaderScope,
|
|
29
|
+
runInsideLoaderBodyScope,
|
|
30
|
+
isInsidePushCallbackScope,
|
|
31
|
+
runInsidePushCallbackScope,
|
|
32
|
+
} from "../server/context.js";
|
|
28
33
|
import { debugLog } from "./logging.js";
|
|
29
34
|
|
|
30
35
|
/**
|
|
@@ -266,7 +271,10 @@ function createLoaderExecutor<TEnv>(
|
|
|
266
271
|
search: (ctx as any).search,
|
|
267
272
|
pathname: ctx.pathname,
|
|
268
273
|
url: ctx.url,
|
|
274
|
+
originalUrl: ctx.originalUrl,
|
|
269
275
|
env: ctx.env,
|
|
276
|
+
waitUntil: ctx.waitUntil.bind(ctx),
|
|
277
|
+
executionContext: ctx.executionContext,
|
|
270
278
|
get: ((keyOrVar: any) =>
|
|
271
279
|
contextGet(variables, keyOrVar)) as typeof ctx.get,
|
|
272
280
|
use: ((item: LoaderDefinition<any, any> | Handle<any, any>) => {
|
|
@@ -284,6 +292,12 @@ function createLoaderExecutor<TEnv>(
|
|
|
284
292
|
);
|
|
285
293
|
}
|
|
286
294
|
const segmentOrder = reqCtx._renderBarrierSegmentOrder ?? [];
|
|
295
|
+
// The complete snapshot is cached at barrier resolution for
|
|
296
|
+
// non-streaming trees, and by rendered() after handleStore.settled for
|
|
297
|
+
// streaming trees (where the eager snapshot would have been incomplete
|
|
298
|
+
// because loading() handlers were still in flight). Either way it is
|
|
299
|
+
// present by the time a loader reads a handle; the fresh build is only
|
|
300
|
+
// a defensive fallback.
|
|
287
301
|
const snapshot =
|
|
288
302
|
reqCtx._renderBarrierHandleSnapshot ??
|
|
289
303
|
buildHandleSnapshot(reqCtx._handleStore, segmentOrder);
|
|
@@ -305,15 +319,7 @@ function createLoaderExecutor<TEnv>(
|
|
|
305
319
|
);
|
|
306
320
|
}
|
|
307
321
|
|
|
308
|
-
// Guard: reject streaming trees
|
|
309
322
|
const reqCtx = reqCtxRef ?? _getRequestContext();
|
|
310
|
-
if (reqCtx?._treeHasStreaming) {
|
|
311
|
-
throw new Error(
|
|
312
|
-
`ctx.rendered() is not supported when the matched route tree uses loading(). ` +
|
|
313
|
-
`Streaming handlers may not have settled when rendered() resolves. ` +
|
|
314
|
-
`Remove loading() from the route tree or restructure to avoid rendered().`,
|
|
315
|
-
);
|
|
316
|
-
}
|
|
317
323
|
|
|
318
324
|
if (renderedPromise) return renderedPromise;
|
|
319
325
|
|
|
@@ -324,7 +330,10 @@ function createLoaderExecutor<TEnv>(
|
|
|
324
330
|
}
|
|
325
331
|
|
|
326
332
|
// Bidirectional deadlock check: if a handler already started
|
|
327
|
-
// awaiting this loader, calling rendered() would deadlock.
|
|
333
|
+
// awaiting this loader, calling rendered() would deadlock. This is the
|
|
334
|
+
// real cycle guard (it holds for both streaming and non-streaming): the
|
|
335
|
+
// handler blocks segment resolution, which blocks the barrier, which
|
|
336
|
+
// blocks this loader.
|
|
328
337
|
if (reqCtx._handlerLoaderDeps?.has(currentLoaderId)) {
|
|
329
338
|
throw new Error(
|
|
330
339
|
`Deadlock: loader "${currentLoaderId}" called ctx.rendered() but a handler ` +
|
|
@@ -342,7 +351,29 @@ function createLoaderExecutor<TEnv>(
|
|
|
342
351
|
}
|
|
343
352
|
reqCtx._renderBarrierWaiters.add(currentLoaderId);
|
|
344
353
|
|
|
345
|
-
|
|
354
|
+
// Streaming trees (loading()): the barrier resolves once the segment
|
|
355
|
+
// tree is resolved, but loading() handlers stream behind Suspense and
|
|
356
|
+
// their handle pushes are still in flight then. Their async execution
|
|
357
|
+
// IS tracked in the handle store (trackHandler -> store.track), so after
|
|
358
|
+
// the barrier we seal (no further handlers register once the tree is
|
|
359
|
+
// resolved) and wait for settled — every tracked handler, streaming
|
|
360
|
+
// included, has finished pushing. The loader's own segment streams in
|
|
361
|
+
// after, so this does not block the shell; the deadlock guard above
|
|
362
|
+
// keeps a handler from depending on this loader.
|
|
363
|
+
const streaming = reqCtx._treeHasStreaming === true;
|
|
364
|
+
renderedPromise = reqCtx._renderBarrier.then(async () => {
|
|
365
|
+
if (streaming) {
|
|
366
|
+
reqCtx._handleStore.seal();
|
|
367
|
+
await reqCtx._handleStore.settled;
|
|
368
|
+
// The eager snapshot was intentionally left unbuilt for streaming
|
|
369
|
+
// (it would have been incomplete). Build the complete one once, now
|
|
370
|
+
// that the store has settled, so every ctx.use(handle) reads the
|
|
371
|
+
// cached snapshot instead of rebuilding it per call.
|
|
372
|
+
reqCtx._renderBarrierHandleSnapshot ??= buildHandleSnapshot(
|
|
373
|
+
reqCtx._handleStore,
|
|
374
|
+
reqCtx._renderBarrierSegmentOrder ?? [],
|
|
375
|
+
);
|
|
376
|
+
}
|
|
346
377
|
renderedResolved = true;
|
|
347
378
|
});
|
|
348
379
|
return renderedPromise;
|
|
@@ -350,8 +381,19 @@ function createLoaderExecutor<TEnv>(
|
|
|
350
381
|
};
|
|
351
382
|
|
|
352
383
|
const doneLoader = track(`loader:${loader.$$id}`, 2);
|
|
384
|
+
// Run the loader body inside loader scope so request-scoped reads
|
|
385
|
+
// (cookies()/headers() and non-cacheable ctx.get) are exempt from the
|
|
386
|
+
// cache-purity guards: loaders always run fresh, so their reads never leak
|
|
387
|
+
// into a cached segment. DSL loaders are already wrapped by fresh.ts; this
|
|
388
|
+
// also covers handler-invoked loaders (ctx.use(Loader) from a handler),
|
|
389
|
+
// which otherwise execute in the caller's cache scope and would wrongly
|
|
390
|
+
// throw. rendered() gating uses the captured isDslLoader (above), so this
|
|
391
|
+
// does not grant rendered() to handler-invoked loaders. Uses a body-only
|
|
392
|
+
// scope, so isInsideLoaderScope() / barrier / deadlock gating is unchanged.
|
|
353
393
|
const promise = Promise.resolve(
|
|
354
|
-
|
|
394
|
+
runInsideLoaderBodyScope(() =>
|
|
395
|
+
loaderFn(loaderCtx as LoaderContext<any, TEnv>),
|
|
396
|
+
),
|
|
355
397
|
).finally(() => {
|
|
356
398
|
pendingLoaders.delete(loader.$$id);
|
|
357
399
|
doneLoader();
|
|
@@ -387,12 +429,6 @@ export function setupLoaderAccess<TEnv>(
|
|
|
387
429
|
|
|
388
430
|
const useLoader = createLoaderExecutor(ctx, loaderPromises);
|
|
389
431
|
|
|
390
|
-
// Track whether we're inside a handle push callback. Loaders started
|
|
391
|
-
// from push callbacks (e.g. push(async () => ctx.use(Loader))) do NOT
|
|
392
|
-
// block segment resolution, so they must not be registered as handler
|
|
393
|
-
// dependencies for deadlock detection.
|
|
394
|
-
let insideHandlePush = false;
|
|
395
|
-
|
|
396
432
|
ctx.use = ((item: LoaderDefinition<any, any> | Handle<any, any>) => {
|
|
397
433
|
if (isHandle(item)) {
|
|
398
434
|
const handle = item;
|
|
@@ -413,15 +449,17 @@ export function setupLoaderAccess<TEnv>(
|
|
|
413
449
|
if (!store) return;
|
|
414
450
|
|
|
415
451
|
if (typeof dataOrFn === "function") {
|
|
416
|
-
//
|
|
417
|
-
//
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
452
|
+
// Run the callback inside the push-callback scope so ctx.use(loader)
|
|
453
|
+
// calls it makes — including after its own awaits, for an async
|
|
454
|
+
// callback — are not registered as handler-to-loader deps and do not
|
|
455
|
+
// trip the deadlock guard. A pushed promise value is not tracked by
|
|
456
|
+
// handleStore.settled and does not block segment resolution, so it
|
|
457
|
+
// cannot form a rendered() deadlock. The ALS scope (not a plain
|
|
458
|
+
// boolean) is what survives the callback's awaits.
|
|
459
|
+
const result = runInsidePushCallbackScope(() =>
|
|
460
|
+
(dataOrFn as () => Promise<unknown>)(),
|
|
461
|
+
);
|
|
462
|
+
store.push(handle.$$id, segmentId, result);
|
|
425
463
|
return;
|
|
426
464
|
}
|
|
427
465
|
|
|
@@ -433,9 +471,12 @@ export function setupLoaderAccess<TEnv>(
|
|
|
433
471
|
// Skip when inside a DSL loader scope (resolveLoaderData also calls
|
|
434
472
|
// ctx.use() but that's DSL-to-DSL, not handler-to-loader) or when
|
|
435
473
|
// inside a handle push callback (push callbacks don't block segment
|
|
436
|
-
// resolution so they can't cause rendered() deadlocks).
|
|
474
|
+
// resolution so they can't cause rendered() deadlocks). The push-callback
|
|
475
|
+
// check is an ALS scope so it also exempts an ASYNC callback's continuation
|
|
476
|
+
// after its first await — relevant on streaming trees, where the guard
|
|
477
|
+
// state now stays live until handleStore.settled.
|
|
437
478
|
const loader = item as LoaderDefinition<any, any>;
|
|
438
|
-
if (!isInsideLoaderScope() && !
|
|
479
|
+
if (!isInsideLoaderScope() && !isInsidePushCallbackScope()) {
|
|
439
480
|
const reqCtx = reqCtxRef ?? _getRequestContext();
|
|
440
481
|
if (reqCtx) {
|
|
441
482
|
// Direction 1: handler awaits loader that already called rendered()
|
|
@@ -449,13 +490,18 @@ export function setupLoaderAccess<TEnv>(
|
|
|
449
490
|
`Move the data dependency to a loader-to-loader pattern instead.`,
|
|
450
491
|
);
|
|
451
492
|
}
|
|
452
|
-
// Direction 2: track dep so rendered() can detect the deadlock
|
|
453
|
-
//
|
|
454
|
-
//
|
|
455
|
-
//
|
|
456
|
-
//
|
|
457
|
-
//
|
|
458
|
-
|
|
493
|
+
// Direction 2: track dep so rendered() can detect the deadlock if the
|
|
494
|
+
// loader calls it later. Skip once the guard window is CLOSED — for a
|
|
495
|
+
// non-streaming tree that is when the barrier resolves (rendered()
|
|
496
|
+
// resolves immediately), and for a streaming tree it is when
|
|
497
|
+
// handleStore.settled completes (rendered() keeps waiting until then, so
|
|
498
|
+
// a loading() handler resuming after the barrier can still form a
|
|
499
|
+
// cycle). Using the explicit guard-closed flag rather than
|
|
500
|
+
// _renderBarrierSegmentOrder keeps tracking live across the streaming
|
|
501
|
+
// settle wait. (Handle push callbacks are already excluded above via
|
|
502
|
+
// isInsidePushCallbackScope(), so they cannot produce false positives
|
|
503
|
+
// here.)
|
|
504
|
+
if (!reqCtx._renderBarrierGuardClosed) {
|
|
459
505
|
if (!reqCtx._handlerLoaderDeps) reqCtx._handlerLoaderDeps = new Set();
|
|
460
506
|
reqCtx._handlerLoaderDeps.add(loader.$$id);
|
|
461
507
|
}
|
package/src/router/manifest.ts
CHANGED
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
type MetricsStore,
|
|
15
15
|
} from "../server/context";
|
|
16
16
|
import MapRootLayout from "../server/root-layout";
|
|
17
|
+
import { joinPrefix } from "./pattern-matching.js";
|
|
17
18
|
import type { RouteEntry } from "../types";
|
|
18
19
|
import type { UrlPatterns } from "../urls";
|
|
19
20
|
import { VERSION } from "@rangojs/router:version";
|
|
@@ -23,10 +24,17 @@ import { VERSION } from "@rangojs/router:version";
|
|
|
23
24
|
// stable references), so the resulting EntryData tree can be safely cached and reused
|
|
24
25
|
// across requests within the same isolate.
|
|
25
26
|
//
|
|
26
|
-
// Cache is keyed by (VERSION, mountIndex, routeKey, isSSR).
|
|
27
|
+
// Cache is keyed by (VERSION, routerId, mountIndex, routeKey, isSSR). routeKey is
|
|
28
|
+
// REQUIRED in the key: loadManifest() runs the handler with forRoute=routeKey, and
|
|
29
|
+
// path-helper.ts prunes (skips registering) every route except forRoute, so the
|
|
30
|
+
// resulting Store.manifest is pruned to the requested route — NOT the full include.
|
|
31
|
+
// Dropping routeKey would make a sibling route miss and overwrite this entry with its
|
|
32
|
+
// own pruned manifest, so alternating sibling requests would thrash (re-run the
|
|
33
|
+
// handler every time). Running the include handler once per isolate instead of once
|
|
34
|
+
// per route is possible but needs an unpruned manifest cache with prune-on-read — see
|
|
35
|
+
// LP1 in docs/internal/matching-and-lazy-discovery.md. VERSION comes from the
|
|
27
36
|
// @rangojs/router:version virtual module which Vite invalidates on RSC module HMR.
|
|
28
37
|
// When VERSION changes, this module re-evaluates and the cache is recreated empty.
|
|
29
|
-
// Including VERSION in the key is additional defense against stale entries.
|
|
30
38
|
const manifestModuleCache = new Map<string, Map<string, EntryData>>();
|
|
31
39
|
|
|
32
40
|
/**
|
|
@@ -34,8 +42,8 @@ const manifestModuleCache = new Map<string, Map<string, EntryData>>();
|
|
|
34
42
|
* Handles lazy imports, unwrapping, and validation
|
|
35
43
|
*
|
|
36
44
|
* Results are cached at module level after first execution. Subsequent calls
|
|
37
|
-
* for the same (routeKey, isSSR) within the same isolate
|
|
38
|
-
* without re-executing the DSL handler.
|
|
45
|
+
* for the same (routerId, mountIndex, routeKey, isSSR) within the same isolate
|
|
46
|
+
* return cached data without re-executing the DSL handler.
|
|
39
47
|
*/
|
|
40
48
|
/**
|
|
41
49
|
* Clear the module-level manifest cache.
|
|
@@ -65,9 +73,11 @@ export async function loadManifest(
|
|
|
65
73
|
|
|
66
74
|
const mountIndex = entry.mountIndex;
|
|
67
75
|
|
|
68
|
-
// Check module-level cache (persists across requests within same isolate)
|
|
76
|
+
// Check module-level cache (persists across requests within same isolate).
|
|
69
77
|
// Include routerId so multi-router setups (host routing) don't share cached
|
|
70
78
|
// EntryData across routers with overlapping mountIndex + routeKey combinations.
|
|
79
|
+
// routeKey is in the key because loadManifest() builds a manifest pruned to
|
|
80
|
+
// forRoute=routeKey (see path-helper.ts) — see the cache comment above.
|
|
71
81
|
const cacheKey = `${VERSION}:${entry.routerId ?? ""}:${mountIndex ?? ""}:${routeKey}:${isSSR ? 1 : 0}`;
|
|
72
82
|
const cached = manifestModuleCache.get(cacheKey);
|
|
73
83
|
if (cached) {
|
|
@@ -126,28 +136,37 @@ export async function loadManifest(
|
|
|
126
136
|
// were created during pattern extraction. This prevents shortCode
|
|
127
137
|
// collisions between lazy and non-lazy entries under the same parent
|
|
128
138
|
// (e.g., ArticlesLayout and BlogLayout both under NavLayout).
|
|
129
|
-
if (lazyContext
|
|
130
|
-
const
|
|
131
|
-
for (const [key, value] of Object.entries(captured)) {
|
|
139
|
+
if (lazyContext?.counters) {
|
|
140
|
+
for (const [key, value] of Object.entries(lazyContext.counters)) {
|
|
132
141
|
Store.counters[key] = Math.max(Store.counters[key] ?? 0, value);
|
|
133
142
|
}
|
|
134
143
|
}
|
|
135
144
|
|
|
136
145
|
// Propagate cache profiles for DSL-time cache("profileName") resolution.
|
|
137
146
|
// Non-lazy entries carry profiles directly; lazy entries carry them
|
|
138
|
-
// in the captured lazyContext from include() time.
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
147
|
+
// in the captured lazyContext from include() time. Always write
|
|
148
|
+
// (including clearing to undefined) so a prior lazy build's profile
|
|
149
|
+
// map cannot leak into a later non-lazy build on the same ALS-backed
|
|
150
|
+
// Store — which would otherwise let cache("name") resolve a profile
|
|
151
|
+
// from an unrelated entry.
|
|
152
|
+
Store.cacheProfiles = entry.cacheProfiles ?? lazyContext?.cacheProfiles;
|
|
144
153
|
|
|
145
154
|
// Propagate rootScoped from lazyContext so that routes inside
|
|
146
155
|
// nested { name: "sub" } under { name: "" } keep inherited root scope
|
|
147
|
-
// when the manifest is rebuilt on each request.
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
156
|
+
// when the manifest is rebuilt on each request. Always write
|
|
157
|
+
// (including clearing to undefined, which makes getRootScoped()
|
|
158
|
+
// return its true default) so a prior lazy build's scope cannot leak
|
|
159
|
+
// into a later non-lazy build on the same ALS-backed Store — which
|
|
160
|
+
// would otherwise mis-register plain routes as non-root-scoped and
|
|
161
|
+
// break dot-local reverse resolution.
|
|
162
|
+
Store.rootScoped = lazyContext?.rootScoped;
|
|
163
|
+
|
|
164
|
+
// Propagate includeScope from lazyContext so that direct-descendant
|
|
165
|
+
// shortCodes of this include use the correct scoped counter namespace
|
|
166
|
+
// on every manifest rebuild. Always write (including clearing to
|
|
167
|
+
// undefined) so a prior lazy build's scope cannot leak into a later
|
|
168
|
+
// non-lazy build on the same ALS-backed Store.
|
|
169
|
+
Store.includeScope = lazyContext?.includeScope;
|
|
151
170
|
|
|
152
171
|
const handlerExecStart = performance.now();
|
|
153
172
|
const useItems = await getContext().runWithStore(
|
|
@@ -167,7 +186,10 @@ export async function loadManifest(
|
|
|
167
186
|
if (entry.lazy && entry.lazyPatterns) {
|
|
168
187
|
const lazyPatterns = entry.lazyPatterns as UrlPatterns<any>;
|
|
169
188
|
const includePrefix = (entry as any)._lazyPrefix || "";
|
|
170
|
-
|
|
189
|
+
// Slash-collapsing join so a trailing-slash parent prefix does not
|
|
190
|
+
// bake a double slash into the registered route patterns (must match
|
|
191
|
+
// the same join in evaluateLazyEntry / the build-time runWithPrefixes).
|
|
192
|
+
const fullPrefix = joinPrefix(lazyContext?.urlPrefix, includePrefix);
|
|
171
193
|
|
|
172
194
|
if (fullPrefix || lazyContext?.namePrefix) {
|
|
173
195
|
return runWithPrefixes(fullPrefix, lazyContext?.namePrefix, () =>
|
package/src/router/match-api.ts
CHANGED
|
@@ -22,10 +22,10 @@ import { collectRouteMiddleware } from "./middleware.js";
|
|
|
22
22
|
import { traverseBack } from "./pattern-matching.js";
|
|
23
23
|
import { DefaultErrorFallback } from "../default-error-boundary.js";
|
|
24
24
|
import {
|
|
25
|
-
EntryData,
|
|
26
|
-
LoaderEntry,
|
|
25
|
+
type EntryData,
|
|
26
|
+
type LoaderEntry,
|
|
27
27
|
getContext,
|
|
28
|
-
InterceptSelectorContext,
|
|
28
|
+
type InterceptSelectorContext,
|
|
29
29
|
} from "../server/context";
|
|
30
30
|
import type { ErrorBoundaryHandler, ErrorInfo, MatchResult } from "../types";
|
|
31
31
|
import type { ReactNode } from "react";
|
|
@@ -550,6 +550,7 @@ export async function matchError<TEnv>(
|
|
|
550
550
|
segments: [errorSegment],
|
|
551
551
|
matched: matchedIds,
|
|
552
552
|
diff: [errorSegment.id],
|
|
553
|
+
resolvedIds: [errorSegment.id],
|
|
553
554
|
params: matched.params,
|
|
554
555
|
};
|
|
555
556
|
}
|