@rangojs/router 0.0.0-experimental.259 → 0.0.0-experimental.26
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 +294 -28
- package/dist/bin/rango.js +355 -47
- package/dist/vite/index.js +1658 -1239
- package/package.json +3 -3
- package/skills/cache-guide/SKILL.md +9 -5
- package/skills/caching/SKILL.md +4 -4
- package/skills/document-cache/SKILL.md +2 -2
- package/skills/hooks/SKILL.md +40 -29
- package/skills/host-router/SKILL.md +218 -0
- package/skills/intercept/SKILL.md +79 -0
- package/skills/layout/SKILL.md +62 -2
- package/skills/loader/SKILL.md +229 -15
- package/skills/middleware/SKILL.md +109 -30
- package/skills/parallel/SKILL.md +57 -2
- package/skills/prerender/SKILL.md +189 -19
- package/skills/rango/SKILL.md +1 -2
- package/skills/response-routes/SKILL.md +3 -3
- package/skills/route/SKILL.md +44 -3
- package/skills/router-setup/SKILL.md +80 -3
- package/skills/theme/SKILL.md +5 -4
- package/skills/typesafety/SKILL.md +59 -16
- package/skills/use-cache/SKILL.md +16 -2
- package/src/__internal.ts +1 -1
- package/src/bin/rango.ts +56 -19
- package/src/browser/action-coordinator.ts +97 -0
- package/src/browser/event-controller.ts +29 -48
- package/src/browser/history-state.ts +80 -0
- package/src/browser/intercept-utils.ts +1 -1
- package/src/browser/link-interceptor.ts +19 -3
- package/src/browser/merge-segment-loaders.ts +9 -2
- package/src/browser/navigation-bridge.ts +66 -443
- package/src/browser/navigation-client.ts +34 -62
- package/src/browser/navigation-store.ts +4 -33
- package/src/browser/navigation-transaction.ts +295 -0
- package/src/browser/partial-update.ts +103 -151
- package/src/browser/prefetch/cache.ts +67 -0
- package/src/browser/prefetch/fetch.ts +137 -0
- package/src/browser/prefetch/observer.ts +65 -0
- package/src/browser/prefetch/policy.ts +42 -0
- package/src/browser/prefetch/queue.ts +88 -0
- package/src/browser/rango-state.ts +112 -0
- package/src/browser/react/Link.tsx +154 -44
- package/src/browser/react/NavigationProvider.tsx +32 -0
- package/src/browser/react/context.ts +6 -0
- package/src/browser/react/filter-segment-order.ts +11 -0
- package/src/browser/react/index.ts +2 -6
- package/src/browser/react/location-state-shared.ts +29 -11
- package/src/browser/react/location-state.ts +6 -4
- 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 +23 -45
- package/src/browser/react/use-client-cache.ts +5 -3
- package/src/browser/react/use-handle.ts +21 -64
- package/src/browser/react/use-navigation.ts +7 -32
- package/src/browser/react/use-params.ts +5 -34
- package/src/browser/react/use-pathname.ts +2 -3
- package/src/browser/react/use-router.ts +3 -6
- package/src/browser/react/use-search-params.ts +2 -1
- package/src/browser/react/use-segments.ts +75 -114
- package/src/browser/response-adapter.ts +73 -0
- package/src/browser/rsc-router.tsx +46 -22
- package/src/browser/scroll-restoration.ts +10 -7
- package/src/browser/server-action-bridge.ts +458 -405
- package/src/browser/types.ts +21 -35
- package/src/browser/validate-redirect-origin.ts +29 -0
- package/src/build/generate-manifest.ts +38 -13
- package/src/build/generate-route-types.ts +4 -0
- package/src/build/index.ts +1 -0
- package/src/build/route-trie.ts +19 -3
- package/src/build/route-types/codegen.ts +13 -4
- package/src/build/route-types/include-resolution.ts +13 -0
- package/src/build/route-types/per-module-writer.ts +15 -3
- package/src/build/route-types/router-processing.ts +170 -18
- package/src/build/runtime-discovery.ts +13 -1
- 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 +136 -123
- package/src/cache/cache-scope.ts +76 -83
- package/src/cache/cf/cf-cache-store.ts +12 -7
- package/src/cache/document-cache.ts +93 -69
- package/src/cache/handle-capture.ts +81 -0
- package/src/cache/index.ts +0 -15
- package/src/cache/memory-segment-store.ts +43 -69
- package/src/cache/profile-registry.ts +43 -8
- package/src/cache/read-through-swr.ts +134 -0
- package/src/cache/segment-codec.ts +140 -117
- package/src/cache/taint.ts +30 -3
- package/src/cache/types.ts +1 -115
- package/src/client.rsc.tsx +0 -1
- package/src/client.tsx +53 -76
- package/src/errors.ts +6 -1
- package/src/handle.ts +1 -1
- package/src/handles/MetaTags.tsx +5 -2
- package/src/host/cookie-handler.ts +8 -3
- package/src/host/index.ts +0 -3
- package/src/host/router.ts +14 -1
- package/src/href-client.ts +3 -1
- package/src/index.rsc.ts +53 -10
- package/src/index.ts +73 -43
- package/src/loader.rsc.ts +12 -4
- package/src/loader.ts +8 -0
- package/src/prerender/store.ts +60 -18
- package/src/prerender.ts +76 -18
- package/src/reverse.ts +11 -7
- package/src/root-error-boundary.tsx +30 -26
- package/src/route-definition/dsl-helpers.ts +9 -6
- package/src/route-definition/index.ts +0 -3
- package/src/route-definition/redirect.ts +15 -3
- package/src/route-map-builder.ts +38 -2
- package/src/route-name.ts +53 -0
- package/src/route-types.ts +7 -0
- package/src/router/content-negotiation.ts +1 -1
- package/src/router/debug-manifest.ts +16 -3
- package/src/router/handler-context.ts +96 -17
- package/src/router/intercept-resolution.ts +6 -4
- package/src/router/lazy-includes.ts +4 -0
- package/src/router/loader-resolution.ts +6 -11
- package/src/router/logging.ts +100 -3
- package/src/router/manifest.ts +32 -3
- package/src/router/match-api.ts +62 -54
- package/src/router/match-context.ts +3 -0
- package/src/router/match-handlers.ts +185 -11
- package/src/router/match-middleware/background-revalidation.ts +65 -85
- package/src/router/match-middleware/cache-lookup.ts +78 -10
- package/src/router/match-middleware/cache-store.ts +2 -0
- package/src/router/match-pipelines.ts +8 -43
- package/src/router/match-result.ts +0 -9
- package/src/router/metrics.ts +233 -13
- package/src/router/middleware-types.ts +34 -39
- package/src/router/middleware.ts +290 -130
- package/src/router/pattern-matching.ts +61 -10
- package/src/router/prerender-match.ts +36 -6
- package/src/router/preview-match.ts +7 -1
- package/src/router/revalidation.ts +61 -2
- package/src/router/router-context.ts +15 -0
- package/src/router/router-interfaces.ts +158 -40
- package/src/router/router-options.ts +223 -1
- package/src/router/router-registry.ts +5 -2
- package/src/router/segment-resolution/fresh.ts +165 -242
- package/src/router/segment-resolution/helpers.ts +263 -0
- package/src/router/segment-resolution/loader-cache.ts +102 -98
- package/src/router/segment-resolution/revalidation.ts +394 -272
- package/src/router/segment-resolution/static-store.ts +2 -2
- package/src/router/segment-resolution.ts +1 -3
- package/src/router/segment-wrappers.ts +3 -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 +20 -2
- package/src/router/types.ts +7 -1
- package/src/router.ts +203 -18
- package/src/rsc/handler-context.ts +13 -2
- package/src/rsc/handler.ts +489 -438
- package/src/rsc/helpers.ts +125 -5
- package/src/rsc/index.ts +0 -20
- package/src/rsc/loader-fetch.ts +84 -42
- package/src/rsc/manifest-init.ts +3 -2
- package/src/rsc/origin-guard.ts +141 -0
- package/src/rsc/progressive-enhancement.ts +245 -19
- package/src/rsc/response-route-handler.ts +347 -0
- package/src/rsc/rsc-rendering.ts +47 -43
- package/src/rsc/runtime-warnings.ts +42 -0
- package/src/rsc/server-action.ts +166 -66
- package/src/rsc/ssr-setup.ts +128 -0
- package/src/rsc/types.ts +20 -2
- package/src/search-params.ts +38 -23
- package/src/server/context.ts +61 -7
- package/src/server/cookie-store.ts +190 -0
- package/src/server/fetchable-loader-store.ts +11 -6
- package/src/server/handle-store.ts +84 -12
- package/src/server/loader-registry.ts +11 -46
- package/src/server/request-context.ts +275 -49
- package/src/server.ts +6 -0
- package/src/ssr/index.tsx +67 -28
- package/src/static-handler.ts +7 -0
- package/src/theme/ThemeProvider.tsx +6 -1
- package/src/theme/index.ts +4 -18
- package/src/theme/theme-context.ts +1 -28
- package/src/theme/theme-script.ts +2 -1
- package/src/types/cache-types.ts +6 -1
- package/src/types/error-types.ts +3 -0
- package/src/types/global-namespace.ts +22 -0
- package/src/types/handler-context.ts +103 -16
- package/src/types/index.ts +1 -1
- package/src/types/loader-types.ts +9 -6
- package/src/types/route-config.ts +17 -26
- package/src/types/route-entry.ts +28 -0
- package/src/types/segments.ts +0 -5
- package/src/urls/include-helper.ts +49 -8
- package/src/urls/index.ts +1 -0
- package/src/urls/path-helper-types.ts +30 -12
- package/src/urls/path-helper.ts +17 -2
- package/src/urls/pattern-types.ts +21 -1
- package/src/urls/response-types.ts +29 -7
- package/src/urls/type-extraction.ts +23 -15
- package/src/use-loader.tsx +27 -9
- package/src/vite/discovery/bundle-postprocess.ts +32 -52
- package/src/vite/discovery/discover-routers.ts +52 -26
- package/src/vite/discovery/prerender-collection.ts +58 -41
- package/src/vite/discovery/route-types-writer.ts +7 -7
- package/src/vite/discovery/state.ts +7 -7
- package/src/vite/discovery/virtual-module-codegen.ts +5 -2
- package/src/vite/index.ts +10 -51
- package/src/vite/plugins/client-ref-dedup.ts +115 -0
- package/src/vite/plugins/client-ref-hashing.ts +3 -3
- package/src/vite/plugins/expose-internal-ids.ts +4 -3
- package/src/vite/plugins/refresh-cmd.ts +65 -0
- package/src/vite/plugins/use-cache-transform.ts +91 -3
- package/src/vite/plugins/version-plugin.ts +188 -18
- package/src/vite/rango.ts +61 -36
- package/src/vite/router-discovery.ts +173 -100
- package/src/vite/utils/prerender-utils.ts +81 -0
- package/src/vite/utils/shared-utils.ts +19 -9
- package/skills/testing/SKILL.md +0 -226
- package/src/browser/lru-cache.ts +0 -61
- package/src/browser/react/prefetch.ts +0 -27
- 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/route-definition/route-function.ts +0 -119
- package/src/router.gen.ts +0 -6
- package/src/static-handler.gen.ts +0 -5
- package/src/urls.gen.ts +0 -8
- /package/{CLAUDE.md → AGENTS.md} +0 -0
|
@@ -7,23 +7,14 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import type { ReactNode } from "react";
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
createErrorInfo,
|
|
13
|
-
createErrorSegment,
|
|
14
|
-
createNotFoundInfo,
|
|
15
|
-
createNotFoundSegment,
|
|
16
|
-
} from "../error-handling.js";
|
|
10
|
+
import { invariant } from "../../errors";
|
|
17
11
|
import { revalidate } from "../loader-resolution.js";
|
|
18
12
|
import { evaluateRevalidation } from "../revalidation.js";
|
|
19
|
-
import { getRequestContext } from "../../server/request-context.js";
|
|
20
|
-
import { DefaultErrorFallback } from "../../default-error-boundary.js";
|
|
21
13
|
import type { EntryData } from "../../server/context";
|
|
22
14
|
import type {
|
|
23
15
|
HandlerContext,
|
|
24
16
|
InternalHandlerContext,
|
|
25
17
|
ResolvedSegment,
|
|
26
|
-
ErrorInfo,
|
|
27
18
|
ShouldRevalidateFn,
|
|
28
19
|
} from "../../types";
|
|
29
20
|
import type {
|
|
@@ -31,10 +22,98 @@ import type {
|
|
|
31
22
|
SegmentRevalidationResult,
|
|
32
23
|
ActionContext,
|
|
33
24
|
} from "../types.js";
|
|
34
|
-
import {
|
|
35
|
-
|
|
36
|
-
|
|
25
|
+
import {
|
|
26
|
+
debugLog,
|
|
27
|
+
pushRevalidationTraceEntry,
|
|
28
|
+
isTraceActive,
|
|
29
|
+
} from "../logging.js";
|
|
37
30
|
import { resolveLoaderData } from "./loader-cache.js";
|
|
31
|
+
import {
|
|
32
|
+
handleHandlerResult,
|
|
33
|
+
tryStaticHandler,
|
|
34
|
+
tryStaticSlot,
|
|
35
|
+
resolveLayoutComponent,
|
|
36
|
+
resolveWithErrorBoundary,
|
|
37
|
+
} from "./helpers.js";
|
|
38
|
+
import { getRouterContext } from "../router-context.js";
|
|
39
|
+
import { resolveSink, safeEmit } from "../telemetry.js";
|
|
40
|
+
import { track } from "../../server/context.js";
|
|
41
|
+
|
|
42
|
+
// ---------------------------------------------------------------------------
|
|
43
|
+
// Telemetry helpers
|
|
44
|
+
// ---------------------------------------------------------------------------
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Attach a fire-and-forget rejection observer to a streamed handler promise.
|
|
48
|
+
* Silently no-ops when called outside RouterContext (e.g. in unit tests).
|
|
49
|
+
*/
|
|
50
|
+
function observeStreamedHandler(
|
|
51
|
+
promise: Promise<ReactNode>,
|
|
52
|
+
segmentId: string,
|
|
53
|
+
segmentType: string,
|
|
54
|
+
pathname?: string,
|
|
55
|
+
routeKey?: string,
|
|
56
|
+
params?: Record<string, string>,
|
|
57
|
+
): void {
|
|
58
|
+
let routerCtx;
|
|
59
|
+
try {
|
|
60
|
+
routerCtx = getRouterContext();
|
|
61
|
+
} catch {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
if (!routerCtx?.telemetry) return;
|
|
65
|
+
const sink = resolveSink(routerCtx.telemetry);
|
|
66
|
+
const reqId = routerCtx.requestId;
|
|
67
|
+
promise.catch((err: unknown) => {
|
|
68
|
+
const errorObj = err instanceof Error ? err : new Error(String(err));
|
|
69
|
+
safeEmit(sink, {
|
|
70
|
+
type: "handler.error",
|
|
71
|
+
timestamp: performance.now(),
|
|
72
|
+
requestId: reqId,
|
|
73
|
+
segmentId,
|
|
74
|
+
segmentType,
|
|
75
|
+
error: errorObj,
|
|
76
|
+
handledByBoundary: true,
|
|
77
|
+
pathname,
|
|
78
|
+
routeKey,
|
|
79
|
+
params,
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// ---------------------------------------------------------------------------
|
|
85
|
+
// Revalidation telemetry helper
|
|
86
|
+
// ---------------------------------------------------------------------------
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Emit revalidation.decision telemetry for a segment if a sink is configured.
|
|
90
|
+
* Called after evaluateRevalidation returns to capture the decision.
|
|
91
|
+
* Silently no-ops when called outside RouterContext (e.g. in unit tests).
|
|
92
|
+
*/
|
|
93
|
+
function emitRevalidationDecision(
|
|
94
|
+
segmentId: string,
|
|
95
|
+
pathname: string,
|
|
96
|
+
routeKey: string,
|
|
97
|
+
shouldRevalidate: boolean,
|
|
98
|
+
): void {
|
|
99
|
+
let routerCtx;
|
|
100
|
+
try {
|
|
101
|
+
routerCtx = getRouterContext();
|
|
102
|
+
} catch {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
if (routerCtx?.telemetry) {
|
|
106
|
+
safeEmit(resolveSink(routerCtx.telemetry), {
|
|
107
|
+
type: "revalidation.decision",
|
|
108
|
+
timestamp: performance.now(),
|
|
109
|
+
requestId: routerCtx.requestId,
|
|
110
|
+
segmentId,
|
|
111
|
+
pathname,
|
|
112
|
+
routeKey,
|
|
113
|
+
shouldRevalidate,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
}
|
|
38
117
|
|
|
39
118
|
// ---------------------------------------------------------------------------
|
|
40
119
|
// Revalidation path (partial match)
|
|
@@ -85,7 +164,20 @@ export async function resolveLoadersWithRevalidation<TEnv>(
|
|
|
85
164
|
}) => {
|
|
86
165
|
const shouldRun = await revalidate(
|
|
87
166
|
async () => {
|
|
88
|
-
if (!clientSegmentIds.has(segmentId))
|
|
167
|
+
if (!clientSegmentIds.has(segmentId)) {
|
|
168
|
+
if (isTraceActive()) {
|
|
169
|
+
pushRevalidationTraceEntry({
|
|
170
|
+
segmentId,
|
|
171
|
+
segmentType: "loader",
|
|
172
|
+
belongsToRoute,
|
|
173
|
+
source: "loader",
|
|
174
|
+
defaultShouldRevalidate: true,
|
|
175
|
+
finalShouldRevalidate: true,
|
|
176
|
+
reason: "new-segment",
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
return true;
|
|
180
|
+
}
|
|
89
181
|
|
|
90
182
|
const dummySegment: ResolvedSegment = {
|
|
91
183
|
id: segmentId,
|
|
@@ -113,11 +205,13 @@ export async function resolveLoadersWithRevalidation<TEnv>(
|
|
|
113
205
|
context: ctx,
|
|
114
206
|
actionContext,
|
|
115
207
|
stale,
|
|
208
|
+
traceSource: "loader",
|
|
116
209
|
});
|
|
117
210
|
},
|
|
118
211
|
async () => true,
|
|
119
212
|
() => false,
|
|
120
213
|
);
|
|
214
|
+
emitRevalidationDecision(segmentId, ctx.pathname, routeKey, shouldRun);
|
|
121
215
|
return { shouldRun, loaderEntry, loader, segmentId, index };
|
|
122
216
|
},
|
|
123
217
|
),
|
|
@@ -160,6 +254,7 @@ export async function resolveLoadersOnlyWithRevalidation<TEnv>(
|
|
|
160
254
|
routeKey: string,
|
|
161
255
|
deps: SegmentResolutionDeps<TEnv>,
|
|
162
256
|
actionContext?: ActionContext,
|
|
257
|
+
stale?: boolean,
|
|
163
258
|
): Promise<{ segments: ResolvedSegment[]; matchedIds: string[] }> {
|
|
164
259
|
const allLoaderSegments: ResolvedSegment[] = [];
|
|
165
260
|
const allMatchedIds: string[] = [];
|
|
@@ -178,6 +273,8 @@ export async function resolveLoadersOnlyWithRevalidation<TEnv>(
|
|
|
178
273
|
routeKey,
|
|
179
274
|
deps,
|
|
180
275
|
actionContext,
|
|
276
|
+
undefined, // shortCodeOverride
|
|
277
|
+
stale,
|
|
181
278
|
);
|
|
182
279
|
allLoaderSegments.push(...segments);
|
|
183
280
|
allMatchedIds.push(...matchedIds);
|
|
@@ -283,9 +380,35 @@ export async function resolveParallelSegmentsWithRevalidation<TEnv>(
|
|
|
283
380
|
}
|
|
284
381
|
|
|
285
382
|
const shouldResolve = await (async () => {
|
|
286
|
-
if (isFullRefetch)
|
|
287
|
-
|
|
288
|
-
|
|
383
|
+
if (isFullRefetch) {
|
|
384
|
+
if (isTraceActive()) {
|
|
385
|
+
pushRevalidationTraceEntry({
|
|
386
|
+
segmentId: parallelId,
|
|
387
|
+
segmentType: "parallel",
|
|
388
|
+
belongsToRoute,
|
|
389
|
+
source: "parallel",
|
|
390
|
+
defaultShouldRevalidate: true,
|
|
391
|
+
finalShouldRevalidate: true,
|
|
392
|
+
reason: "full-refetch",
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
return true;
|
|
396
|
+
}
|
|
397
|
+
if (!clientSegmentIds.has(parallelId)) {
|
|
398
|
+
const result = belongsToRoute || isNewParent;
|
|
399
|
+
if (isTraceActive()) {
|
|
400
|
+
pushRevalidationTraceEntry({
|
|
401
|
+
segmentId: parallelId,
|
|
402
|
+
segmentType: "parallel",
|
|
403
|
+
belongsToRoute,
|
|
404
|
+
source: "parallel",
|
|
405
|
+
defaultShouldRevalidate: result,
|
|
406
|
+
finalShouldRevalidate: result,
|
|
407
|
+
reason: result ? "new-segment" : "skip-parent-chain",
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
return result;
|
|
411
|
+
}
|
|
289
412
|
|
|
290
413
|
const dummySegment: ResolvedSegment = {
|
|
291
414
|
id: parallelId,
|
|
@@ -317,14 +440,19 @@ export async function resolveParallelSegmentsWithRevalidation<TEnv>(
|
|
|
317
440
|
context,
|
|
318
441
|
actionContext,
|
|
319
442
|
stale,
|
|
443
|
+
traceSource: "parallel",
|
|
320
444
|
});
|
|
321
445
|
})();
|
|
446
|
+
emitRevalidationDecision(
|
|
447
|
+
parallelId,
|
|
448
|
+
context.pathname,
|
|
449
|
+
routeKey,
|
|
450
|
+
shouldResolve,
|
|
451
|
+
);
|
|
322
452
|
|
|
323
453
|
let component: ReactNode | undefined;
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
if (slotStaticId && shouldResolve) {
|
|
327
|
-
component = await tryStaticLookup(slotStaticId, parallelId);
|
|
454
|
+
if (shouldResolve) {
|
|
455
|
+
component = await tryStaticSlot(parallelEntry, slot, parallelId);
|
|
328
456
|
}
|
|
329
457
|
if (component === undefined) {
|
|
330
458
|
const hasLoadingFallback =
|
|
@@ -333,9 +461,25 @@ export async function resolveParallelSegmentsWithRevalidation<TEnv>(
|
|
|
333
461
|
if (!shouldResolve) {
|
|
334
462
|
component = null;
|
|
335
463
|
} else if (hasLoadingFallback) {
|
|
336
|
-
|
|
337
|
-
typeof handler === "function" ? handler(context) : handler
|
|
338
|
-
)
|
|
464
|
+
const result =
|
|
465
|
+
typeof handler === "function" ? handler(context) : handler;
|
|
466
|
+
if (result instanceof Promise) {
|
|
467
|
+
const tracked = deps.trackHandler(result, {
|
|
468
|
+
segmentId: parallelId,
|
|
469
|
+
segmentType: "parallel",
|
|
470
|
+
});
|
|
471
|
+
observeStreamedHandler(
|
|
472
|
+
tracked,
|
|
473
|
+
parallelId,
|
|
474
|
+
"parallel",
|
|
475
|
+
context.pathname,
|
|
476
|
+
routeKey,
|
|
477
|
+
params,
|
|
478
|
+
);
|
|
479
|
+
component = tracked as ReactNode;
|
|
480
|
+
} else {
|
|
481
|
+
component = result as ReactNode;
|
|
482
|
+
}
|
|
339
483
|
} else {
|
|
340
484
|
component =
|
|
341
485
|
typeof handler === "function" ? await handler(context) : handler;
|
|
@@ -413,7 +557,24 @@ export async function resolveEntryHandlerWithRevalidation<TEnv>(
|
|
|
413
557
|
clientHasSegment: hasSegment,
|
|
414
558
|
belongsToRoute,
|
|
415
559
|
});
|
|
416
|
-
if (!hasSegment)
|
|
560
|
+
if (!hasSegment) {
|
|
561
|
+
if (isTraceActive()) {
|
|
562
|
+
const segType =
|
|
563
|
+
entry.type === "cache"
|
|
564
|
+
? "layout"
|
|
565
|
+
: (entry.type as "layout" | "route");
|
|
566
|
+
pushRevalidationTraceEntry({
|
|
567
|
+
segmentId: entry.shortCode,
|
|
568
|
+
segmentType: segType,
|
|
569
|
+
belongsToRoute,
|
|
570
|
+
source: "segment-resolution",
|
|
571
|
+
defaultShouldRevalidate: true,
|
|
572
|
+
finalShouldRevalidate: true,
|
|
573
|
+
reason: "new-segment",
|
|
574
|
+
});
|
|
575
|
+
}
|
|
576
|
+
return true;
|
|
577
|
+
}
|
|
417
578
|
|
|
418
579
|
const dummySegment: ResolvedSegment = {
|
|
419
580
|
id: entry.shortCode,
|
|
@@ -448,6 +609,12 @@ export async function resolveEntryHandlerWithRevalidation<TEnv>(
|
|
|
448
609
|
actionContext,
|
|
449
610
|
stale,
|
|
450
611
|
});
|
|
612
|
+
emitRevalidationDecision(
|
|
613
|
+
entry.shortCode,
|
|
614
|
+
context.pathname,
|
|
615
|
+
routeKey,
|
|
616
|
+
shouldRevalidate,
|
|
617
|
+
);
|
|
451
618
|
debugLog("segment.revalidate", "entry revalidation decision", {
|
|
452
619
|
segmentId: entry.shortCode,
|
|
453
620
|
shouldRevalidate,
|
|
@@ -455,40 +622,55 @@ export async function resolveEntryHandlerWithRevalidation<TEnv>(
|
|
|
455
622
|
return shouldRevalidate;
|
|
456
623
|
},
|
|
457
624
|
async () => {
|
|
625
|
+
const doneHandler = track(`handler:${entry.id}`, 2);
|
|
458
626
|
(context as InternalHandlerContext<any, TEnv>)._currentSegmentId =
|
|
459
627
|
entry.shortCode;
|
|
460
|
-
// Static handler interception: use pre-rendered component from build-time store
|
|
461
|
-
const entryAny = entry as any;
|
|
462
|
-
if (entryAny.isStaticPrerender && entryAny.staticHandlerId) {
|
|
463
|
-
const staticComponent = await tryStaticLookup(
|
|
464
|
-
entryAny.staticHandlerId,
|
|
465
|
-
entry.shortCode,
|
|
466
|
-
);
|
|
467
|
-
if (staticComponent !== undefined) return staticComponent;
|
|
468
|
-
}
|
|
469
628
|
if (entry.type === "layout" || entry.type === "cache") {
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
629
|
+
const layoutComponent = await resolveLayoutComponent(entry, context);
|
|
630
|
+
doneHandler();
|
|
631
|
+
return layoutComponent;
|
|
632
|
+
}
|
|
633
|
+
const staticComponent = await tryStaticHandler(entry, entry.shortCode);
|
|
634
|
+
if (staticComponent !== undefined) {
|
|
635
|
+
doneHandler();
|
|
636
|
+
return staticComponent;
|
|
473
637
|
}
|
|
474
638
|
const routeEntry = entry as Extract<EntryData, { type: "route" }>;
|
|
475
639
|
if (!routeEntry.loading) {
|
|
476
|
-
|
|
640
|
+
const result = handleHandlerResult(await routeEntry.handler(context));
|
|
641
|
+
doneHandler();
|
|
642
|
+
return result;
|
|
477
643
|
}
|
|
478
644
|
if (!actionContext) {
|
|
479
645
|
const result = handleHandlerResult(routeEntry.handler(context));
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
646
|
+
if (result instanceof Promise) {
|
|
647
|
+
result.finally(doneHandler).catch(() => {});
|
|
648
|
+
const tracked = deps.trackHandler(result, {
|
|
649
|
+
segmentId: entry.shortCode,
|
|
650
|
+
segmentType: entry.type,
|
|
651
|
+
});
|
|
652
|
+
observeStreamedHandler(
|
|
653
|
+
tracked,
|
|
654
|
+
entry.shortCode,
|
|
655
|
+
entry.type,
|
|
656
|
+
context.pathname,
|
|
657
|
+
routeKey,
|
|
658
|
+
params,
|
|
659
|
+
);
|
|
660
|
+
return { content: tracked };
|
|
661
|
+
}
|
|
662
|
+
doneHandler();
|
|
663
|
+
return { content: result };
|
|
484
664
|
}
|
|
485
665
|
debugLog("segment.action", "resolving action route with awaited value", {
|
|
486
666
|
entryId: entry.id,
|
|
487
667
|
});
|
|
668
|
+
const actionResult = handleHandlerResult(
|
|
669
|
+
await routeEntry.handler(context),
|
|
670
|
+
);
|
|
671
|
+
doneHandler();
|
|
488
672
|
return {
|
|
489
|
-
content: Promise.resolve(
|
|
490
|
-
handleHandlerResult(await routeEntry.handler(context)),
|
|
491
|
-
),
|
|
673
|
+
content: Promise.resolve(actionResult),
|
|
492
674
|
};
|
|
493
675
|
},
|
|
494
676
|
() => null,
|
|
@@ -605,31 +787,32 @@ export async function resolveSegmentWithRevalidation<TEnv>(
|
|
|
605
787
|
}
|
|
606
788
|
}
|
|
607
789
|
|
|
608
|
-
const parallelResult = await resolveParallelSegmentsWithRevalidation(
|
|
609
|
-
entry,
|
|
610
|
-
params,
|
|
611
|
-
context,
|
|
612
|
-
belongsToRoute,
|
|
613
|
-
clientSegmentIds,
|
|
614
|
-
prevParams,
|
|
615
|
-
request,
|
|
616
|
-
prevUrl,
|
|
617
|
-
nextUrl,
|
|
618
|
-
routeKey,
|
|
619
|
-
deps,
|
|
620
|
-
actionContext,
|
|
621
|
-
stale,
|
|
622
|
-
);
|
|
623
|
-
segments.push(...parallelResult.segments);
|
|
624
|
-
matchedIds.push(...parallelResult.matchedIds);
|
|
625
|
-
|
|
626
|
-
// Push handler BEFORE orphan layouts for layout/cache entries (matching SSR
|
|
627
|
-
// order in resolveSegment). Route handler was already executed and is pushed
|
|
628
|
-
// after children for tree composition.
|
|
629
790
|
if (routeHandlerResult) {
|
|
791
|
+
// Route entry: handler already executed above; resolve parallels
|
|
792
|
+
// (handler data visible) then push handler segment last for tree order.
|
|
793
|
+
const parallelResult = await resolveParallelSegmentsWithRevalidation(
|
|
794
|
+
entry,
|
|
795
|
+
params,
|
|
796
|
+
context,
|
|
797
|
+
belongsToRoute,
|
|
798
|
+
clientSegmentIds,
|
|
799
|
+
prevParams,
|
|
800
|
+
request,
|
|
801
|
+
prevUrl,
|
|
802
|
+
nextUrl,
|
|
803
|
+
routeKey,
|
|
804
|
+
deps,
|
|
805
|
+
actionContext,
|
|
806
|
+
stale,
|
|
807
|
+
);
|
|
808
|
+
segments.push(...parallelResult.segments);
|
|
809
|
+
matchedIds.push(...parallelResult.matchedIds);
|
|
810
|
+
|
|
630
811
|
segments.push(routeHandlerResult.segment);
|
|
631
812
|
matchedIds.push(routeHandlerResult.matchedId);
|
|
632
813
|
} else {
|
|
814
|
+
// Layout/cache entry: handler-first — resolve handler before parallels
|
|
815
|
+
// so ctx.set() values are visible to parallel children.
|
|
633
816
|
const handlerResult = await resolveEntryHandlerWithRevalidation(
|
|
634
817
|
entry,
|
|
635
818
|
params,
|
|
@@ -647,9 +830,25 @@ export async function resolveSegmentWithRevalidation<TEnv>(
|
|
|
647
830
|
);
|
|
648
831
|
segments.push(handlerResult.segment);
|
|
649
832
|
matchedIds.push(handlerResult.matchedId);
|
|
650
|
-
}
|
|
651
833
|
|
|
652
|
-
|
|
834
|
+
const parallelResult = await resolveParallelSegmentsWithRevalidation(
|
|
835
|
+
entry,
|
|
836
|
+
params,
|
|
837
|
+
context,
|
|
838
|
+
belongsToRoute,
|
|
839
|
+
clientSegmentIds,
|
|
840
|
+
prevParams,
|
|
841
|
+
request,
|
|
842
|
+
prevUrl,
|
|
843
|
+
nextUrl,
|
|
844
|
+
routeKey,
|
|
845
|
+
deps,
|
|
846
|
+
actionContext,
|
|
847
|
+
stale,
|
|
848
|
+
);
|
|
849
|
+
segments.push(...parallelResult.segments);
|
|
850
|
+
matchedIds.push(...parallelResult.matchedIds);
|
|
851
|
+
|
|
653
852
|
for (const orphan of entry.layout) {
|
|
654
853
|
const orphanResult = await resolveOrphanLayoutWithRevalidation(
|
|
655
854
|
orphan,
|
|
@@ -720,6 +919,82 @@ export async function resolveOrphanLayoutWithRevalidation<TEnv>(
|
|
|
720
919
|
segments.push(...loaderResult.segments);
|
|
721
920
|
matchedIds.push(...loaderResult.matchedIds);
|
|
722
921
|
|
|
922
|
+
// Handler-first: resolve orphan layout handler before its parallels
|
|
923
|
+
// so ctx.set() values are visible to parallel children.
|
|
924
|
+
matchedIds.push(orphan.shortCode);
|
|
925
|
+
|
|
926
|
+
const component = await revalidate(
|
|
927
|
+
async () => {
|
|
928
|
+
if (!clientSegmentIds.has(orphan.shortCode)) {
|
|
929
|
+
if (isTraceActive()) {
|
|
930
|
+
pushRevalidationTraceEntry({
|
|
931
|
+
segmentId: orphan.shortCode,
|
|
932
|
+
segmentType: "layout",
|
|
933
|
+
belongsToRoute,
|
|
934
|
+
source: "orphan-layout",
|
|
935
|
+
defaultShouldRevalidate: true,
|
|
936
|
+
finalShouldRevalidate: true,
|
|
937
|
+
reason: "new-segment",
|
|
938
|
+
});
|
|
939
|
+
}
|
|
940
|
+
return true;
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
const dummySegment: ResolvedSegment = {
|
|
944
|
+
id: orphan.shortCode,
|
|
945
|
+
namespace: orphan.id,
|
|
946
|
+
type: "layout",
|
|
947
|
+
index: 0,
|
|
948
|
+
component: null as any,
|
|
949
|
+
params,
|
|
950
|
+
belongsToRoute,
|
|
951
|
+
layoutName: orphan.id,
|
|
952
|
+
...(orphan.mountPath ? { mountPath: orphan.mountPath } : {}),
|
|
953
|
+
};
|
|
954
|
+
|
|
955
|
+
const shouldRevalidate = await evaluateRevalidation({
|
|
956
|
+
segment: dummySegment,
|
|
957
|
+
prevParams,
|
|
958
|
+
getPrevSegment: null,
|
|
959
|
+
request,
|
|
960
|
+
prevUrl,
|
|
961
|
+
nextUrl,
|
|
962
|
+
revalidations: orphan.revalidate.map((fn, i) => ({
|
|
963
|
+
name: `revalidate${i}`,
|
|
964
|
+
fn,
|
|
965
|
+
})),
|
|
966
|
+
routeKey,
|
|
967
|
+
context,
|
|
968
|
+
actionContext,
|
|
969
|
+
stale,
|
|
970
|
+
traceSource: "orphan-layout",
|
|
971
|
+
});
|
|
972
|
+
emitRevalidationDecision(
|
|
973
|
+
orphan.shortCode,
|
|
974
|
+
context.pathname,
|
|
975
|
+
routeKey,
|
|
976
|
+
shouldRevalidate,
|
|
977
|
+
);
|
|
978
|
+
return shouldRevalidate;
|
|
979
|
+
},
|
|
980
|
+
async () => resolveLayoutComponent(orphan, context),
|
|
981
|
+
() => null,
|
|
982
|
+
);
|
|
983
|
+
|
|
984
|
+
segments.push({
|
|
985
|
+
id: orphan.shortCode,
|
|
986
|
+
namespace: orphan.id,
|
|
987
|
+
type: "layout",
|
|
988
|
+
index: 0,
|
|
989
|
+
component,
|
|
990
|
+
params,
|
|
991
|
+
belongsToRoute,
|
|
992
|
+
layoutName: orphan.id,
|
|
993
|
+
loading: orphan.loading === false ? null : orphan.loading,
|
|
994
|
+
transition: orphan.transition,
|
|
995
|
+
...(orphan.mountPath ? { mountPath: orphan.mountPath } : {}),
|
|
996
|
+
});
|
|
997
|
+
|
|
723
998
|
for (const parallelEntry of orphan.parallel) {
|
|
724
999
|
invariant(
|
|
725
1000
|
parallelEntry.type === "parallel",
|
|
@@ -758,7 +1033,20 @@ export async function resolveOrphanLayoutWithRevalidation<TEnv>(
|
|
|
758
1033
|
matchedIds.push(parallelId);
|
|
759
1034
|
|
|
760
1035
|
const shouldResolve = await (async () => {
|
|
761
|
-
if (!clientSegmentIds.has(parallelId))
|
|
1036
|
+
if (!clientSegmentIds.has(parallelId)) {
|
|
1037
|
+
if (isTraceActive()) {
|
|
1038
|
+
pushRevalidationTraceEntry({
|
|
1039
|
+
segmentId: parallelId,
|
|
1040
|
+
segmentType: "parallel",
|
|
1041
|
+
belongsToRoute,
|
|
1042
|
+
source: "parallel",
|
|
1043
|
+
defaultShouldRevalidate: true,
|
|
1044
|
+
finalShouldRevalidate: true,
|
|
1045
|
+
reason: "new-segment",
|
|
1046
|
+
});
|
|
1047
|
+
}
|
|
1048
|
+
return true;
|
|
1049
|
+
}
|
|
762
1050
|
|
|
763
1051
|
const dummySegment: ResolvedSegment = {
|
|
764
1052
|
id: parallelId,
|
|
@@ -790,14 +1078,19 @@ export async function resolveOrphanLayoutWithRevalidation<TEnv>(
|
|
|
790
1078
|
context,
|
|
791
1079
|
actionContext,
|
|
792
1080
|
stale,
|
|
1081
|
+
traceSource: "parallel",
|
|
793
1082
|
});
|
|
794
1083
|
})();
|
|
1084
|
+
emitRevalidationDecision(
|
|
1085
|
+
parallelId,
|
|
1086
|
+
context.pathname,
|
|
1087
|
+
routeKey,
|
|
1088
|
+
shouldResolve,
|
|
1089
|
+
);
|
|
795
1090
|
|
|
796
1091
|
let component: ReactNode | undefined;
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
if (slotStaticId && shouldResolve) {
|
|
800
|
-
component = await tryStaticLookup(slotStaticId, parallelId);
|
|
1092
|
+
if (shouldResolve) {
|
|
1093
|
+
component = await tryStaticSlot(parallelEntry, slot, parallelId);
|
|
801
1094
|
}
|
|
802
1095
|
if (component === undefined) {
|
|
803
1096
|
const hasLoadingFallback =
|
|
@@ -806,9 +1099,25 @@ export async function resolveOrphanLayoutWithRevalidation<TEnv>(
|
|
|
806
1099
|
if (!shouldResolve) {
|
|
807
1100
|
component = null;
|
|
808
1101
|
} else if (hasLoadingFallback) {
|
|
809
|
-
|
|
810
|
-
typeof handler === "function" ? handler(context) : handler
|
|
811
|
-
)
|
|
1102
|
+
const result =
|
|
1103
|
+
typeof handler === "function" ? handler(context) : handler;
|
|
1104
|
+
if (result instanceof Promise) {
|
|
1105
|
+
const tracked = deps.trackHandler(result, {
|
|
1106
|
+
segmentId: parallelId,
|
|
1107
|
+
segmentType: "parallel",
|
|
1108
|
+
});
|
|
1109
|
+
observeStreamedHandler(
|
|
1110
|
+
tracked,
|
|
1111
|
+
parallelId,
|
|
1112
|
+
"parallel",
|
|
1113
|
+
context.pathname,
|
|
1114
|
+
routeKey,
|
|
1115
|
+
params,
|
|
1116
|
+
);
|
|
1117
|
+
component = tracked as ReactNode;
|
|
1118
|
+
} else {
|
|
1119
|
+
component = result as ReactNode;
|
|
1120
|
+
}
|
|
812
1121
|
} else {
|
|
813
1122
|
component =
|
|
814
1123
|
typeof handler === "function" ? await handler(context) : handler;
|
|
@@ -834,204 +1143,9 @@ export async function resolveOrphanLayoutWithRevalidation<TEnv>(
|
|
|
834
1143
|
}
|
|
835
1144
|
}
|
|
836
1145
|
|
|
837
|
-
matchedIds.push(orphan.shortCode);
|
|
838
|
-
|
|
839
|
-
const component = await revalidate(
|
|
840
|
-
async () => {
|
|
841
|
-
if (!clientSegmentIds.has(orphan.shortCode)) return true;
|
|
842
|
-
|
|
843
|
-
const dummySegment: ResolvedSegment = {
|
|
844
|
-
id: orphan.shortCode,
|
|
845
|
-
namespace: orphan.id,
|
|
846
|
-
type: "layout",
|
|
847
|
-
index: 0,
|
|
848
|
-
component: null as any,
|
|
849
|
-
params,
|
|
850
|
-
belongsToRoute,
|
|
851
|
-
layoutName: orphan.id,
|
|
852
|
-
...(orphan.mountPath ? { mountPath: orphan.mountPath } : {}),
|
|
853
|
-
};
|
|
854
|
-
|
|
855
|
-
return await evaluateRevalidation({
|
|
856
|
-
segment: dummySegment,
|
|
857
|
-
prevParams,
|
|
858
|
-
getPrevSegment: null,
|
|
859
|
-
request,
|
|
860
|
-
prevUrl,
|
|
861
|
-
nextUrl,
|
|
862
|
-
revalidations: orphan.revalidate.map((fn, i) => ({
|
|
863
|
-
name: `revalidate${i}`,
|
|
864
|
-
fn,
|
|
865
|
-
})),
|
|
866
|
-
routeKey,
|
|
867
|
-
context,
|
|
868
|
-
actionContext,
|
|
869
|
-
stale,
|
|
870
|
-
});
|
|
871
|
-
},
|
|
872
|
-
async () => {
|
|
873
|
-
// Static handler interception for orphan layouts
|
|
874
|
-
const orphanAny = orphan as any;
|
|
875
|
-
if (orphanAny.isStaticPrerender && orphanAny.staticHandlerId) {
|
|
876
|
-
const staticComponent = await tryStaticLookup(
|
|
877
|
-
orphanAny.staticHandlerId,
|
|
878
|
-
orphan.shortCode,
|
|
879
|
-
);
|
|
880
|
-
if (staticComponent !== undefined) return staticComponent;
|
|
881
|
-
}
|
|
882
|
-
return typeof orphan.handler === "function"
|
|
883
|
-
? handleHandlerResult(await orphan.handler(context))
|
|
884
|
-
: orphan.handler;
|
|
885
|
-
},
|
|
886
|
-
() => null,
|
|
887
|
-
);
|
|
888
|
-
|
|
889
|
-
segments.push({
|
|
890
|
-
id: orphan.shortCode,
|
|
891
|
-
namespace: orphan.id,
|
|
892
|
-
type: "layout",
|
|
893
|
-
index: 0,
|
|
894
|
-
component,
|
|
895
|
-
params,
|
|
896
|
-
belongsToRoute,
|
|
897
|
-
layoutName: orphan.id,
|
|
898
|
-
loading: orphan.loading === false ? null : orphan.loading,
|
|
899
|
-
transition: orphan.transition,
|
|
900
|
-
...(orphan.mountPath ? { mountPath: orphan.mountPath } : {}),
|
|
901
|
-
});
|
|
902
|
-
|
|
903
1146
|
return { segments, matchedIds };
|
|
904
1147
|
}
|
|
905
1148
|
|
|
906
|
-
/**
|
|
907
|
-
* Wrapper for segment resolution with revalidation that adds error boundary handling.
|
|
908
|
-
*/
|
|
909
|
-
export async function resolveWithRevalidationErrorHandling<TEnv>(
|
|
910
|
-
entry: EntryData,
|
|
911
|
-
params: Record<string, string>,
|
|
912
|
-
resolveFn: () => Promise<SegmentRevalidationResult>,
|
|
913
|
-
deps: SegmentResolutionDeps<TEnv>,
|
|
914
|
-
pathname?: string,
|
|
915
|
-
errorContext?: {
|
|
916
|
-
request: Request;
|
|
917
|
-
url: URL;
|
|
918
|
-
routeKey?: string;
|
|
919
|
-
env?: TEnv;
|
|
920
|
-
isPartial?: boolean;
|
|
921
|
-
requestStartTime?: number;
|
|
922
|
-
},
|
|
923
|
-
): Promise<SegmentRevalidationResult> {
|
|
924
|
-
try {
|
|
925
|
-
return await resolveFn();
|
|
926
|
-
} catch (error) {
|
|
927
|
-
if (error instanceof Response) {
|
|
928
|
-
throw error;
|
|
929
|
-
}
|
|
930
|
-
|
|
931
|
-
if (error instanceof DataNotFoundError) {
|
|
932
|
-
const notFoundFallback = deps.findNearestNotFoundBoundary(entry);
|
|
933
|
-
|
|
934
|
-
if (notFoundFallback) {
|
|
935
|
-
const notFoundInfo = createNotFoundInfo(
|
|
936
|
-
error,
|
|
937
|
-
entry.shortCode,
|
|
938
|
-
entry.type,
|
|
939
|
-
pathname,
|
|
940
|
-
);
|
|
941
|
-
|
|
942
|
-
if (errorContext) {
|
|
943
|
-
deps.callOnError(error, "handler", {
|
|
944
|
-
request: errorContext.request,
|
|
945
|
-
url: errorContext.url,
|
|
946
|
-
routeKey: errorContext.routeKey,
|
|
947
|
-
params,
|
|
948
|
-
segmentId: entry.shortCode,
|
|
949
|
-
segmentType: entry.type as any,
|
|
950
|
-
env: errorContext.env,
|
|
951
|
-
isPartial: errorContext.isPartial,
|
|
952
|
-
handledByBoundary: true,
|
|
953
|
-
metadata: { notFound: true, message: notFoundInfo.message },
|
|
954
|
-
requestStartTime: errorContext.requestStartTime,
|
|
955
|
-
});
|
|
956
|
-
}
|
|
957
|
-
|
|
958
|
-
debugLog("segment", "notFound boundary handled error", {
|
|
959
|
-
segmentId: entry.shortCode,
|
|
960
|
-
message: notFoundInfo.message,
|
|
961
|
-
});
|
|
962
|
-
|
|
963
|
-
const reqCtx = getRequestContext();
|
|
964
|
-
if (reqCtx) {
|
|
965
|
-
reqCtx.res = new Response(null, {
|
|
966
|
-
status: 404,
|
|
967
|
-
headers: reqCtx.res.headers,
|
|
968
|
-
});
|
|
969
|
-
}
|
|
970
|
-
|
|
971
|
-
const notFoundSegment = createNotFoundSegment(
|
|
972
|
-
notFoundInfo,
|
|
973
|
-
notFoundFallback,
|
|
974
|
-
entry,
|
|
975
|
-
params,
|
|
976
|
-
);
|
|
977
|
-
|
|
978
|
-
return {
|
|
979
|
-
segments: [notFoundSegment],
|
|
980
|
-
matchedIds: [notFoundSegment.id],
|
|
981
|
-
};
|
|
982
|
-
}
|
|
983
|
-
}
|
|
984
|
-
|
|
985
|
-
const fallback = deps.findNearestErrorBoundary(entry);
|
|
986
|
-
const segmentType: ErrorInfo["segmentType"] = entry.type;
|
|
987
|
-
const errorInfo = createErrorInfo(error, entry.shortCode, segmentType);
|
|
988
|
-
const effectiveFallback = fallback ?? DefaultErrorFallback;
|
|
989
|
-
|
|
990
|
-
if (errorContext) {
|
|
991
|
-
deps.callOnError(error, "handler", {
|
|
992
|
-
request: errorContext.request,
|
|
993
|
-
url: errorContext.url,
|
|
994
|
-
routeKey: errorContext.routeKey,
|
|
995
|
-
params,
|
|
996
|
-
segmentId: entry.shortCode,
|
|
997
|
-
segmentType: entry.type as any,
|
|
998
|
-
env: errorContext.env,
|
|
999
|
-
isPartial: errorContext.isPartial,
|
|
1000
|
-
handledByBoundary: !!fallback,
|
|
1001
|
-
requestStartTime: errorContext.requestStartTime,
|
|
1002
|
-
});
|
|
1003
|
-
}
|
|
1004
|
-
|
|
1005
|
-
debugLog("segment", "error boundary handled error", {
|
|
1006
|
-
segmentId: entry.shortCode,
|
|
1007
|
-
boundary: fallback ? "custom" : "default",
|
|
1008
|
-
message: errorInfo.message,
|
|
1009
|
-
});
|
|
1010
|
-
|
|
1011
|
-
{
|
|
1012
|
-
const reqCtx = getRequestContext();
|
|
1013
|
-
if (reqCtx) {
|
|
1014
|
-
reqCtx.res = new Response(null, {
|
|
1015
|
-
status: 500,
|
|
1016
|
-
headers: reqCtx.res.headers,
|
|
1017
|
-
});
|
|
1018
|
-
}
|
|
1019
|
-
}
|
|
1020
|
-
|
|
1021
|
-
const errorSegment = createErrorSegment(
|
|
1022
|
-
errorInfo,
|
|
1023
|
-
effectiveFallback,
|
|
1024
|
-
entry,
|
|
1025
|
-
params,
|
|
1026
|
-
);
|
|
1027
|
-
|
|
1028
|
-
return {
|
|
1029
|
-
segments: [errorSegment],
|
|
1030
|
-
matchedIds: [errorSegment.id],
|
|
1031
|
-
};
|
|
1032
|
-
}
|
|
1033
|
-
}
|
|
1034
|
-
|
|
1035
1149
|
/**
|
|
1036
1150
|
* Resolve all segments for a route with revalidation logic (for matchPartial).
|
|
1037
1151
|
*/
|
|
@@ -1057,6 +1171,8 @@ export async function resolveAllSegmentsWithRevalidation<TEnv>(
|
|
|
1057
1171
|
const seenSegIds = new Set<string>();
|
|
1058
1172
|
const seenMatchIds = new Set<string>();
|
|
1059
1173
|
|
|
1174
|
+
const telemetry = getRouterContext()?.telemetry;
|
|
1175
|
+
|
|
1060
1176
|
for (const entry of entries) {
|
|
1061
1177
|
if (entry.type === "route" && interceptResult) {
|
|
1062
1178
|
debugLog(
|
|
@@ -1075,7 +1191,8 @@ export async function resolveAllSegmentsWithRevalidation<TEnv>(
|
|
|
1075
1191
|
}
|
|
1076
1192
|
|
|
1077
1193
|
const nonParallelEntry = entry as Exclude<EntryData, { type: "parallel" }>;
|
|
1078
|
-
const
|
|
1194
|
+
const doneEntry = track(`segment:${entry.id}`, 1);
|
|
1195
|
+
const resolved = await resolveWithErrorBoundary(
|
|
1079
1196
|
nonParallelEntry,
|
|
1080
1197
|
params,
|
|
1081
1198
|
() =>
|
|
@@ -1094,9 +1211,14 @@ export async function resolveAllSegmentsWithRevalidation<TEnv>(
|
|
|
1094
1211
|
actionContext,
|
|
1095
1212
|
false,
|
|
1096
1213
|
),
|
|
1214
|
+
(seg) => ({ segments: [seg], matchedIds: [seg.id] }),
|
|
1097
1215
|
deps,
|
|
1216
|
+
telemetry
|
|
1217
|
+
? { request, url: context.url, routeKey, isPartial: true, telemetry }
|
|
1218
|
+
: undefined,
|
|
1098
1219
|
pathname,
|
|
1099
1220
|
);
|
|
1221
|
+
doneEntry();
|
|
1100
1222
|
|
|
1101
1223
|
// Deduplicate segments and matchedIds by ID, matching resolveAllSegments.
|
|
1102
1224
|
// include() scopes can produce entries that resolve the same shared
|