@rangojs/router 0.17.0 → 0.19.0
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 +3 -2
- package/dist/types/browser/cookie-name.d.ts +6 -0
- package/dist/types/browser/event-controller.d.ts +3 -3
- package/dist/types/browser/history-state.d.ts +18 -0
- package/dist/types/browser/navigation-transaction.d.ts +20 -2
- package/dist/types/browser/partial-update.d.ts +16 -1
- package/dist/types/browser/prefetch/cache.d.ts +6 -6
- package/dist/types/browser/react/Link.d.ts +19 -9
- package/dist/types/browser/react/context.d.ts +2 -2
- package/dist/types/browser/react/location-state-shared.d.ts +95 -56
- package/dist/types/browser/react/location-state.d.ts +1 -1
- package/dist/types/browser/transition-when.d.ts +80 -0
- package/dist/types/browser/types.d.ts +104 -9
- package/dist/types/build/generate-manifest.d.ts +8 -0
- package/dist/types/cache/background-task.d.ts +24 -2
- package/dist/types/cache/cache-exec-scope.d.ts +31 -0
- package/dist/types/cache/cache-key-utils.d.ts +48 -2
- package/dist/types/cache/cache-policy.d.ts +18 -1
- package/dist/types/cache/cache-runtime.d.ts +7 -0
- package/dist/types/cache/cache-scope.d.ts +192 -26
- package/dist/types/cache/cache-tag.d.ts +116 -11
- package/dist/types/cache/cf/cf-base64.d.ts +6 -0
- package/dist/types/cache/cf/cf-cache-constants.d.ts +11 -0
- package/dist/types/cache/cf/cf-cache-store.d.ts +224 -72
- package/dist/types/cache/cf/cf-cache-types.d.ts +31 -4
- package/dist/types/cache/cf/cf-shell-frame.d.ts +95 -0
- package/dist/types/cache/cf/cf-tag-marker-memo.d.ts +19 -0
- package/dist/types/cache/document-cache.d.ts +10 -0
- package/dist/types/cache/handle-snapshot.d.ts +41 -8
- package/dist/types/cache/index.d.ts +1 -0
- package/dist/types/cache/invalidation-order.d.ts +23 -0
- package/dist/types/cache/isolate-tag-memo.d.ts +149 -0
- package/dist/types/cache/request-tag-mask.d.ts +54 -0
- package/dist/types/cache/shell-memo.d.ts +178 -0
- package/dist/types/cache/shell-snapshot.d.ts +141 -105
- package/dist/types/cache/tag-invalidation.d.ts +54 -3
- package/dist/types/cache/types.d.ts +267 -96
- package/dist/types/cache/vercel/vercel-cache-store.d.ts +123 -18
- package/dist/types/client-urls/navigation.d.ts +28 -2
- package/dist/types/client-urls/server-projection.d.ts +5 -2
- package/dist/types/client-urls/types.d.ts +5 -5
- package/dist/types/client.d.ts +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.rsc.d.ts +1 -1
- package/dist/types/prerender/build-shell-capture.d.ts +11 -4
- package/dist/types/route-definition/helpers-types.d.ts +11 -11
- package/dist/types/route-name.d.ts +7 -0
- package/dist/types/router/error-handling.d.ts +1 -1
- package/dist/types/router/instrument.d.ts +8 -0
- package/dist/types/router/intercept-resolution.d.ts +2 -0
- package/dist/types/router/match-context.d.ts +5 -0
- package/dist/types/router/match-middleware/background-revalidation.d.ts +0 -4
- package/dist/types/router/middleware-types.d.ts +6 -1
- package/dist/types/router/router-options.d.ts +4 -1
- package/dist/types/router/segment-resolution/loader-cache.d.ts +58 -12
- package/dist/types/router/segment-resolution/loader-mask.d.ts +3 -4
- package/dist/types/router/segment-resolution/loader-snapshot.d.ts +33 -48
- package/dist/types/router/segment-resolution/mask-nested.d.ts +73 -24
- package/dist/types/router/segment-resolution/view-transition-default.d.ts +7 -5
- package/dist/types/router/transition-when.d.ts +24 -10
- package/dist/types/rsc/attach-transition-when.d.ts +18 -0
- package/dist/types/rsc/full-payload.d.ts +21 -0
- package/dist/types/rsc/handler.d.ts +1 -1
- package/dist/types/rsc/response-cache-serve.d.ts +15 -12
- package/dist/types/rsc/shell-build-manifest.d.ts +12 -3
- package/dist/types/rsc/shell-capture-constants.d.ts +116 -12
- package/dist/types/rsc/shell-capture.d.ts +202 -74
- package/dist/types/rsc/shell-serve.d.ts +144 -50
- package/dist/types/rsc/types.d.ts +9 -0
- package/dist/types/segment-system.d.ts +7 -0
- package/dist/types/server/context.d.ts +155 -33
- package/dist/types/server/cookie-store.d.ts +33 -0
- package/dist/types/server/handle-store.d.ts +52 -1
- package/dist/types/server/request-context.d.ts +210 -63
- package/dist/types/server/request-headers.d.ts +44 -0
- package/dist/types/server.d.ts +1 -0
- package/dist/types/ssr/index.d.ts +9 -0
- package/dist/types/ssr/ssr-root.d.ts +9 -8
- package/dist/types/testing/e2e/index.d.ts +1 -1
- package/dist/types/testing/flight.entry.d.ts +7 -0
- package/dist/types/testing/index.d.ts +3 -2
- package/dist/types/testing/internal/action-id.d.ts +7 -0
- package/dist/types/testing/location-state-key.d.ts +24 -0
- package/dist/types/testing/render-route.d.ts +17 -1
- package/dist/types/testing/run-loader.d.ts +18 -3
- package/dist/types/testing/run-transition-when.d.ts +44 -69
- package/dist/types/testing/serve-shell-request.d.ts +133 -0
- package/dist/types/testing/shell-status.d.ts +44 -19
- package/dist/types/transition-when-ref.d.ts +79 -0
- package/dist/types/types/cache-types.d.ts +15 -3
- package/dist/types/types/handler-context.d.ts +14 -1
- package/dist/types/types/index.d.ts +1 -1
- package/dist/types/types/segments.d.ts +86 -71
- package/dist/types/urls/path-helper-types.d.ts +3 -4
- package/dist/types/urls/pattern-types.d.ts +21 -18
- package/dist/types/vite/plugins/transition-when-hoist.d.ts +45 -0
- package/dist/types/vite/router-discovery.d.ts +29 -2
- package/dist/vite/index.js +1005 -105
- package/package.json +1 -1
- package/skills/breadcrumbs/SKILL.md +17 -7
- package/skills/cache-guide/SKILL.md +175 -76
- package/skills/caching/SKILL.md +361 -33
- package/skills/catalog.json +1 -1
- package/skills/client-urls/SKILL.md +12 -7
- package/skills/cloudflare/SKILL.md +15 -0
- package/skills/comparison/references/framework-comparison.md +52 -37
- package/skills/defer-hydration/SKILL.md +50 -30
- package/skills/deployment-caching/SKILL.md +5 -2
- package/skills/document-cache/SKILL.md +32 -2
- package/skills/handler-use/SKILL.md +2 -0
- package/skills/hooks/SKILL.md +3 -1
- package/skills/hooks/handle-and-actions.md +3 -1
- package/skills/hooks/navigation.md +23 -2
- package/skills/hooks/state.md +101 -7
- package/skills/intercept/SKILL.md +29 -12
- package/skills/loader/SKILL.md +197 -44
- package/skills/middleware/SKILL.md +34 -9
- package/skills/migrate-nextjs/SKILL.md +68 -37
- package/skills/migrate-react-router/SKILL.md +2 -1
- package/skills/migrate-react-router/component-migration.md +3 -1
- package/skills/migrate-react-router/data-and-actions.md +3 -1
- package/skills/migrate-react-router/route-mapping.md +7 -4
- package/skills/observability/SKILL.md +65 -0
- package/skills/parallel/SKILL.md +13 -8
- package/skills/ppr/SKILL.md +715 -371
- package/skills/prerender/SKILL.md +30 -17
- package/skills/rango/SKILL.md +72 -34
- package/skills/route/SKILL.md +14 -7
- package/skills/router-setup/SKILL.md +4 -2
- package/skills/scripts/SKILL.md +8 -1
- package/skills/server-actions/SKILL.md +11 -0
- package/skills/shell-manifest/SKILL.md +21 -12
- package/skills/testing/SKILL.md +49 -43
- package/skills/testing/cache-prerender.md +15 -14
- package/skills/testing/client-components.md +45 -3
- package/skills/testing/loader.md +19 -1
- package/skills/testing/middleware.md +1 -0
- package/skills/testing/predicates.md +49 -49
- package/skills/testing/render-handler.md +1 -0
- package/skills/testing/serve-shell-request.md +202 -0
- package/skills/testing/server-actions.md +9 -3
- package/skills/theme/SKILL.md +56 -4
- package/skills/typesafety/env-and-bindings.md +18 -2
- package/skills/use-cache/SKILL.md +91 -23
- package/skills/vercel/SKILL.md +14 -2
- package/skills/view-transitions/SKILL.md +150 -29
- package/src/__augment-tests__/augmented.check.ts +23 -0
- package/src/browser/cookie-name.ts +23 -0
- package/src/browser/event-controller.ts +4 -4
- package/src/browser/history-state.ts +75 -1
- package/src/browser/link-interceptor.ts +4 -0
- package/src/browser/navigation-bridge.ts +77 -18
- package/src/browser/navigation-client.ts +4 -3
- package/src/browser/navigation-store.ts +43 -1
- package/src/browser/navigation-transaction.ts +31 -11
- package/src/browser/partial-update.ts +146 -35
- package/src/browser/prefetch/cache.ts +6 -6
- package/src/browser/prefetch/fetch.ts +2 -2
- package/src/browser/react/Link.tsx +38 -33
- package/src/browser/react/NavigationProvider.tsx +2 -2
- package/src/browser/react/context.ts +2 -2
- package/src/browser/react/location-state-shared.ts +241 -49
- package/src/browser/react/location-state.ts +5 -2
- package/src/browser/rsc-router.tsx +9 -0
- package/src/browser/server-action-bridge.ts +84 -15
- package/src/browser/transition-when.ts +205 -0
- package/src/browser/types.ts +124 -10
- package/src/build/generate-manifest.ts +77 -1
- package/src/cache/background-task.ts +55 -2
- package/src/cache/cache-exec-scope.ts +48 -1
- package/src/cache/cache-key-utils.ts +62 -2
- package/src/cache/cache-policy.ts +31 -4
- package/src/cache/cache-runtime.ts +209 -129
- package/src/cache/cache-scope.ts +573 -69
- package/src/cache/cache-tag.ts +475 -19
- package/src/cache/cf/cf-base64.ts +36 -4
- package/src/cache/cf/cf-cache-constants.ts +12 -0
- package/src/cache/cf/cf-cache-store.ts +1094 -376
- package/src/cache/cf/cf-cache-types.ts +32 -4
- package/src/cache/cf/cf-shell-frame.ts +258 -0
- package/src/cache/cf/cf-tag-marker-memo.ts +55 -40
- package/src/cache/document-cache.ts +53 -7
- package/src/cache/handle-snapshot.ts +53 -9
- package/src/cache/index.ts +2 -0
- package/src/cache/invalidation-order.ts +84 -0
- package/src/cache/isolate-tag-memo.ts +308 -0
- package/src/cache/request-tag-mask.ts +147 -0
- package/src/cache/shell-memo.ts +341 -0
- package/src/cache/shell-snapshot.ts +249 -277
- package/src/cache/tag-invalidation.ts +109 -18
- package/src/cache/types.ts +274 -102
- package/src/cache/vercel/vercel-cache-store.ts +561 -74
- package/src/client-urls/client-root.tsx +8 -4
- package/src/client-urls/client-urls.ts +8 -6
- package/src/client-urls/navigation.ts +55 -4
- package/src/client-urls/server-projection.ts +31 -10
- package/src/client-urls/types.ts +12 -5
- package/src/client.tsx +2 -0
- package/src/host/cookie-handler.ts +2 -1
- package/src/index.rsc.ts +3 -0
- package/src/index.ts +3 -0
- package/src/loader-redirect.tsx +7 -3
- package/src/prerender/build-shell-capture.ts +173 -110
- package/src/route-definition/dsl-helpers.ts +6 -3
- package/src/route-definition/helpers-types.ts +11 -11
- package/src/route-name.ts +12 -0
- package/src/router/content-negotiation.ts +4 -1
- package/src/router/error-handling.ts +5 -2
- package/src/router/handler-context.ts +42 -5
- package/src/router/instrument.ts +54 -2
- package/src/router/intercept-resolution.ts +30 -8
- package/src/router/loader-resolution.ts +89 -33
- package/src/router/logging.ts +5 -3
- package/src/router/match-api.ts +89 -42
- package/src/router/match-context.ts +5 -0
- package/src/router/match-handlers.ts +14 -29
- package/src/router/match-middleware/background-revalidation.ts +3 -5
- package/src/router/match-middleware/cache-lookup.ts +52 -19
- package/src/router/match-middleware/cache-store.ts +36 -10
- package/src/router/match-result.ts +1 -0
- package/src/router/middleware-types.ts +6 -1
- package/src/router/middleware.ts +20 -4
- package/src/router/navigation-snapshot.ts +6 -5
- package/src/router/prerender-match.ts +10 -0
- package/src/router/request-classification.ts +7 -4
- package/src/router/router-options.ts +4 -1
- package/src/router/segment-resolution/fresh.ts +18 -13
- package/src/router/segment-resolution/helpers.ts +4 -2
- package/src/router/segment-resolution/loader-cache.ts +366 -70
- package/src/router/segment-resolution/loader-mask.ts +3 -4
- package/src/router/segment-resolution/loader-snapshot.ts +215 -126
- package/src/router/segment-resolution/mask-nested.ts +161 -42
- package/src/router/segment-resolution/revalidation.ts +27 -13
- package/src/router/segment-resolution/view-transition-default.ts +6 -21
- package/src/router/telemetry.ts +6 -3
- package/src/router/transition-when.ts +34 -52
- package/src/router.ts +2 -1
- package/src/rsc/attach-transition-when.ts +47 -0
- package/src/rsc/full-payload.ts +47 -3
- package/src/rsc/handler.ts +24 -10
- package/src/rsc/loader-fetch.ts +2 -1
- package/src/rsc/manifest-init.ts +4 -0
- package/src/rsc/origin-guard.ts +8 -5
- package/src/rsc/progressive-enhancement.ts +10 -32
- package/src/rsc/response-cache-serve.ts +69 -78
- package/src/rsc/response-route-handler.ts +2 -2
- package/src/rsc/rsc-rendering.ts +712 -338
- package/src/rsc/server-action.ts +8 -20
- package/src/rsc/shell-build-manifest.ts +49 -29
- package/src/rsc/shell-capture-constants.ts +182 -12
- package/src/rsc/shell-capture.ts +1126 -612
- package/src/rsc/shell-serve.ts +331 -85
- package/src/rsc/ssr-setup.ts +3 -2
- package/src/rsc/types.ts +9 -0
- package/src/segment-system.tsx +40 -7
- package/src/server/context.ts +328 -72
- package/src/server/cookie-store.ts +184 -133
- package/src/server/handle-store.ts +245 -27
- package/src/server/request-context.ts +364 -100
- package/src/server/request-headers.ts +134 -0
- package/src/server.ts +4 -0
- package/src/ssr/index.tsx +96 -37
- package/src/ssr/ssr-root.tsx +9 -8
- package/src/testing/dispatch.ts +4 -7
- package/src/testing/e2e/index.ts +1 -0
- package/src/testing/flight.entry.ts +14 -0
- package/src/testing/index.ts +5 -0
- package/src/testing/internal/action-id.ts +25 -0
- package/src/testing/internal/context.ts +1 -1
- package/src/testing/location-state-key.ts +45 -0
- package/src/testing/render-handler.ts +1 -1
- package/src/testing/render-route.tsx +109 -16
- package/src/testing/run-client-revalidate.ts +3 -19
- package/src/testing/run-loader.ts +67 -5
- package/src/testing/run-transition-when.ts +109 -172
- package/src/testing/serve-shell-request.ts +381 -0
- package/src/testing/shell-status.ts +73 -47
- package/src/theme/ThemeProvider.tsx +7 -6
- package/src/transition-when-ref.ts +220 -0
- package/src/types/cache-types.ts +15 -3
- package/src/types/handler-context.ts +14 -1
- package/src/types/index.ts +3 -0
- package/src/types/segments.ts +94 -91
- package/src/urls/path-helper-types.ts +3 -4
- package/src/urls/pattern-types.ts +21 -18
- package/src/vite/discovery/discover-routers.ts +3 -0
- package/src/vite/discovery/shell-prerender-phase.ts +9 -2
- package/src/vite/discovery/virtual-module-codegen.ts +16 -6
- package/src/vite/plugins/transition-when-hoist.ts +1064 -0
- package/src/vite/rango.ts +2 -0
- package/src/vite/router-discovery.ts +89 -9
- package/dist/types/rsc/transition-gate.d.ts +0 -23
- package/src/rsc/transition-gate.ts +0 -86
package/src/router/instrument.ts
CHANGED
|
@@ -28,7 +28,14 @@
|
|
|
28
28
|
* is a direct call — no wrapper, no timestamp, no allocation.
|
|
29
29
|
*/
|
|
30
30
|
|
|
31
|
-
import {
|
|
31
|
+
import {
|
|
32
|
+
_getRequestContext,
|
|
33
|
+
type RequestContext,
|
|
34
|
+
} from "../server/request-context.js";
|
|
35
|
+
import {
|
|
36
|
+
recordTagOwnersArmed,
|
|
37
|
+
runInSegmentTagScope,
|
|
38
|
+
} from "../cache/cache-tag.js";
|
|
32
39
|
import { isAutoGeneratedRouteName } from "../route-name.js";
|
|
33
40
|
import { getRouterContext } from "./router-context.js";
|
|
34
41
|
import { resolveSink, safeEmit, type TelemetryEvent } from "./telemetry.js";
|
|
@@ -352,11 +359,56 @@ export function observeHandler<C, R>(
|
|
|
352
359
|
handler: (ctx: C) => R,
|
|
353
360
|
ctx: C,
|
|
354
361
|
): R {
|
|
355
|
-
|
|
362
|
+
return observeHandlerIn(_getRequestContext(), id, handler, ctx);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
function observeHandlerIn<C, R>(
|
|
366
|
+
reqCtx: RequestContext | undefined,
|
|
367
|
+
id: string,
|
|
368
|
+
handler: (ctx: C) => R,
|
|
369
|
+
ctx: C,
|
|
370
|
+
): R {
|
|
371
|
+
const tracing = reqCtx?._tracing;
|
|
356
372
|
if (!tracing || tracing.phases.handler === false) return handler(ctx);
|
|
357
373
|
return observePhase(PHASES.handler(id), () => handler(ctx));
|
|
358
374
|
}
|
|
359
375
|
|
|
376
|
+
/**
|
|
377
|
+
* observeHandler, inside segment `segmentId`'s tag scope, so the tags the
|
|
378
|
+
* handler records reach the route's cache() record (#957, cache-tag.ts
|
|
379
|
+
* runInSegmentTagScope). `spanId` is observeHandler's `id`. A request that
|
|
380
|
+
* cannot write a record takes observeHandler's path as is, with the same one
|
|
381
|
+
* request-context read: no scope, no closure.
|
|
382
|
+
*/
|
|
383
|
+
export function observeSegmentHandler<C, R>(
|
|
384
|
+
segmentId: string,
|
|
385
|
+
spanId: string,
|
|
386
|
+
handler: (ctx: C) => R,
|
|
387
|
+
ctx: C,
|
|
388
|
+
): R {
|
|
389
|
+
const reqCtx = _getRequestContext();
|
|
390
|
+
if (!recordTagOwnersArmed(reqCtx)) {
|
|
391
|
+
return observeHandlerIn(reqCtx, spanId, handler, ctx);
|
|
392
|
+
}
|
|
393
|
+
return observeInSegmentScope(segmentId, reqCtx, spanId, handler, ctx);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// Separate from observeSegmentHandler: a closure there would make V8
|
|
397
|
+
// allocate its captured-variable context on every call, armed or not.
|
|
398
|
+
function observeInSegmentScope<C, R>(
|
|
399
|
+
segmentId: string,
|
|
400
|
+
reqCtx: RequestContext | undefined,
|
|
401
|
+
spanId: string,
|
|
402
|
+
handler: (ctx: C) => R,
|
|
403
|
+
ctx: C,
|
|
404
|
+
): R {
|
|
405
|
+
return runInSegmentTagScope(
|
|
406
|
+
segmentId,
|
|
407
|
+
(c) => observeHandlerIn(reqCtx, spanId, handler, c),
|
|
408
|
+
ctx,
|
|
409
|
+
);
|
|
410
|
+
}
|
|
411
|
+
|
|
360
412
|
/**
|
|
361
413
|
* Emit one discrete telemetry event (the event-shaped counterpart to
|
|
362
414
|
* observePhase). Resolves the sink from the active router context and stamps the
|
|
@@ -32,6 +32,7 @@ import { resolveLoaderData } from "./segment-resolution/loader-cache.js";
|
|
|
32
32
|
import type { SegmentResolutionDeps } from "./types.js";
|
|
33
33
|
import { debugLog } from "./logging.js";
|
|
34
34
|
import { getRouterContext } from "./router-context.js";
|
|
35
|
+
import { runInSegmentTagScope } from "../cache/cache-tag.js";
|
|
35
36
|
import {
|
|
36
37
|
RangoContext,
|
|
37
38
|
latchPprHeaderScopeForEntries,
|
|
@@ -63,6 +64,8 @@ function latchPprHeaderScopeForInterceptTarget(
|
|
|
63
64
|
* Check if an intercept's when conditions are satisfied.
|
|
64
65
|
* All when() functions must return true for the intercept to activate.
|
|
65
66
|
* If no when() conditions are defined, the intercept always activates.
|
|
67
|
+
* A selector that throws yields the conservative default, no intercept (the
|
|
68
|
+
* full page renders), and is logged; it never fails the request.
|
|
66
69
|
*
|
|
67
70
|
* During action revalidation, when() is NOT evaluated.
|
|
68
71
|
*/
|
|
@@ -83,7 +86,15 @@ export function evaluateInterceptWhen(
|
|
|
83
86
|
return false;
|
|
84
87
|
}
|
|
85
88
|
|
|
86
|
-
|
|
89
|
+
try {
|
|
90
|
+
return intercept.when.every((fn) => fn(selectorContext));
|
|
91
|
+
} catch (error) {
|
|
92
|
+
console.error(
|
|
93
|
+
`[rango] intercept({ when }) for route "${selectorContext.to.routeName ?? intercept.routeName}" (${intercept.slotName}) threw; rendering the full page without the intercept.`,
|
|
94
|
+
error,
|
|
95
|
+
);
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
87
98
|
}
|
|
88
99
|
|
|
89
100
|
/**
|
|
@@ -332,12 +343,18 @@ export async function resolveInterceptEntry<TEnv>(
|
|
|
332
343
|
);
|
|
333
344
|
}
|
|
334
345
|
|
|
346
|
+
// The intercept segment's id, and the tag scope its handler and layout
|
|
347
|
+
// record in (#957, cache-tag.ts runInSegmentTagScope).
|
|
348
|
+
const slotSegmentId = `${parentEntry.shortCode}.${interceptEntry.slotName}`;
|
|
335
349
|
let handlerResult: ReactNode;
|
|
336
350
|
try {
|
|
351
|
+
const handler = interceptEntry.handler;
|
|
337
352
|
handlerResult =
|
|
338
|
-
typeof
|
|
339
|
-
? handleHandlerResult(
|
|
340
|
-
|
|
353
|
+
typeof handler === "function"
|
|
354
|
+
? handleHandlerResult(
|
|
355
|
+
runInSegmentTagScope(slotSegmentId, handler, context),
|
|
356
|
+
)
|
|
357
|
+
: handler;
|
|
341
358
|
} catch (error) {
|
|
342
359
|
handlerResult = renderInterceptHandlerError(
|
|
343
360
|
error,
|
|
@@ -355,14 +372,19 @@ export async function resolveInterceptEntry<TEnv>(
|
|
|
355
372
|
|
|
356
373
|
let layoutElement: ReactNode | undefined;
|
|
357
374
|
if (interceptEntry.layout) {
|
|
358
|
-
|
|
359
|
-
|
|
375
|
+
const layout = interceptEntry.layout;
|
|
376
|
+
if (typeof layout === "function") {
|
|
377
|
+
const layoutResult = await runInSegmentTagScope(
|
|
378
|
+
slotSegmentId,
|
|
379
|
+
layout,
|
|
380
|
+
context,
|
|
381
|
+
);
|
|
360
382
|
if (layoutResult instanceof Response) {
|
|
361
383
|
throw layoutResult;
|
|
362
384
|
}
|
|
363
385
|
layoutElement = layoutResult;
|
|
364
386
|
} else {
|
|
365
|
-
layoutElement =
|
|
387
|
+
layoutElement = layout;
|
|
366
388
|
}
|
|
367
389
|
}
|
|
368
390
|
|
|
@@ -413,7 +435,7 @@ export async function resolveInterceptEntry<TEnv>(
|
|
|
413
435
|
}
|
|
414
436
|
|
|
415
437
|
const interceptSegment = {
|
|
416
|
-
id:
|
|
438
|
+
id: slotSegmentId,
|
|
417
439
|
namespace: `intercept:${interceptEntry.routeName}`,
|
|
418
440
|
type: "parallel" as const,
|
|
419
441
|
index: 0,
|
|
@@ -9,6 +9,16 @@ import type { EntryData } from "../server/context";
|
|
|
9
9
|
import { observePhase, PHASES } from "./instrument.js";
|
|
10
10
|
import { contextGet, isNonCacheable } from "../context-var.js";
|
|
11
11
|
import { NOCACHE_SYMBOL } from "../cache/taint.js";
|
|
12
|
+
import {
|
|
13
|
+
linkLoaderTags,
|
|
14
|
+
linkRecordedTags,
|
|
15
|
+
linkValueTags,
|
|
16
|
+
loaderTagSetsArmed,
|
|
17
|
+
markTagSetStart,
|
|
18
|
+
readValueTags,
|
|
19
|
+
tagLoaderValue,
|
|
20
|
+
} from "../cache/cache-tag.js";
|
|
21
|
+
import { executionStart } from "../cache/tag-invalidation.js";
|
|
12
22
|
import type {
|
|
13
23
|
ResolvedSegment,
|
|
14
24
|
HandlerContext,
|
|
@@ -388,6 +398,18 @@ function createLoaderExecutor<TEnv>(
|
|
|
388
398
|
* payload's handle-stream lifetime.
|
|
389
399
|
*/
|
|
390
400
|
silentHandles?: boolean;
|
|
401
|
+
/**
|
|
402
|
+
* A stale refresh's executor (setupLoaderAccess _runLoaderIsolated): its
|
|
403
|
+
* runs' pushes are diverted from the page's store, so a run's end says
|
|
404
|
+
* nothing about the page's replayed values (HandleStore.settleLoaderRun).
|
|
405
|
+
*/
|
|
406
|
+
detached?: boolean;
|
|
407
|
+
/**
|
|
408
|
+
* loaderId -> the cache() tags of that loader's binding in this request
|
|
409
|
+
* (setupLoaderAccess _bindLoaderCacheTags): an execution of it outside
|
|
410
|
+
* the binding answers for them too (#964).
|
|
411
|
+
*/
|
|
412
|
+
bindingTags?: ReadonlyMap<string, Set<string>>;
|
|
391
413
|
},
|
|
392
414
|
): (
|
|
393
415
|
loader: LoaderDefinition<any, any>,
|
|
@@ -396,6 +418,8 @@ function createLoaderExecutor<TEnv>(
|
|
|
396
418
|
// Capture RequestContext eagerly for cookie access (ALS protection on Cloudflare)
|
|
397
419
|
const reqCtxRef = _getRequestContext();
|
|
398
420
|
const silentHandles = executorOptions?.silentHandles === true;
|
|
421
|
+
// A run's end reports to the page's store only when its pushes reach it.
|
|
422
|
+
const reportsRunEnd = !silentHandles && executorOptions?.detached !== true;
|
|
399
423
|
const internal = ctx as InternalHandlerContext<any, TEnv>;
|
|
400
424
|
|
|
401
425
|
// Dependency graph: loaderId -> set of loader IDs it directly depends on.
|
|
@@ -438,8 +462,24 @@ function createLoaderExecutor<TEnv>(
|
|
|
438
462
|
// loader's cache() read-through lives in loader-cache.ts
|
|
439
463
|
// (executeLoaderData), whose ctx.use override serves a later handler read
|
|
440
464
|
// the binding's result.
|
|
441
|
-
|
|
442
|
-
|
|
465
|
+
//
|
|
466
|
+
// A read is a consumption: the value bakes into the reader's output, so
|
|
467
|
+
// the loader's tags follow the reader onto a cache() record (#957) and
|
|
468
|
+
// into the reader's own loader-cache entry (#964, readValueTags; armed
|
|
469
|
+
// only where a loader cache() can read them).
|
|
470
|
+
const reqCtx = reqCtxRef ?? _getRequestContext();
|
|
471
|
+
linkLoaderTags(loader.$$id, reqCtx);
|
|
472
|
+
const recordTags = loaderTagSetsArmed(reqCtx);
|
|
473
|
+
// A loader body reads a cache()-bound loader through its binding too, so
|
|
474
|
+
// every reader in the request gets the one value a HIT served. The
|
|
475
|
+
// binding's own MISS (callerLoaderId null) runs the loader below.
|
|
476
|
+
const bound =
|
|
477
|
+
callerLoaderId !== null &&
|
|
478
|
+
internal._loaderCacheOverrides?.get(loader.$$id);
|
|
479
|
+
const memo = bound || loaderPromises.get(loader.$$id);
|
|
480
|
+
if (memo) {
|
|
481
|
+
if (recordTags) readValueTags(memo);
|
|
482
|
+
return memo;
|
|
443
483
|
}
|
|
444
484
|
|
|
445
485
|
// Get loader function - either from loader object or fetchable registry
|
|
@@ -534,7 +574,7 @@ function createLoaderExecutor<TEnv>(
|
|
|
534
574
|
return collectHandleData(keyOrVar, snapshot, segmentOrder);
|
|
535
575
|
}
|
|
536
576
|
if (isNonCacheable(variables, keyOrVar)) {
|
|
537
|
-
assertNonCacheableReadAllowed(keyOrVar);
|
|
577
|
+
assertNonCacheableReadAllowed(keyOrVar, _getRequestContext());
|
|
538
578
|
}
|
|
539
579
|
return contextGet(variables, keyOrVar);
|
|
540
580
|
}) as typeof ctx.get,
|
|
@@ -680,42 +720,39 @@ function createLoaderExecutor<TEnv>(
|
|
|
680
720
|
// does not grant rendered() to handler-invoked loaders. Uses a body-only
|
|
681
721
|
// scope, so isInsideLoaderScope() / barrier / deadlock gating is unchanged.
|
|
682
722
|
//
|
|
683
|
-
//
|
|
684
|
-
//
|
|
685
|
-
//
|
|
686
|
-
//
|
|
687
|
-
//
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
//
|
|
691
|
-
//
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
// path and keeps the full tail. DSL loaders re-run on every HIT and never
|
|
697
|
-
// set this.
|
|
698
|
-
if (!isDslLoader) {
|
|
699
|
-
const captureCtx = _getRequestContext();
|
|
700
|
-
if (
|
|
701
|
-
captureCtx?._shellCaptureRun &&
|
|
702
|
-
captureCtx._shellCaptureHandleLiveness
|
|
703
|
-
) {
|
|
704
|
-
captureCtx._shellCaptureHandleLiveness.handlerInvokedLoader = true;
|
|
705
|
-
}
|
|
706
|
-
}
|
|
707
|
-
|
|
723
|
+
// The CONSUMPTION-LANE RULE: a handler-consumed loader's value is a
|
|
724
|
+
// BAKED copy in every shared artifact (cache(), "use cache", the PPR
|
|
725
|
+
// shell). The cache() guard permits its identity reads; a "use cache"
|
|
726
|
+
// body it was entered in and a PPR capture do not (server/context.ts
|
|
727
|
+
// guardIdentityRead).
|
|
728
|
+
|
|
729
|
+
const recordedTags = recordTags ? new Set<string>() : undefined;
|
|
730
|
+
// A loader-cache write that stores or reads this value gates on the
|
|
731
|
+
// earliest start behind it (#977, earliestRecordedStart).
|
|
732
|
+
if (recordedTags) markTagSetStart(recordedTags, executionStart());
|
|
733
|
+
const runEndStore = reportsRunEnd
|
|
734
|
+
? (reqCtxRef ?? _getRequestContext())?._handleStore
|
|
735
|
+
: undefined;
|
|
708
736
|
const promise = observePhase(PHASES.loader(loader.$$id), () =>
|
|
709
737
|
Promise.resolve(
|
|
710
738
|
runInsideLoaderBodyScope(
|
|
711
739
|
() => loaderFn(loaderCtx as LoaderContext<any, TEnv>),
|
|
712
740
|
loader.$$id,
|
|
713
|
-
|
|
741
|
+
recordedTags,
|
|
714
742
|
),
|
|
715
743
|
).finally(() => {
|
|
716
744
|
pendingLoaders.delete(loader.$$id);
|
|
745
|
+
// Its pushes are made: values replayed for it that none replaced
|
|
746
|
+
// go (a hole that throws or skips its push shows none).
|
|
747
|
+
runEndStore?.settleLoaderRun(loader.$$id);
|
|
717
748
|
}),
|
|
718
749
|
);
|
|
750
|
+
if (recordedTags) {
|
|
751
|
+
tagLoaderValue(promise, recordedTags);
|
|
752
|
+
readValueTags(promise);
|
|
753
|
+
const bindingTags = executorOptions?.bindingTags?.get(loader.$$id);
|
|
754
|
+
if (bindingTags) linkRecordedTags(recordedTags, bindingTags);
|
|
755
|
+
}
|
|
719
756
|
|
|
720
757
|
// Auxiliary-lane tracking: keeps the handle store open for this body's
|
|
721
758
|
// ctx.handle() pushes without joining `settled` (the handler barrier that
|
|
@@ -759,8 +796,6 @@ export function setupLoaderAccess<TEnv>(
|
|
|
759
796
|
const reqCtxRef = _getRequestContext();
|
|
760
797
|
const handleStoreRef = reqCtxRef?._handleStore;
|
|
761
798
|
|
|
762
|
-
const useLoader = createLoaderExecutor(ctx, loaderPromises);
|
|
763
|
-
|
|
764
799
|
// Loader-cache HIT replay (loader-cache.ts replayLoaderHandles): a cached
|
|
765
800
|
// entry records its ctx.use dependencies' pushes too, and a dependency is
|
|
766
801
|
// memoized here and shared with live readers. A loader that already ran
|
|
@@ -773,12 +808,33 @@ export function setupLoaderAccess<TEnv>(
|
|
|
773
808
|
claimed.add(loaderId);
|
|
774
809
|
return true;
|
|
775
810
|
};
|
|
811
|
+
// A loader cache() binding's tags reach every execution of that loader
|
|
812
|
+
// outside the binding: one a reader started before the binding (linked
|
|
813
|
+
// here), a later one, or a refresh's (bindingTags).
|
|
814
|
+
const executor: { bindingTags?: Map<string, Set<string>> } = {};
|
|
815
|
+
internal._bindLoaderCacheTags = (loaderId, tags) => {
|
|
816
|
+
(executor.bindingTags ??= new Map()).set(loaderId, tags);
|
|
817
|
+
const started = loaderPromises.get(loaderId);
|
|
818
|
+
if (started) linkValueTags(started, tags);
|
|
819
|
+
};
|
|
820
|
+
const useLoader = createLoaderExecutor(ctx, loaderPromises, executor);
|
|
776
821
|
// A refresh's pushes are diverted, so its loader ctxs never claim
|
|
777
|
-
// (cache-runtime.ts refreshView).
|
|
822
|
+
// (cache-runtime.ts refreshView). It reads each loader fresh, not through
|
|
823
|
+
// the page's bindings: a stale refresh must not rebuild its entry from
|
|
824
|
+
// another stale entry.
|
|
778
825
|
internal._runLoaderIsolated = (loader) =>
|
|
779
826
|
createLoaderExecutor(
|
|
780
|
-
Object.create(ctx, {
|
|
827
|
+
Object.create(ctx, {
|
|
828
|
+
_claimLoaderPushes: { value: undefined },
|
|
829
|
+
_loaderCacheOverrides: { value: undefined },
|
|
830
|
+
}),
|
|
781
831
|
new Map(),
|
|
832
|
+
{
|
|
833
|
+
get bindingTags() {
|
|
834
|
+
return executor.bindingTags;
|
|
835
|
+
},
|
|
836
|
+
detached: true,
|
|
837
|
+
},
|
|
782
838
|
)(loader, null);
|
|
783
839
|
|
|
784
840
|
ctx.use = ((item: LoaderDefinition<any, any> | Handle<any, any>) => {
|
package/src/router/logging.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { requestHeaders } from "../server/request-headers.js";
|
|
1
2
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
3
|
import { INTERNAL_RANGO_DEBUG } from "../internal-debug.js";
|
|
3
4
|
|
|
@@ -61,10 +62,11 @@ function nextId(prefix: string, counter: number): string {
|
|
|
61
62
|
}
|
|
62
63
|
|
|
63
64
|
function getHeaderRequestId(request: Request): string | null {
|
|
65
|
+
const headers = requestHeaders(request);
|
|
64
66
|
const candidate =
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
67
|
+
headers.get("x-rsc-router-request-id") ??
|
|
68
|
+
headers.get("x-request-id") ??
|
|
69
|
+
headers.get("cf-ray");
|
|
68
70
|
if (!candidate) return null;
|
|
69
71
|
const trimmed = candidate.trim();
|
|
70
72
|
return trimmed.length > 0 ? trimmed : null;
|
package/src/router/match-api.ts
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
|
+
import { requestHeaders } from "../server/request-headers.js";
|
|
1
2
|
import {
|
|
2
3
|
CacheScope,
|
|
3
4
|
createCacheScope,
|
|
4
5
|
resolveShellImplicitCacheScope,
|
|
5
6
|
} from "../cache/cache-scope.js";
|
|
7
|
+
import { armLoaderTagSets, armRecordTagOwners } from "../cache/cache-tag.js";
|
|
8
|
+
import {
|
|
9
|
+
bindsLoaderCache,
|
|
10
|
+
routeLoaderLanes,
|
|
11
|
+
} from "./segment-resolution/loader-cache.js";
|
|
6
12
|
import { RouteNotFoundError } from "../errors";
|
|
7
13
|
import {
|
|
8
14
|
createErrorInfo,
|
|
@@ -35,8 +41,7 @@ import {
|
|
|
35
41
|
_getRequestContext,
|
|
36
42
|
setRequestContextPrevRouteKey,
|
|
37
43
|
} from "../server/request-context.js";
|
|
38
|
-
import {
|
|
39
|
-
import type { DefaultRouteName } from "../types/global-namespace.js";
|
|
44
|
+
import { publicRouteName } from "../route-name.js";
|
|
40
45
|
import { debugLog, debugWarn } from "./logging.js";
|
|
41
46
|
import {
|
|
42
47
|
resolveRoute,
|
|
@@ -60,7 +65,7 @@ export async function createMatchContextForFull<TEnv>(
|
|
|
60
65
|
|
|
61
66
|
const metricsStore = deps.getMetricsStore();
|
|
62
67
|
|
|
63
|
-
const isHmr = !!request.
|
|
68
|
+
const isHmr = !!requestHeaders(request).get("X-RSC-HMR");
|
|
64
69
|
// HMR: clear the manifest cache so stale handler references are discarded.
|
|
65
70
|
if (isHmr) {
|
|
66
71
|
clearManifestCache();
|
|
@@ -150,6 +155,26 @@ export async function createMatchContextForFull<TEnv>(
|
|
|
150
155
|
Store.metrics = metricsStore;
|
|
151
156
|
}
|
|
152
157
|
|
|
158
|
+
// A shell capture's implicit doc scope, or a document HIT tail's (which
|
|
159
|
+
// replaces any route-derived scope: the tail replays the shell's doc
|
|
160
|
+
// record), or else the route's own scope.
|
|
161
|
+
const cacheScope = resolveShellImplicitCacheScope(snapshot.cacheScope);
|
|
162
|
+
// A scope means a route cache() record may be written: arm its tag owners
|
|
163
|
+
// before any handler runs (#957). A document HIT tail writes no record and
|
|
164
|
+
// runs no handler, so it arms nothing. A loader bound with its own cache()
|
|
165
|
+
// needs the per-execution loader tag sets before any loader runs (#964):
|
|
166
|
+
// loaders still run on a HIT.
|
|
167
|
+
if (cacheScope && !_getRequestContext()?._shellImplicitCache?.docTail) {
|
|
168
|
+
armRecordTagOwners();
|
|
169
|
+
}
|
|
170
|
+
if (bindsLoaderCache(snapshot.entries)) armLoaderTagSets();
|
|
171
|
+
// A shell capture credits a push under a live-lane loader's body to that
|
|
172
|
+
// loader (shell-capture.ts deriveShellCaptureContext). The lanes are set
|
|
173
|
+
// before any loader runs: a bake-lane loader can start a later one.
|
|
174
|
+
if (reqCtx?._shellCaptureRun) {
|
|
175
|
+
reqCtx._shellCaptureLoaderLanes = routeLoaderLanes(snapshot.entries);
|
|
176
|
+
}
|
|
177
|
+
|
|
153
178
|
return {
|
|
154
179
|
request,
|
|
155
180
|
url: cleanUrl,
|
|
@@ -172,23 +197,23 @@ export async function createMatchContextForFull<TEnv>(
|
|
|
172
197
|
metricsStore,
|
|
173
198
|
Store,
|
|
174
199
|
interceptContextMatch: null,
|
|
175
|
-
interceptSelectorContext: {
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
200
|
+
interceptSelectorContext: (() => {
|
|
201
|
+
const here = {
|
|
202
|
+
url: cleanUrl,
|
|
203
|
+
params: matched.params,
|
|
204
|
+
routeName: publicRouteName(matched.routeKey),
|
|
205
|
+
};
|
|
206
|
+
return {
|
|
207
|
+
from: here,
|
|
208
|
+
to: here,
|
|
209
|
+
request,
|
|
210
|
+
env,
|
|
211
|
+
segments: { path: [], ids: [] },
|
|
212
|
+
};
|
|
213
|
+
})(),
|
|
187
214
|
isSameRouteNavigation: false,
|
|
188
215
|
interceptResult: null,
|
|
189
|
-
|
|
190
|
-
// substitute an implicit doc-level scope (a route-derived scope wins).
|
|
191
|
-
cacheScope: resolveShellImplicitCacheScope(snapshot.cacheScope),
|
|
216
|
+
cacheScope,
|
|
192
217
|
isIntercept: false,
|
|
193
218
|
actionContext: undefined,
|
|
194
219
|
isAction: false,
|
|
@@ -217,7 +242,7 @@ export async function createMatchContextForPartial<TEnv>(
|
|
|
217
242
|
|
|
218
243
|
const metricsStore = deps.getMetricsStore();
|
|
219
244
|
|
|
220
|
-
const isHmr = !!request.
|
|
245
|
+
const isHmr = !!requestHeaders(request).get("X-RSC-HMR");
|
|
221
246
|
|
|
222
247
|
// HMR: clear manifest cache so stale handler references are discarded
|
|
223
248
|
if (isHmr) {
|
|
@@ -335,31 +360,27 @@ export async function createMatchContextForPartial<TEnv>(
|
|
|
335
360
|
});
|
|
336
361
|
}
|
|
337
362
|
|
|
338
|
-
setRequestContextPrevRouteKey(
|
|
339
|
-
nav.effectiveFromMatch?.routeKey,
|
|
340
|
-
nav.effectiveFromUrl,
|
|
341
|
-
nav.effectiveFromMatch?.params ?? nav.prevParams,
|
|
342
|
-
);
|
|
363
|
+
setRequestContextPrevRouteKey(nav.effectiveFromMatch?.routeKey);
|
|
343
364
|
|
|
344
365
|
const interceptSelectorContext: InterceptSelectorContext = {
|
|
345
|
-
from
|
|
346
|
-
|
|
347
|
-
|
|
366
|
+
// The source's params come from matching its URL (the intercept source
|
|
367
|
+
// while one is open), like its route name.
|
|
368
|
+
from: {
|
|
369
|
+
url: nav.effectiveFromUrl,
|
|
370
|
+
params: nav.effectiveFromMatch?.params ?? {},
|
|
371
|
+
routeName: publicRouteName(nav.effectiveFromMatch?.routeKey),
|
|
372
|
+
},
|
|
373
|
+
to: {
|
|
374
|
+
url: cleanUrl,
|
|
375
|
+
params: matched.params,
|
|
376
|
+
routeName: publicRouteName(matched.routeKey),
|
|
377
|
+
},
|
|
348
378
|
request,
|
|
349
379
|
env,
|
|
350
380
|
segments: {
|
|
351
381
|
path: nav.effectiveFromUrl.pathname.split("/").filter(Boolean),
|
|
352
382
|
ids: nav.filteredSegmentIds,
|
|
353
383
|
},
|
|
354
|
-
fromRouteName:
|
|
355
|
-
nav.effectiveFromMatch?.routeKey &&
|
|
356
|
-
!isAutoGeneratedRouteName(nav.effectiveFromMatch.routeKey)
|
|
357
|
-
? (nav.effectiveFromMatch.routeKey as DefaultRouteName)
|
|
358
|
-
: undefined,
|
|
359
|
-
toRouteName:
|
|
360
|
-
matched.routeKey && !isAutoGeneratedRouteName(matched.routeKey)
|
|
361
|
-
? (matched.routeKey as DefaultRouteName)
|
|
362
|
-
: undefined,
|
|
363
384
|
};
|
|
364
385
|
const isAction = !!actionContext;
|
|
365
386
|
|
|
@@ -400,7 +421,37 @@ export async function createMatchContextForPartial<TEnv>(
|
|
|
400
421
|
clientSegmentSet.delete(snapshot.manifestEntry.shortCode);
|
|
401
422
|
}
|
|
402
423
|
|
|
424
|
+
// Whether an intercept targets this route at all: the same walk as above
|
|
425
|
+
// with isAction=true, which skips when(). If one does, the response depends
|
|
426
|
+
// on the source even when none applied (a when() that rejected it, a
|
|
427
|
+
// same-route navigation), so it must not share the source-agnostic prefetch
|
|
428
|
+
// slot (#1007). The walk is over the TARGET's chain, so a route no
|
|
429
|
+
// intercept targets never depends on the source this way.
|
|
430
|
+
const targetsIntercept = (routeKey: string): boolean =>
|
|
431
|
+
findInterceptForRoute(
|
|
432
|
+
routeKey,
|
|
433
|
+
snapshot.manifestEntry.parent,
|
|
434
|
+
null,
|
|
435
|
+
true,
|
|
436
|
+
) !== null;
|
|
437
|
+
const interceptTargeted =
|
|
438
|
+
!!interceptResult ||
|
|
439
|
+
(!skipInterceptForAction &&
|
|
440
|
+
(targetsIntercept(matched.routeKey) ||
|
|
441
|
+
(snapshot.localRouteName !== matched.routeKey &&
|
|
442
|
+
targetsIntercept(snapshot.localRouteName))));
|
|
443
|
+
|
|
403
444
|
const isIntercept = !!interceptResult;
|
|
445
|
+
// A normal-route navigation may replay a PPR shell's canonical segment
|
|
446
|
+
// snapshot. Intercepts remain source-dependent and always use their normal
|
|
447
|
+
// cache path.
|
|
448
|
+
const cacheScope = isIntercept
|
|
449
|
+
? snapshot.cacheScope
|
|
450
|
+
: resolveShellImplicitCacheScope(snapshot.cacheScope);
|
|
451
|
+
// A scope means a route cache() record may be written: arm its tag owners
|
|
452
|
+
// before any handler runs (#957); see the full-match site for #964's arm.
|
|
453
|
+
if (cacheScope) armRecordTagOwners();
|
|
454
|
+
if (bindsLoaderCache(snapshot.entries)) armLoaderTagSets();
|
|
404
455
|
|
|
405
456
|
return {
|
|
406
457
|
request,
|
|
@@ -427,12 +478,8 @@ export async function createMatchContextForPartial<TEnv>(
|
|
|
427
478
|
interceptSelectorContext,
|
|
428
479
|
isSameRouteNavigation: nav.isSameRouteNavigation,
|
|
429
480
|
interceptResult,
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
// cache path.
|
|
433
|
-
cacheScope: isIntercept
|
|
434
|
-
? snapshot.cacheScope
|
|
435
|
-
: resolveShellImplicitCacheScope(snapshot.cacheScope),
|
|
481
|
+
interceptTargeted,
|
|
482
|
+
cacheScope,
|
|
436
483
|
isIntercept,
|
|
437
484
|
actionContext,
|
|
438
485
|
isAction,
|
|
@@ -176,6 +176,11 @@ export interface MatchContext<TEnv = any> {
|
|
|
176
176
|
interceptSelectorContext: InterceptSelectorContext;
|
|
177
177
|
isSameRouteNavigation: boolean;
|
|
178
178
|
interceptResult: InterceptResult | null;
|
|
179
|
+
/**
|
|
180
|
+
* An intercept targets this route, matched or not (a same-route navigation
|
|
181
|
+
* skips it), so the response depends on the source. Partial matches only.
|
|
182
|
+
*/
|
|
183
|
+
interceptTargeted?: boolean;
|
|
179
184
|
|
|
180
185
|
// Cache
|
|
181
186
|
cacheScope: CacheScope | null;
|
|
@@ -9,7 +9,6 @@ import type {
|
|
|
9
9
|
NotFoundBoundaryHandler,
|
|
10
10
|
} from "../types";
|
|
11
11
|
import {
|
|
12
|
-
isPprEntry,
|
|
13
12
|
type EntryData,
|
|
14
13
|
type InterceptEntry,
|
|
15
14
|
type InterceptSelectorContext,
|
|
@@ -47,7 +46,8 @@ import {
|
|
|
47
46
|
buildCacheSignalSegments,
|
|
48
47
|
} from "./telemetry.js";
|
|
49
48
|
import { _getRequestContext } from "../server/request-context.js";
|
|
50
|
-
import {
|
|
49
|
+
import { ShellRecordUnavailableError } from "../cache/shell-snapshot.js";
|
|
50
|
+
import { recordTransitionWhenRefs } from "./transition-when.js";
|
|
51
51
|
|
|
52
52
|
/**
|
|
53
53
|
* Per-call telemetry lifecycle emitter for match()/matchPartial(). Each method
|
|
@@ -234,33 +234,14 @@ export function createMatchHandlers<TEnv = any>(
|
|
|
234
234
|
if (reqCtx) reqCtx._cacheSignal = segments;
|
|
235
235
|
};
|
|
236
236
|
|
|
237
|
-
const
|
|
238
|
-
ctx: MatchContext<TEnv>,
|
|
239
|
-
isPartial: boolean,
|
|
240
|
-
): void => {
|
|
237
|
+
const recordTransitionWhenRefsForMatch = (ctx: MatchContext<TEnv>): void => {
|
|
241
238
|
const reqCtx = _getRequestContext();
|
|
242
239
|
if (!reqCtx) return;
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
reqCtx,
|
|
249
|
-
{
|
|
250
|
-
params: ctx.matched.params,
|
|
251
|
-
routeName: ctx.interceptSelectorContext.toRouteName,
|
|
252
|
-
},
|
|
253
|
-
(error, segmentId) =>
|
|
254
|
-
callOnError(error, "rendering", {
|
|
255
|
-
request: ctx.request,
|
|
256
|
-
url: ctx.url,
|
|
257
|
-
env: ctx.env,
|
|
258
|
-
params: ctx.matched.params,
|
|
259
|
-
segmentId,
|
|
260
|
-
isPartial,
|
|
261
|
-
handledByBoundary: false,
|
|
262
|
-
}),
|
|
263
|
-
);
|
|
240
|
+
const entry = ctx.manifestEntry;
|
|
241
|
+
recordTransitionWhenRefs(ctx.entries, reqCtx, {
|
|
242
|
+
routeName: ctx.routeKey,
|
|
243
|
+
pattern: entry?.type === "route" ? entry.pattern : undefined,
|
|
244
|
+
});
|
|
264
245
|
};
|
|
265
246
|
|
|
266
247
|
async function createMatchContextForFull(
|
|
@@ -349,7 +330,7 @@ export function createMatchHandlers<TEnv = any>(
|
|
|
349
330
|
}
|
|
350
331
|
|
|
351
332
|
const ctx = result as MatchContext<TEnv>;
|
|
352
|
-
|
|
333
|
+
recordTransitionWhenRefsForMatch(ctx);
|
|
353
334
|
|
|
354
335
|
try {
|
|
355
336
|
const state = createPipelineState();
|
|
@@ -380,6 +361,10 @@ export function createMatchHandlers<TEnv = any>(
|
|
|
380
361
|
emitter.end(0, false, error.status);
|
|
381
362
|
throw error;
|
|
382
363
|
}
|
|
364
|
+
// A document shell HIT tail's doc record could not supply the
|
|
365
|
+
// handler layer: a serve-path signal serveShellHit degrades, not a
|
|
366
|
+
// routing error (the record's decode failure was already reported).
|
|
367
|
+
if (error instanceof ShellRecordUnavailableError) throw error;
|
|
383
368
|
emitter.error(
|
|
384
369
|
error instanceof Error ? error : new Error(String(error)),
|
|
385
370
|
"routing",
|
|
@@ -461,7 +446,7 @@ export function createMatchHandlers<TEnv = any>(
|
|
|
461
446
|
emitter.end(0, false);
|
|
462
447
|
return null;
|
|
463
448
|
}
|
|
464
|
-
|
|
449
|
+
recordTransitionWhenRefsForMatch(ctx);
|
|
465
450
|
|
|
466
451
|
if (isRouterDebugEnabled()) {
|
|
467
452
|
startRevalidationTrace({
|