@rangojs/router 0.27.0 → 0.29.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/dist/bin/rango.js +1 -1
- package/dist/types/__internal.d.ts +7 -1
- package/dist/types/browser/action-fence.d.ts +2 -1
- package/dist/types/browser/event-controller.d.ts +8 -2
- package/dist/types/browser/history-state.d.ts +3 -9
- package/dist/types/browser/navigation-store.d.ts +8 -6
- package/dist/types/browser/partial-update.d.ts +11 -0
- package/dist/types/browser/react/filter-segment-order.d.ts +2 -2
- package/dist/types/browser/react/use-action.d.ts +7 -2
- package/dist/types/browser/react/use-handle.d.ts +19 -16
- package/dist/types/browser/react/use-link-status.d.ts +5 -2
- package/dist/types/browser/segment-reconciler.d.ts +32 -4
- package/dist/types/browser/types.d.ts +24 -5
- package/dist/types/browser/wire-protocol.d.ts +26 -0
- package/dist/types/cache/cache-key-utils.d.ts +11 -5
- package/dist/types/cache/cache-scope.d.ts +34 -22
- package/dist/types/cache/cf/cf-cache-constants.d.ts +2 -2
- package/dist/types/cache/cf/cf-cache-store.d.ts +47 -65
- package/dist/types/cache/cf/cf-kv-utils.d.ts +1 -1
- package/dist/types/cache/handle-snapshot.d.ts +9 -4
- package/dist/types/cache/segment-codec.d.ts +14 -0
- package/dist/types/cache/shell-snapshot.d.ts +1 -1
- package/dist/types/cache/taint.d.ts +5 -3
- package/dist/types/cache/types.d.ts +7 -3
- package/dist/types/cache/vercel/vercel-cache-store.d.ts +8 -6
- package/dist/types/prefetch-markers.d.ts +5 -14
- package/dist/types/prerender/store.d.ts +31 -0
- package/dist/types/prerender/warm-request.d.ts +3 -3
- package/dist/types/prerender.d.ts +2 -2
- package/dist/types/route-name.d.ts +5 -0
- package/dist/types/router/error-handling.d.ts +11 -5
- package/dist/types/router/instrument.d.ts +4 -3
- package/dist/types/router/intercept-resolution.d.ts +25 -9
- package/dist/types/router/match-context.d.ts +6 -11
- package/dist/types/router/match-handlers.d.ts +2 -6
- package/dist/types/router/match-middleware/background-revalidation.d.ts +13 -22
- package/dist/types/router/match-middleware/cache-lookup.d.ts +34 -86
- package/dist/types/router/match-middleware/segment-resolution.d.ts +8 -6
- package/dist/types/router/navigation-snapshot.d.ts +7 -0
- package/dist/types/router/prerender-match.d.ts +21 -6
- package/dist/types/router/preview-match.d.ts +2 -7
- package/dist/types/router/request-classification.d.ts +5 -0
- package/dist/types/router/router-context.d.ts +7 -46
- package/dist/types/router/router-interfaces.d.ts +8 -14
- package/dist/types/router/segment-resolution/helpers.d.ts +19 -3
- package/dist/types/router/segment-resolution/loader-cache.d.ts +3 -3
- package/dist/types/router/segment-resolution/loader-mask.d.ts +4 -4
- package/dist/types/router/segment-resolution/prefetch-deferral.d.ts +20 -18
- package/dist/types/router/segment-resolution/resolution-pass.d.ts +95 -0
- package/dist/types/router/segment-resolution/revalidation.d.ts +82 -31
- package/dist/types/router/segment-resolution/streamed-handler-boundary.d.ts +2 -0
- package/dist/types/router/segment-resolution.d.ts +1 -2
- package/dist/types/router/segment-wrappers.d.ts +11 -48
- package/dist/types/router/telemetry.d.ts +2 -0
- package/dist/types/router/types.d.ts +11 -1
- package/dist/types/rsc/full-payload.d.ts +2 -2
- package/dist/types/rsc/handler-context.d.ts +6 -6
- package/dist/types/rsc/json-route-result.d.ts +5 -9
- package/dist/types/rsc/request-stages.d.ts +203 -0
- package/dist/types/rsc/response-cache-serve.d.ts +7 -12
- package/dist/types/rsc/response-route-handler.d.ts +2 -2
- package/dist/types/rsc/routine-plan.d.ts +1 -1
- package/dist/types/rsc/server-action.d.ts +2 -2
- package/dist/types/rsc/shell-capture-constants.d.ts +1 -1
- package/dist/types/rsc/shell-capture.d.ts +132 -15
- package/dist/types/rsc/shell-serve.d.ts +37 -12
- package/dist/types/server/context.d.ts +9 -8
- package/dist/types/server/handle-store.d.ts +16 -0
- package/dist/types/server/render-pass.d.ts +72 -0
- package/dist/types/server/request-context.d.ts +53 -104
- package/dist/types/server/rewrite-trail.d.ts +2 -2
- package/dist/types/testing/dispatch.d.ts +35 -18
- package/dist/types/testing/render-route.d.ts +43 -8
- package/dist/types/testing/serve-shell-request.d.ts +8 -1
- package/dist/types/types/cache-types.d.ts +6 -3
- package/dist/types/types/error-types.d.ts +30 -3
- package/dist/types/types/handler-context.d.ts +9 -1
- package/dist/types/types/segments.d.ts +1 -1
- package/dist/types/vite/discovery/build-discovery-phase.d.ts +40 -0
- package/dist/types/vite/discovery/build-env.d.ts +22 -0
- package/dist/types/vite/discovery/bundle-scan-phase.d.ts +12 -0
- package/dist/types/vite/discovery/dev-prerender-cache.d.ts +3 -2
- package/dist/types/vite/discovery/dev-server-phase.d.ts +26 -0
- package/dist/types/vite/discovery/discovery-errors.d.ts +1 -1
- package/dist/types/vite/discovery/state.d.ts +12 -0
- package/dist/types/vite/plugins/version-plugin.d.ts +1 -1
- package/dist/types/vite/plugins/virtual-entries.d.ts +1 -1
- package/dist/types/vite/router-discovery.d.ts +8 -47
- package/dist/types/vite/temp-rsc-server.d.ts +39 -0
- package/dist/types/vite/utils/prerender-utils.d.ts +9 -0
- package/dist/types/wire-params.d.ts +39 -0
- package/dist/vite/index.js +3822 -3727
- package/package.json +1 -1
- package/skills/cache-guide/SKILL.md +8 -5
- package/skills/caching/SKILL.md +37 -18
- package/skills/client-urls/SKILL.md +4 -2
- package/skills/document-cache/SKILL.md +13 -1
- package/skills/handler-use/SKILL.md +1 -1
- package/skills/hooks/SKILL.md +1 -1
- package/skills/hooks/data.md +8 -1
- package/skills/hooks/handle-and-actions.md +33 -2
- package/skills/hooks/navigation.md +121 -15
- package/skills/hooks/state.md +6 -4
- package/skills/hooks/urls.md +16 -5
- package/skills/intercept/SKILL.md +23 -7
- package/skills/middleware/SKILL.md +18 -0
- package/skills/observability/SKILL.md +30 -0
- package/skills/ppr/SKILL.md +18 -3
- package/skills/prerender/SKILL.md +39 -15
- package/skills/rango/SKILL.md +5 -1
- package/skills/server-actions/SKILL.md +15 -1
- package/skills/testing/client-components.md +25 -18
- package/skills/testing/response-routes.md +6 -4
- package/skills/testing/serve-shell-request.md +7 -7
- package/skills/use-cache/SKILL.md +46 -8
- package/skills/view-transitions/SKILL.md +1 -1
- package/src/__internal.ts +7 -1
- package/src/browser/action-fence.ts +2 -1
- package/src/browser/connection-warmup.ts +3 -1
- package/src/browser/event-controller.ts +37 -13
- package/src/browser/history-state.ts +6 -12
- package/src/browser/navigation-bridge.ts +59 -26
- package/src/browser/navigation-client.ts +20 -25
- package/src/browser/navigation-store.ts +25 -27
- package/src/browser/navigation-transaction.ts +4 -1
- package/src/browser/network-error-handler.ts +4 -0
- package/src/browser/partial-update.ts +33 -6
- package/src/browser/prefetch/cache.ts +2 -1
- package/src/browser/prefetch/fetch.ts +8 -16
- package/src/browser/prefetch/queue.ts +4 -4
- package/src/browser/react/NavigationProvider.tsx +2 -1
- package/src/browser/react/filter-segment-order.ts +2 -2
- package/src/browser/react/use-action.ts +24 -48
- package/src/browser/react/use-handle.ts +22 -18
- package/src/browser/react/use-link-status.ts +48 -60
- package/src/browser/react/use-navigation.ts +92 -41
- package/src/browser/rsc-router.tsx +1 -0
- package/src/browser/segment-reconciler.ts +109 -53
- package/src/browser/server-action-bridge.ts +81 -61
- package/src/browser/types.ts +25 -5
- package/src/browser/wire-protocol.ts +43 -0
- package/src/cache/cache-key-utils.ts +11 -5
- package/src/cache/cache-runtime.ts +132 -75
- package/src/cache/cache-scope.ts +110 -64
- package/src/cache/cf/cf-cache-constants.ts +2 -2
- package/src/cache/cf/cf-cache-store.ts +486 -874
- package/src/cache/cf/cf-kv-utils.ts +1 -1
- package/src/cache/document-cache.ts +64 -46
- package/src/cache/handle-snapshot.ts +19 -7
- package/src/cache/read-through-swr.ts +20 -18
- package/src/cache/segment-codec.ts +55 -0
- package/src/cache/shell-snapshot.ts +2 -2
- package/src/cache/tag-invalidation.ts +6 -2
- package/src/cache/taint.ts +5 -3
- package/src/cache/types.ts +7 -3
- package/src/cache/vercel/vercel-cache-store.ts +243 -310
- package/src/handles/pushed-by.ts +1 -1
- package/src/prefetch-markers.ts +5 -16
- package/src/prerender/store.ts +76 -0
- package/src/prerender/warm-request.ts +8 -4
- package/src/prerender.ts +2 -2
- package/src/route-name.ts +8 -0
- package/src/router/error-handling.ts +22 -10
- package/src/router/instrument.ts +4 -3
- package/src/router/intercept-resolution.ts +82 -75
- package/src/router/loader-resolution.ts +5 -4
- package/src/router/match-api.ts +5 -5
- package/src/router/match-context.ts +23 -12
- package/src/router/match-handlers.ts +8 -20
- package/src/router/match-middleware/background-revalidation.ts +46 -57
- package/src/router/match-middleware/cache-lookup.ts +677 -726
- package/src/router/match-middleware/cache-store.ts +4 -8
- package/src/router/match-middleware/intercept-resolution.ts +3 -23
- package/src/router/match-middleware/segment-resolution.ts +34 -61
- package/src/router/middleware.ts +69 -11
- package/src/router/navigation-snapshot.ts +14 -4
- package/src/router/prerender-match.ts +100 -44
- package/src/router/preview-match.ts +6 -41
- package/src/router/request-classification.ts +13 -7
- package/src/router/route-snapshot.ts +2 -5
- package/src/router/router-context.ts +10 -95
- package/src/router/router-interfaces.ts +8 -14
- package/src/router/segment-resolution/helpers.ts +49 -13
- package/src/router/segment-resolution/loader-cache.ts +15 -9
- package/src/router/segment-resolution/loader-mask.ts +5 -5
- package/src/router/segment-resolution/prefetch-deferral.ts +30 -27
- package/src/router/segment-resolution/resolution-pass.ts +139 -0
- package/src/router/segment-resolution/revalidation.ts +939 -789
- package/src/router/segment-resolution/streamed-handler-boundary.ts +3 -0
- package/src/router/segment-resolution.ts +4 -13
- package/src/router/segment-wrappers.ts +51 -269
- package/src/router/telemetry.ts +5 -0
- package/src/router/types.ts +11 -1
- package/src/router.ts +19 -17
- package/src/rsc/full-payload.ts +6 -5
- package/src/rsc/handler-context.ts +8 -14
- package/src/rsc/handler.ts +126 -507
- package/src/rsc/json-route-result.ts +5 -9
- package/src/rsc/loader-fetch.ts +6 -5
- package/src/rsc/progressive-enhancement.ts +5 -0
- package/src/rsc/request-stages.ts +759 -0
- package/src/rsc/response-cache-serve.ts +10 -14
- package/src/rsc/response-route-handler.ts +13 -5
- package/src/rsc/rewrite-passes.ts +2 -1
- package/src/rsc/routine-plan.ts +1 -1
- package/src/rsc/rsc-rendering.ts +164 -349
- package/src/rsc/server-action.ts +87 -113
- package/src/rsc/shell-capture-constants.ts +1 -1
- package/src/rsc/shell-capture.ts +275 -227
- package/src/rsc/shell-serve.ts +121 -18
- package/src/rsc/ssr-setup.ts +5 -4
- package/src/segment-system.tsx +3 -3
- package/src/server/context.ts +22 -25
- package/src/server/handle-store.ts +55 -0
- package/src/server/render-pass.ts +291 -0
- package/src/server/request-context.ts +75 -115
- package/src/server/rewrite-trail.ts +6 -4
- package/src/ssr/index.tsx +1 -1
- package/src/testing/dispatch.ts +226 -710
- package/src/testing/e2e/page-helpers.ts +2 -2
- package/src/testing/render-route.tsx +199 -13
- package/src/testing/serve-shell-request.ts +36 -40
- package/src/types/cache-types.ts +6 -3
- package/src/types/error-types.ts +30 -3
- package/src/types/handler-context.ts +12 -1
- package/src/types/segments.ts +1 -1
- package/src/vite/discovery/build-discovery-phase.ts +204 -0
- package/src/vite/discovery/build-env.ts +148 -0
- package/src/vite/discovery/bundle-scan-phase.ts +128 -0
- package/src/vite/discovery/dev-prerender-cache.ts +8 -3
- package/src/vite/discovery/dev-server-phase.ts +1776 -0
- package/src/vite/discovery/discover-routers.ts +1 -1
- package/src/vite/discovery/discovery-errors.ts +1 -1
- package/src/vite/discovery/prerender-collection.ts +24 -39
- package/src/vite/discovery/state.ts +15 -0
- package/src/vite/plugins/version-plugin.ts +1 -1
- package/src/vite/plugins/virtual-entries.ts +1 -1
- package/src/vite/router-discovery.ts +29 -2428
- package/src/vite/temp-rsc-server.ts +223 -0
- package/src/vite/utils/prerender-utils.ts +47 -0
- package/src/wire-params.ts +40 -0
- package/dist/types/router/segment-resolution/fresh.d.ts +0 -72
- package/src/router/segment-resolution/fresh.ts +0 -945
package/src/rsc/handler.ts
CHANGED
|
@@ -6,19 +6,15 @@
|
|
|
6
6
|
* and progressive enhancement (no-JS form submissions).
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import { requestHeaders } from "../server/request-headers.js";
|
|
10
9
|
import { isRouteNotFoundError } from "../errors.js";
|
|
11
10
|
import { matchMiddleware, executeMiddleware } from "../router/middleware.js";
|
|
12
11
|
import {
|
|
13
12
|
runWithRequestContext,
|
|
14
|
-
setRequestContextParams,
|
|
15
13
|
getRequestContext,
|
|
16
14
|
_getRequestContext,
|
|
17
15
|
createRequestContext,
|
|
18
16
|
captureRequestEntryContext,
|
|
19
17
|
} from "../server/request-context.js";
|
|
20
|
-
import { REWRITE_HEADER, runRewritePasses } from "./rewrite-passes.js";
|
|
21
|
-
import { rewriteTrail } from "../server/rewrite-trail.js";
|
|
22
18
|
import { payloadLocation } from "./full-payload.js";
|
|
23
19
|
import * as rscDeps from "@vitejs/plugin-rsc/rsc/server";
|
|
24
20
|
import type {
|
|
@@ -36,7 +32,6 @@ import {
|
|
|
36
32
|
buildRouteMiddlewareEntries,
|
|
37
33
|
} from "./helpers.js";
|
|
38
34
|
import { renderRscFlightStage, renderRscResponse } from "./render-pipeline.js";
|
|
39
|
-
import { guardOutgoingRedirect } from "./redirect-guard.js";
|
|
40
35
|
import {
|
|
41
36
|
resolveSoftRedirectUrl,
|
|
42
37
|
resolveExternalRedirect,
|
|
@@ -48,21 +43,13 @@ import {
|
|
|
48
43
|
appendVaryAccept,
|
|
49
44
|
trySetHeader,
|
|
50
45
|
} from "../response-utils.js";
|
|
51
|
-
import {
|
|
52
|
-
handleResponseRoute,
|
|
53
|
-
type ResponseRouteMatch,
|
|
54
|
-
} from "./response-route-handler.js";
|
|
55
46
|
import { nonce as nonceToken, resolveProviderNonce } from "./nonce.js";
|
|
56
47
|
import {
|
|
57
48
|
resolvePrerenderVersion,
|
|
58
49
|
resolveRouterVersions,
|
|
59
50
|
} from "../server/build-version-table.js";
|
|
60
|
-
import type { ErrorPhase } from "../types.js";
|
|
61
51
|
import type { RouterRequestInput } from "../router/router-interfaces.js";
|
|
62
|
-
import {
|
|
63
|
-
invokeOnError,
|
|
64
|
-
resolveDefaultNotFound,
|
|
65
|
-
} from "../router/error-handling.js";
|
|
52
|
+
import { resolveDefaultNotFound } from "../router/error-handling.js";
|
|
66
53
|
import {
|
|
67
54
|
printServerError,
|
|
68
55
|
wasServerErrorPrinted,
|
|
@@ -76,7 +63,6 @@ import {
|
|
|
76
63
|
getRouterTrie,
|
|
77
64
|
} from "../route-map-builder.js";
|
|
78
65
|
import type { HandlerContext } from "./handler-context.js";
|
|
79
|
-
import type { CacheErrorCategory } from "../cache/cache-error.js";
|
|
80
66
|
import {
|
|
81
67
|
compileSearchParamsFilter,
|
|
82
68
|
type SearchParamsFilter,
|
|
@@ -91,7 +77,6 @@ import {
|
|
|
91
77
|
type ActionContinuation,
|
|
92
78
|
} from "./server-action.js";
|
|
93
79
|
import { handleLoaderFetch, renderLoaderError } from "./loader-fetch.js";
|
|
94
|
-
import { applyStreamIdleTimeout } from "./stream-idle.js";
|
|
95
80
|
import {
|
|
96
81
|
checkRequestOrigin,
|
|
97
82
|
ORIGIN_CHECK_PHASE_BY_MODE,
|
|
@@ -100,14 +85,7 @@ import { handleRscRendering } from "./rsc-rendering.js";
|
|
|
100
85
|
import { withoutShellMissMarker } from "./shell-serve.js";
|
|
101
86
|
import { readWarmMark } from "../prerender/warm-request.js";
|
|
102
87
|
import { defaultSSRModuleLoaderForTests } from "./ssr-module-loader.js";
|
|
103
|
-
import {
|
|
104
|
-
withTimeout,
|
|
105
|
-
isTimeoutEnabled,
|
|
106
|
-
RouterTimeoutError,
|
|
107
|
-
createDefaultTimeoutResponse,
|
|
108
|
-
type TimeoutPhase,
|
|
109
|
-
type RenderTimeoutContext,
|
|
110
|
-
} from "../router/timeout.js";
|
|
88
|
+
import { withTimeout, isTimeoutEnabled } from "../router/timeout.js";
|
|
111
89
|
import {
|
|
112
90
|
createMetricsStore,
|
|
113
91
|
appendMetric,
|
|
@@ -123,12 +101,21 @@ import {
|
|
|
123
101
|
SSR_SETUP_VAR,
|
|
124
102
|
type SSRSetup,
|
|
125
103
|
} from "./ssr-setup.js";
|
|
104
|
+
import type { ExecutableRequestPlan } from "../router/request-classification.js";
|
|
126
105
|
import {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
106
|
+
classify,
|
|
107
|
+
createErrorReporter,
|
|
108
|
+
finalizeRequestResponse,
|
|
109
|
+
originRejectedResponse,
|
|
110
|
+
recordPlanRoute,
|
|
111
|
+
reportBackgroundErrors,
|
|
112
|
+
responseRouteMatch,
|
|
113
|
+
runRequestPasses,
|
|
114
|
+
serveResponseRoute,
|
|
115
|
+
terminalPlanResponse,
|
|
116
|
+
timeoutResponse,
|
|
117
|
+
} from "./request-stages.js";
|
|
118
|
+
import { WIRE } from "../wire-params.js";
|
|
132
119
|
|
|
133
120
|
/**
|
|
134
121
|
* Create an RSC request handler.
|
|
@@ -218,27 +205,7 @@ export function createRSCHandler<
|
|
|
218
205
|
}))
|
|
219
206
|
: rawLoadSSRModule;
|
|
220
207
|
|
|
221
|
-
|
|
222
|
-
* Per-request error reporter that deduplicates via the ALS request context.
|
|
223
|
-
*
|
|
224
|
-
* Uses the same _reportedErrors WeakSet as the router layer so errors
|
|
225
|
-
* that propagate across layers are only reported once per request.
|
|
226
|
-
*/
|
|
227
|
-
function callOnError(
|
|
228
|
-
error: unknown,
|
|
229
|
-
phase: ErrorPhase,
|
|
230
|
-
context: Parameters<typeof invokeOnError<TEnv>>[3],
|
|
231
|
-
): void {
|
|
232
|
-
// Guard: abort signal handlers fire asynchronously outside the ALS
|
|
233
|
-
// request scope, so the context may be gone. Skip dedup in that
|
|
234
|
-
// case — the error is from a cancelled stream, not a real failure.
|
|
235
|
-
const reqCtx = _getRequestContext();
|
|
236
|
-
if (error != null && typeof error === "object" && reqCtx) {
|
|
237
|
-
if (reqCtx._reportedErrors.has(error)) return;
|
|
238
|
-
reqCtx._reportedErrors.add(error);
|
|
239
|
-
}
|
|
240
|
-
invokeOnError(router.onError, error, phase, context, "RSC");
|
|
241
|
-
}
|
|
208
|
+
const callOnError = createErrorReporter<TEnv>(router);
|
|
242
209
|
|
|
243
210
|
function getRequiredRouteMap(): Record<string, string> {
|
|
244
211
|
const routeMap = getRouterManifest(router.id);
|
|
@@ -250,112 +217,6 @@ export function createRSCHandler<
|
|
|
250
217
|
return routeMap;
|
|
251
218
|
}
|
|
252
219
|
|
|
253
|
-
/**
|
|
254
|
-
* Handle a timeout by reporting the error, emitting telemetry,
|
|
255
|
-
* and returning either the custom onTimeout response or a default 504.
|
|
256
|
-
*/
|
|
257
|
-
async function handleTimeoutResponse(
|
|
258
|
-
request: Request,
|
|
259
|
-
env: TEnv,
|
|
260
|
-
url: URL,
|
|
261
|
-
phase: TimeoutPhase,
|
|
262
|
-
durationMs: number,
|
|
263
|
-
routeKey?: string,
|
|
264
|
-
actionId?: string,
|
|
265
|
-
): Promise<Response> {
|
|
266
|
-
const timeoutError = new RouterTimeoutError(phase, durationMs);
|
|
267
|
-
const requestContext = _getRequestContext<TEnv>();
|
|
268
|
-
const cursor = requestContext?._renderForeground;
|
|
269
|
-
const render: RenderTimeoutContext | undefined =
|
|
270
|
-
phase === "render-start" && cursor
|
|
271
|
-
? {
|
|
272
|
-
mode: cursor.mode,
|
|
273
|
-
phase: cursor.phase,
|
|
274
|
-
state: cursor.state,
|
|
275
|
-
completed: cursor.completed,
|
|
276
|
-
total: cursor.total,
|
|
277
|
-
...(cursor.phaseStartedAt !== undefined && {
|
|
278
|
-
phaseDurationMs: performance.now() - cursor.phaseStartedAt,
|
|
279
|
-
}),
|
|
280
|
-
}
|
|
281
|
-
: undefined;
|
|
282
|
-
const trace =
|
|
283
|
-
phase === "render-start" ? requestContext?._activeRoutine : undefined;
|
|
284
|
-
const activeEntries = trace?.active() ?? [];
|
|
285
|
-
const activeStep = activeEntries.at(-1);
|
|
286
|
-
const activeAt = performance.now();
|
|
287
|
-
const routineSnapshot =
|
|
288
|
-
trace && activeStep
|
|
289
|
-
? {
|
|
290
|
-
name: trace.name,
|
|
291
|
-
path: activeEntries.map((entry) => entry.name),
|
|
292
|
-
durationMs: activeAt - activeStep.startedAt,
|
|
293
|
-
}
|
|
294
|
-
: undefined;
|
|
295
|
-
|
|
296
|
-
if (INTERNAL_RANGO_DEBUG && trace && activeStep) {
|
|
297
|
-
console.log(
|
|
298
|
-
`[routine] TIMEOUT ${request.method} ${url.pathname} (${trace.name})\n${trace.formatActive(activeEntries, activeAt)}`,
|
|
299
|
-
);
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
// Each surface gets its OWN shallow render snapshot. A consumer that mutates
|
|
303
|
-
// its copy must not corrupt what the other two observe. The internal-debug
|
|
304
|
-
// routine snapshot is bounded metadata sent only to onError.
|
|
305
|
-
callOnError(timeoutError, phase === "action" ? "action" : "handler", {
|
|
306
|
-
request,
|
|
307
|
-
url,
|
|
308
|
-
env,
|
|
309
|
-
routeKey,
|
|
310
|
-
actionId,
|
|
311
|
-
handledByBoundary: false,
|
|
312
|
-
metadata: {
|
|
313
|
-
timeout: true,
|
|
314
|
-
phase,
|
|
315
|
-
durationMs,
|
|
316
|
-
...(render && { render: { ...render } }),
|
|
317
|
-
...(routineSnapshot && { routine: routineSnapshot }),
|
|
318
|
-
},
|
|
319
|
-
});
|
|
320
|
-
|
|
321
|
-
if (router.telemetry) {
|
|
322
|
-
safeEmit(resolveSink(router.telemetry), {
|
|
323
|
-
type: "request.timeout",
|
|
324
|
-
timestamp: performance.now(),
|
|
325
|
-
requestId: getRequestId(request),
|
|
326
|
-
phase,
|
|
327
|
-
pathname: url.pathname,
|
|
328
|
-
routeKey,
|
|
329
|
-
actionId,
|
|
330
|
-
durationMs,
|
|
331
|
-
customHandler: !!router.onTimeout,
|
|
332
|
-
...(render && { render: { ...render } }),
|
|
333
|
-
});
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
if (router.onTimeout) {
|
|
337
|
-
try {
|
|
338
|
-
return await router.onTimeout({
|
|
339
|
-
phase,
|
|
340
|
-
request,
|
|
341
|
-
url,
|
|
342
|
-
env,
|
|
343
|
-
routeKey,
|
|
344
|
-
actionId,
|
|
345
|
-
durationMs,
|
|
346
|
-
...(render && { render: { ...render } }),
|
|
347
|
-
});
|
|
348
|
-
} catch (e) {
|
|
349
|
-
if (process.env.NODE_ENV !== "production") {
|
|
350
|
-
console.error("[RSC] onTimeout callback error:", e);
|
|
351
|
-
}
|
|
352
|
-
return createDefaultTimeoutResponse(phase);
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
return createDefaultTimeoutResponse(phase);
|
|
357
|
-
}
|
|
358
|
-
|
|
359
220
|
/**
|
|
360
221
|
* Build a 200 Flight response that carries a redirect URL and optional state.
|
|
361
222
|
* Used when a partial/action request results in a redirect -- fetch
|
|
@@ -413,7 +274,7 @@ export function createRSCHandler<
|
|
|
413
274
|
env: reqCtx.env,
|
|
414
275
|
payload: redirectPayload,
|
|
415
276
|
tracking: {
|
|
416
|
-
mode: reqCtx.url.searchParams.has(
|
|
277
|
+
mode: reqCtx.url.searchParams.has(WIRE.action)
|
|
417
278
|
? "action-revalidation"
|
|
418
279
|
: "partial",
|
|
419
280
|
routeKey: reqCtx._routeName,
|
|
@@ -476,7 +337,7 @@ export function createRSCHandler<
|
|
|
476
337
|
// Connection warmup: return 204 immediately before any processing
|
|
477
338
|
if (router?.warmupEnabled && request.method === "HEAD") {
|
|
478
339
|
const warmupUrl = new URL(request.url);
|
|
479
|
-
if (warmupUrl.searchParams.has(
|
|
340
|
+
if (warmupUrl.searchParams.has(WIRE.warmup)) {
|
|
480
341
|
return new Response(null, { status: 204 });
|
|
481
342
|
}
|
|
482
343
|
}
|
|
@@ -637,18 +498,7 @@ export function createRSCHandler<
|
|
|
637
498
|
requestContext._debugPerformance = true;
|
|
638
499
|
requestContext._metricsStore = earlyMetricsStore;
|
|
639
500
|
}
|
|
640
|
-
|
|
641
|
-
// can surface non-fatal errors through the router's onError callback.
|
|
642
|
-
requestContext._reportBackgroundError = (
|
|
643
|
-
error: unknown,
|
|
644
|
-
category: CacheErrorCategory,
|
|
645
|
-
) => {
|
|
646
|
-
callOnError(error, "cache", {
|
|
647
|
-
request,
|
|
648
|
-
url,
|
|
649
|
-
metadata: { category },
|
|
650
|
-
});
|
|
651
|
-
};
|
|
501
|
+
reportBackgroundErrors(requestContext, request, url, callOnError);
|
|
652
502
|
|
|
653
503
|
const ctxCreateDur = performance.now() - ctxCreateStart;
|
|
654
504
|
|
|
@@ -660,9 +510,8 @@ export function createRSCHandler<
|
|
|
660
510
|
`handler-ctx-create;dur=${ctxCreateDur.toFixed(2)}`,
|
|
661
511
|
];
|
|
662
512
|
|
|
663
|
-
//
|
|
664
|
-
|
|
665
|
-
variables.__handlerStart = handlerStart;
|
|
513
|
+
// The core handler adds its classify timing (coreRequestHandler).
|
|
514
|
+
requestContext._handlerTiming = handlerTiming;
|
|
666
515
|
|
|
667
516
|
// Wrap entire request handling in request context
|
|
668
517
|
// Makes context available via getRequestContext() throughout:
|
|
@@ -693,57 +542,29 @@ export function createRSCHandler<
|
|
|
693
542
|
// latter is reserved for the low-cardinality template (OTel convention).
|
|
694
543
|
span.setAttribute("url.path", url.pathname);
|
|
695
544
|
|
|
696
|
-
//
|
|
697
|
-
//
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
const { response: downstream, rewrittenTo } = await runRewritePasses({
|
|
545
|
+
// The global middleware passes (rsc/request-stages.ts). The response
|
|
546
|
+
// is finalized below, inside the response span.
|
|
547
|
+
const {
|
|
548
|
+
response: downstream,
|
|
549
|
+
rewrittenTo,
|
|
550
|
+
hasMiddleware,
|
|
551
|
+
} = await runRequestPasses({
|
|
552
|
+
ctx: handlerCtx,
|
|
705
553
|
reqCtx: requestContext,
|
|
706
554
|
request,
|
|
707
555
|
url,
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
? matchedMiddleware
|
|
714
|
-
: matchMiddleware(
|
|
715
|
-
requestContext.url.pathname,
|
|
716
|
-
router.middleware,
|
|
717
|
-
);
|
|
718
|
-
hasMiddleware = passMiddleware.length > 0;
|
|
719
|
-
const coreHandler = (): Promise<Response> =>
|
|
720
|
-
coreRequestHandler(passRequest, env, passUrl, variables, nonce);
|
|
721
|
-
return hasMiddleware
|
|
722
|
-
? executeMiddleware(
|
|
723
|
-
passMiddleware,
|
|
724
|
-
passRequest,
|
|
725
|
-
env,
|
|
726
|
-
variables,
|
|
727
|
-
coreHandler,
|
|
728
|
-
createReverseFunction(getRequiredRouteMap()),
|
|
729
|
-
)
|
|
730
|
-
: coreHandler();
|
|
731
|
-
},
|
|
556
|
+
env,
|
|
557
|
+
variables,
|
|
558
|
+
firstPassMiddleware: matchedMiddleware,
|
|
559
|
+
core: (passRequest, passUrl) =>
|
|
560
|
+
coreRequestHandler(passRequest, env, passUrl, variables, nonce),
|
|
732
561
|
onHop: (pass) => {
|
|
733
562
|
span.setAttribute("rango.rewrite.target", pass.target.pathname);
|
|
734
563
|
span.setAttribute("rango.rewrite.hops", pass.hops);
|
|
735
564
|
},
|
|
736
|
-
report: (error, at) => {
|
|
737
|
-
callOnError(error, "routing", {
|
|
738
|
-
request: at.request,
|
|
739
|
-
url: at.url,
|
|
740
|
-
env,
|
|
741
|
-
handledByBoundary: false,
|
|
742
|
-
});
|
|
743
|
-
},
|
|
744
565
|
// A loader fetch answers with the error `load()` rejects with.
|
|
745
566
|
refuse: (error) =>
|
|
746
|
-
url.searchParams.has(
|
|
567
|
+
url.searchParams.has(WIRE.loader)
|
|
747
568
|
? renderLoaderError(
|
|
748
569
|
handlerCtx,
|
|
749
570
|
{ message: (error as Error).message, name: "Error" },
|
|
@@ -752,7 +573,7 @@ export function createRSCHandler<
|
|
|
752
573
|
request,
|
|
753
574
|
url,
|
|
754
575
|
env,
|
|
755
|
-
loaderId: url.searchParams.get(
|
|
576
|
+
loaderId: url.searchParams.get(WIRE.loader) ?? "",
|
|
756
577
|
},
|
|
757
578
|
)
|
|
758
579
|
: undefined,
|
|
@@ -767,130 +588,60 @@ export function createRSCHandler<
|
|
|
767
588
|
// Header and cookie writes from here on (a streaming loader or
|
|
768
589
|
// render) no longer reach the response.
|
|
769
590
|
requestContext._responseSent = true;
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
)
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
}
|
|
824
|
-
|
|
825
|
-
const fullTiming = timingParts.join(", ");
|
|
826
|
-
if (fullTiming) trySetHeader(response, "Server-Timing", fullTiming);
|
|
827
|
-
if (rewrittenTo !== undefined) {
|
|
828
|
-
trySetHeader(response, REWRITE_HEADER, rewrittenTo);
|
|
829
|
-
}
|
|
830
|
-
|
|
831
|
-
// Single open-redirect chokepoint: every response (PE, full-page,
|
|
832
|
-
// middleware short-circuit, response-route) funnels through here, so
|
|
833
|
-
// guarding browser-followed (3xx) redirects once covers them all and any
|
|
834
|
-
// future redirect exit. Soft SPA/Flight redirects are 200/204 and pass
|
|
835
|
-
// through untouched (validated client-side instead).
|
|
836
|
-
const guarded = guardOutgoingRedirect(
|
|
837
|
-
response,
|
|
838
|
-
url.origin,
|
|
839
|
-
router.basename,
|
|
840
|
-
);
|
|
841
|
-
|
|
842
|
-
// Stream-idle watchdog (opt-in via timeouts.streamIdleMs): bounds
|
|
843
|
-
// end-to-end idle flow on the streamed body — see rsc/stream-idle.ts
|
|
844
|
-
// for the semantics. Applied at this finalization chokepoint so every
|
|
845
|
-
// streaming exit is covered; websocket upgrades must never be
|
|
846
|
-
// reconstructed and bodiless responses have nothing to bound. The
|
|
847
|
-
// trip fires POST-handoff (the request ALS may be gone), so it
|
|
848
|
-
// reports via the eagerly captured surfaces — callOnError +
|
|
849
|
-
// router.telemetry directly — mirroring handleStore.onError.
|
|
850
|
-
// onTimeout does NOT apply: the response already left the handler,
|
|
851
|
-
// so no replacement Response can be served mid-stream.
|
|
852
|
-
let finalResponse = guarded;
|
|
853
|
-
const streamIdleMs = router.timeouts.streamIdleMs;
|
|
854
|
-
// Websocket check FIRST: a workerd upgrade response must never have
|
|
855
|
-
// its body getter poked (same invariant as the body_kind attribute
|
|
856
|
-
// below).
|
|
857
|
-
if (
|
|
858
|
-
isTimeoutEnabled(streamIdleMs) &&
|
|
859
|
-
!isWebSocketUpgradeResponse(guarded) &&
|
|
860
|
-
guarded.body
|
|
861
|
-
) {
|
|
862
|
-
const routeKey = requestContext._routeName;
|
|
863
|
-
finalResponse = applyStreamIdleTimeout(
|
|
864
|
-
guarded,
|
|
865
|
-
streamIdleMs!,
|
|
866
|
-
(tripInfo) => {
|
|
867
|
-
callOnError(tripInfo.error, "handler", {
|
|
868
|
-
request,
|
|
869
|
-
url,
|
|
870
|
-
env,
|
|
871
|
-
routeKey,
|
|
872
|
-
handledByBoundary: false,
|
|
873
|
-
metadata: {
|
|
874
|
-
timeout: true,
|
|
875
|
-
phase: "stream-idle",
|
|
876
|
-
durationMs: tripInfo.totalMs,
|
|
877
|
-
},
|
|
878
|
-
});
|
|
879
|
-
if (router.telemetry) {
|
|
880
|
-
safeEmit(resolveSink(router.telemetry), {
|
|
881
|
-
type: "request.timeout",
|
|
882
|
-
timestamp: performance.now(),
|
|
883
|
-
requestId: getRequestId(request),
|
|
884
|
-
phase: "stream-idle",
|
|
885
|
-
pathname: url.pathname,
|
|
886
|
-
routeKey,
|
|
887
|
-
durationMs: tripInfo.totalMs,
|
|
888
|
-
customHandler: false,
|
|
889
|
-
});
|
|
890
|
-
}
|
|
891
|
-
},
|
|
892
|
-
);
|
|
893
|
-
}
|
|
591
|
+
const finalResponse = finalizeRequestResponse(downstream, {
|
|
592
|
+
ctx: handlerCtx,
|
|
593
|
+
reqCtx: requestContext,
|
|
594
|
+
request,
|
|
595
|
+
url,
|
|
596
|
+
env,
|
|
597
|
+
hasMiddleware,
|
|
598
|
+
rewrittenTo,
|
|
599
|
+
beforeGuard: (response) => {
|
|
600
|
+
// Finalize metrics after all middleware (including post-next
|
|
601
|
+
// work) has completed so :post spans are captured in the
|
|
602
|
+
// timeline. Handler timing parts are always emitted (even
|
|
603
|
+
// without debug metrics) so non-debug requests still get
|
|
604
|
+
// bootstrap Server-Timing entries.
|
|
605
|
+
// Preserve any existing Server-Timing set by response routes or
|
|
606
|
+
// middleware
|
|
607
|
+
const existingTiming = response.headers.get("Server-Timing");
|
|
608
|
+
const timingParts = existingTiming
|
|
609
|
+
? [existingTiming, ...handlerTiming]
|
|
610
|
+
: [...handlerTiming];
|
|
611
|
+
|
|
612
|
+
const metricsStore = requestContext._metricsStore;
|
|
613
|
+
if (metricsStore) {
|
|
614
|
+
// When the store was created at handler start
|
|
615
|
+
// (earlyMetricsStore), handler:total covers the full request.
|
|
616
|
+
// When ctx.debugPerformance() created the store mid-request
|
|
617
|
+
// its requestStart is now the threaded _handlerStart
|
|
618
|
+
// (== handlerStart), so both branches yield the true request
|
|
619
|
+
// entry; reading the store's own anchor keeps this correct
|
|
620
|
+
// even if a store ever lands without the threading (falls
|
|
621
|
+
// back to its start).
|
|
622
|
+
const totalStart = earlyMetricsStore
|
|
623
|
+
? handlerStart
|
|
624
|
+
: metricsStore.requestStart;
|
|
625
|
+
appendMetric(
|
|
626
|
+
metricsStore,
|
|
627
|
+
"handler:total",
|
|
628
|
+
totalStart,
|
|
629
|
+
performance.now() - totalStart,
|
|
630
|
+
);
|
|
631
|
+
const metricsTiming = buildMetricsTiming(
|
|
632
|
+
request.method,
|
|
633
|
+
url.pathname,
|
|
634
|
+
metricsStore,
|
|
635
|
+
);
|
|
636
|
+
if (metricsTiming) timingParts.push(metricsTiming);
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
const fullTiming = timingParts.join(", ");
|
|
640
|
+
if (fullTiming) {
|
|
641
|
+
trySetHeader(response, "Server-Timing", fullTiming);
|
|
642
|
+
}
|
|
643
|
+
},
|
|
644
|
+
});
|
|
894
645
|
|
|
895
646
|
// Attributes describe the response actually handed to the host (after
|
|
896
647
|
// unsafe-redirect replacement), low-cardinality only. body_kind checks
|
|
@@ -920,8 +671,9 @@ export function createRSCHandler<
|
|
|
920
671
|
};
|
|
921
672
|
|
|
922
673
|
// Core request handling logic (separated for middleware wrapping).
|
|
923
|
-
//
|
|
924
|
-
//
|
|
674
|
+
// The request stages (rsc/request-stages.ts) classify the request into a
|
|
675
|
+
// RequestPlan and answer its terminal plans; execution dispatches on the
|
|
676
|
+
// plan mode.
|
|
925
677
|
async function coreRequestHandler(
|
|
926
678
|
request: Request,
|
|
927
679
|
env: TEnv,
|
|
@@ -929,44 +681,12 @@ export function createRSCHandler<
|
|
|
929
681
|
variables: Record<string, any>,
|
|
930
682
|
nonce: string | undefined,
|
|
931
683
|
): Promise<Response> {
|
|
932
|
-
const
|
|
684
|
+
const reqCtx = getRequestContext();
|
|
685
|
+
const handlerTiming: string[] = reqCtx._handlerTiming || [];
|
|
933
686
|
|
|
934
687
|
// ---- 1. Classify ----
|
|
935
|
-
// classifyRequest may throw RouteNotFoundError for unknown routes.
|
|
936
|
-
// In that case, fall through to a full-render plan so the pipeline
|
|
937
|
-
// can render the 404 page via the existing error handling path.
|
|
938
688
|
const classifyStart = performance.now();
|
|
939
|
-
|
|
940
|
-
try {
|
|
941
|
-
plan = await classifyRequest<TEnv>(request, url, {
|
|
942
|
-
findMatch: router.findMatch,
|
|
943
|
-
routerVersion: version,
|
|
944
|
-
routerId: router.id,
|
|
945
|
-
});
|
|
946
|
-
} catch (error) {
|
|
947
|
-
if (isRouteNotFoundError(error)) {
|
|
948
|
-
// Let the render path handle 404 — match()/matchPartial() will
|
|
949
|
-
// re-throw RouteNotFoundError and the catch block in
|
|
950
|
-
// executeRenderWithMiddleware renders the not-found page.
|
|
951
|
-
plan = {
|
|
952
|
-
mode: "full-render",
|
|
953
|
-
route: {
|
|
954
|
-
matched: null as any,
|
|
955
|
-
manifestEntry: null as any,
|
|
956
|
-
entries: [],
|
|
957
|
-
routeKey: "",
|
|
958
|
-
localRouteName: "",
|
|
959
|
-
params: {},
|
|
960
|
-
routeMiddleware: [],
|
|
961
|
-
cacheScope: null,
|
|
962
|
-
isPassthrough: false,
|
|
963
|
-
},
|
|
964
|
-
negotiated: false,
|
|
965
|
-
};
|
|
966
|
-
} else {
|
|
967
|
-
throw error;
|
|
968
|
-
}
|
|
969
|
-
}
|
|
689
|
+
const plan = await classify(request, url, handlerCtx);
|
|
970
690
|
const classifyDur = performance.now() - classifyStart;
|
|
971
691
|
handlerTiming.push(`handler-classify;dur=${classifyDur.toFixed(2)}`);
|
|
972
692
|
|
|
@@ -974,64 +694,16 @@ export function createRSCHandler<
|
|
|
974
694
|
// — the outer handler tail cannot see the plan. Stays unset when middleware
|
|
975
695
|
// short-circuits before core execution runs (reported as
|
|
976
696
|
// "middleware-short-circuit").
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
// A rewritten pass of an action must reach the branch that runs it: the
|
|
980
|
-
// one that ran it re-renders from its outcome, and a JS action a global
|
|
981
|
-
// rewrite moved before it ran runs only on the action branch. Any other
|
|
982
|
-
// plan would serve the POST as a fresh request or drop the action; a
|
|
983
|
-
// reload (version mismatch, app switch) does neither.
|
|
984
|
-
const passCtx = getRequestContext();
|
|
985
|
-
const actionRan = passCtx._actionRan;
|
|
986
|
-
const actionMode = actionRan
|
|
987
|
-
? actionRan.kind === "action"
|
|
988
|
-
? "action"
|
|
989
|
-
: "pe-render"
|
|
990
|
-
: url.searchParams.has("_rsc_action") && rewriteTrail(passCtx)
|
|
991
|
-
? "action"
|
|
992
|
-
: undefined;
|
|
993
|
-
if (
|
|
994
|
-
actionMode &&
|
|
995
|
-
plan.mode !== actionMode &&
|
|
996
|
-
plan.mode !== "version-mismatch" &&
|
|
997
|
-
plan.mode !== "app-switch"
|
|
998
|
-
) {
|
|
999
|
-
throw new Error(
|
|
1000
|
-
`A rewrite of a server action or form post must target a page ` +
|
|
1001
|
-
`route; ${url.pathname} ${plan.mode === "response" ? "is a response route" : plan.mode === "redirect" ? "redirects" : "is not one"}.`,
|
|
1002
|
-
);
|
|
1003
|
-
}
|
|
697
|
+
reqCtx._requestMode = plan.mode;
|
|
1004
698
|
|
|
1005
699
|
// ---- 2. Terminal plans (no execution needed) ----
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
// but for partial requests we short-circuit with a Flight redirect.
|
|
1009
|
-
if (url.searchParams.has("_rsc_partial")) {
|
|
1010
|
-
return createRedirectFlightResponse(plan.redirectUrl);
|
|
1011
|
-
}
|
|
1012
|
-
// Full requests: let the pipeline handle the redirect via match()
|
|
1013
|
-
// which returns { redirect: url }. Fall through to full-render.
|
|
1014
|
-
}
|
|
1015
|
-
|
|
1016
|
-
// A reload points at the visitor's URL, never a rewrite target's.
|
|
1017
|
-
if (plan.mode === "version-mismatch") {
|
|
1018
|
-
console.log(
|
|
1019
|
-
`[RSC] Version mismatch: client=${url.searchParams.get("_rsc_v")}, server=${version}. Forcing reload.`,
|
|
1020
|
-
);
|
|
1021
|
-
return createReloadResponse(getRequestContext(), plan.reloadUrl);
|
|
1022
|
-
}
|
|
1023
|
-
|
|
1024
|
-
if (plan.mode === "app-switch") {
|
|
1025
|
-
// Cross-app SPA navigation crossed a host-router app boundary. Force a
|
|
1026
|
-
// real document navigation so the target app's document is re-established
|
|
1027
|
-
// (stylesheets, theme, warmup, prefetch-TTL). See request-classification.
|
|
1028
|
-
return createReloadResponse(getRequestContext(), plan.reloadUrl);
|
|
1029
|
-
}
|
|
700
|
+
const terminal = terminalPlanResponse(plan, url, handlerCtx);
|
|
701
|
+
if (terminal) return terminal;
|
|
1030
702
|
|
|
1031
703
|
// ---- 3. Origin guard (gate for action/loader/PE modes) ----
|
|
1032
704
|
const originPhase = ORIGIN_CHECK_PHASE_BY_MODE[plan.mode];
|
|
1033
705
|
if (originPhase) {
|
|
1034
|
-
const
|
|
706
|
+
const rejection = await checkRequestOrigin(
|
|
1035
707
|
request,
|
|
1036
708
|
url,
|
|
1037
709
|
router.originCheck,
|
|
@@ -1039,36 +711,15 @@ export function createRSCHandler<
|
|
|
1039
711
|
router.id,
|
|
1040
712
|
originPhase,
|
|
1041
713
|
);
|
|
1042
|
-
if (
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
`Origin check rejected: ${origin ?? "none"} vs ${host ?? "none"}`,
|
|
1047
|
-
);
|
|
1048
|
-
originError.name = "OriginCheckError";
|
|
1049
|
-
|
|
1050
|
-
callOnError(originError, "origin", {
|
|
714
|
+
if (rejection) {
|
|
715
|
+
return originRejectedResponse(
|
|
716
|
+
rejection,
|
|
717
|
+
originPhase,
|
|
1051
718
|
request,
|
|
1052
|
-
url,
|
|
1053
719
|
env,
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
if (router.telemetry) {
|
|
1059
|
-
safeEmit(resolveSink(router.telemetry), {
|
|
1060
|
-
type: "request.origin-rejected",
|
|
1061
|
-
timestamp: performance.now(),
|
|
1062
|
-
requestId: getRequestId(request),
|
|
1063
|
-
method: request.method,
|
|
1064
|
-
pathname: url.pathname,
|
|
1065
|
-
phase: originPhase,
|
|
1066
|
-
origin,
|
|
1067
|
-
host,
|
|
1068
|
-
});
|
|
1069
|
-
}
|
|
1070
|
-
|
|
1071
|
-
return originResult;
|
|
720
|
+
url,
|
|
721
|
+
handlerCtx,
|
|
722
|
+
);
|
|
1072
723
|
}
|
|
1073
724
|
}
|
|
1074
725
|
|
|
@@ -1108,7 +759,9 @@ export function createRSCHandler<
|
|
|
1108
759
|
params: reqCtx.params as Record<string, string>,
|
|
1109
760
|
handledByBoundary: true,
|
|
1110
761
|
});
|
|
1111
|
-
|
|
762
|
+
// A PPR shell capture's store inherits this onError
|
|
763
|
+
// (deriveShellCaptureContext); the capture sends nothing to the sink.
|
|
764
|
+
if (router.telemetry && !reqCtx._shellCaptureState?.run) {
|
|
1112
765
|
safeEmit(resolveSink(router.telemetry), {
|
|
1113
766
|
type: "handler.error",
|
|
1114
767
|
timestamp: performance.now(),
|
|
@@ -1122,57 +775,21 @@ export function createRSCHandler<
|
|
|
1122
775
|
}
|
|
1123
776
|
};
|
|
1124
777
|
|
|
1125
|
-
//
|
|
1126
|
-
|
|
1127
|
-
// instead of calling resolveRoute again.
|
|
1128
|
-
if (plan.mode !== "redirect") {
|
|
1129
|
-
setRequestContextParams(plan.route.params, plan.route.routeKey);
|
|
1130
|
-
getRequestContext()._classifiedRoute = plan.route;
|
|
1131
|
-
}
|
|
778
|
+
// Before any execution path, so each can read ctx.params.
|
|
779
|
+
recordPlanRoute(plan);
|
|
1132
780
|
|
|
1133
781
|
const routeReverse = createReverseFunction(getRequiredRouteMap());
|
|
1134
782
|
|
|
1135
783
|
// ---- Response route: skip entire RSC pipeline ----
|
|
1136
784
|
if (plan.mode === "response") {
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
routeMiddleware: plan.routeMiddleware,
|
|
1146
|
-
};
|
|
1147
|
-
const responseOutcome = await withTimeout(
|
|
1148
|
-
handleResponseRoute(
|
|
1149
|
-
handlerCtx,
|
|
1150
|
-
responseMatch,
|
|
1151
|
-
request,
|
|
1152
|
-
env,
|
|
1153
|
-
url,
|
|
1154
|
-
variables,
|
|
1155
|
-
),
|
|
1156
|
-
router.timeouts.renderStartMs,
|
|
1157
|
-
"render-start",
|
|
1158
|
-
);
|
|
1159
|
-
if (responseOutcome.timedOut) {
|
|
1160
|
-
return handleTimeoutResponse(
|
|
1161
|
-
request,
|
|
1162
|
-
env,
|
|
1163
|
-
url,
|
|
1164
|
-
"render-start",
|
|
1165
|
-
responseOutcome.durationMs,
|
|
1166
|
-
plan.route.routeKey,
|
|
1167
|
-
);
|
|
1168
|
-
}
|
|
1169
|
-
const response = responseOutcome.result;
|
|
1170
|
-
if (plan.negotiated && !isWebSocketUpgradeResponse(response)) {
|
|
1171
|
-
// handleResponseRoute (callHandlerWithVary) already appends Vary: Accept
|
|
1172
|
-
// for negotiated responses; dedup so we don't emit Vary: Accept, Accept.
|
|
1173
|
-
appendVaryAccept(response);
|
|
1174
|
-
}
|
|
1175
|
-
return response;
|
|
785
|
+
return serveResponseRoute(handlerCtx, {
|
|
786
|
+
match: responseRouteMatch(plan),
|
|
787
|
+
routeKey: plan.route.routeKey,
|
|
788
|
+
request,
|
|
789
|
+
url,
|
|
790
|
+
env,
|
|
791
|
+
variables,
|
|
792
|
+
});
|
|
1176
793
|
}
|
|
1177
794
|
|
|
1178
795
|
// SSR setup: kick off in parallel for modes that need HTML rendering.
|
|
@@ -1285,7 +902,8 @@ export function createRSCHandler<
|
|
|
1285
902
|
"action",
|
|
1286
903
|
);
|
|
1287
904
|
if (actionOutcome.timedOut) {
|
|
1288
|
-
return
|
|
905
|
+
return timeoutResponse(
|
|
906
|
+
handlerCtx,
|
|
1289
907
|
request,
|
|
1290
908
|
env,
|
|
1291
909
|
url,
|
|
@@ -1325,7 +943,7 @@ export function createRSCHandler<
|
|
|
1325
943
|
// Actions from client-side navigation include _rsc_partial — preserve
|
|
1326
944
|
// the partial flag so the revalidation returns a Flight stream, not HTML.
|
|
1327
945
|
// App-switch is already excluded by classifyRequest (would be full-render).
|
|
1328
|
-
const isPartialAction = url.searchParams.has(
|
|
946
|
+
const isPartialAction = url.searchParams.has(WIRE.partial);
|
|
1329
947
|
return executeRenderWithMiddleware(
|
|
1330
948
|
plan.route.routeMiddleware,
|
|
1331
949
|
plan.negotiated,
|
|
@@ -1615,7 +1233,8 @@ export function createRSCHandler<
|
|
|
1615
1233
|
"render-start",
|
|
1616
1234
|
);
|
|
1617
1235
|
if (renderOutcome.timedOut) {
|
|
1618
|
-
return
|
|
1236
|
+
return timeoutResponse(
|
|
1237
|
+
handlerCtx,
|
|
1619
1238
|
request,
|
|
1620
1239
|
env,
|
|
1621
1240
|
url,
|