vinext 1.0.1 → 1.1.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 +72 -1
- package/dist/build/app-page-static-eligibility.d.ts +15 -0
- package/dist/build/app-page-static-eligibility.js +82 -0
- package/dist/build/prerender-paths.js +3 -78
- package/dist/build/prerender.js +33 -8
- package/dist/build/server-manifest.d.ts +7 -0
- package/dist/build/server-manifest.js +11 -1
- package/dist/cache/cache-adapters-virtual.d.ts +7 -1
- package/dist/cache/cache-adapters-virtual.js +3 -3
- package/dist/client/navigation-runtime.d.ts +11 -0
- package/dist/client/navigation-runtime.js +1 -1
- package/dist/config/config-matchers.js +3 -1
- package/dist/config/next-config.d.ts +7 -0
- package/dist/config/next-config.js +18 -3
- package/dist/entries/app-rsc-entry.d.ts +2 -0
- package/dist/entries/app-rsc-entry.js +64 -43
- package/dist/index.d.ts +4 -1
- package/dist/index.js +59 -30
- package/dist/plugins/cache-flight-codec.d.ts +19 -0
- package/dist/plugins/cache-flight-codec.js +68 -0
- package/dist/plugins/commonjs-esm-facade.d.ts +35 -0
- package/dist/plugins/commonjs-esm-facade.js +80 -0
- package/dist/plugins/fonts.d.ts +8 -11
- package/dist/plugins/fonts.js +71 -34
- package/dist/routing/file-matcher.d.ts +1 -0
- package/dist/routing/file-matcher.js +1 -1
- package/dist/server/api-handler.js +17 -7
- package/dist/server/app-browser-entry.js +11 -7
- package/dist/server/app-browser-history-controller.d.ts +2 -0
- package/dist/server/app-browser-history-controller.js +8 -0
- package/dist/server/app-browser-interception-context.js +26 -2
- package/dist/server/app-browser-navigation-controller.d.ts +7 -1
- package/dist/server/app-browser-navigation-controller.js +93 -10
- package/dist/server/app-fallback-renderer.d.ts +2 -0
- package/dist/server/app-fallback-renderer.js +1 -0
- package/dist/server/app-history-state.d.ts +3 -0
- package/dist/server/app-history-state.js +7 -0
- package/dist/server/app-middleware.js +2 -1
- package/dist/server/app-optimistic-routing.js +1 -1
- package/dist/server/app-page-boundary-render.d.ts +5 -0
- package/dist/server/app-page-boundary-render.js +6 -4
- package/dist/server/app-page-cache-finalizer.d.ts +11 -0
- package/dist/server/app-page-cache-finalizer.js +41 -7
- package/dist/server/app-page-cache-render.d.ts +18 -0
- package/dist/server/app-page-cache-render.js +63 -43
- package/dist/server/app-page-cache.d.ts +5 -0
- package/dist/server/app-page-cache.js +27 -7
- package/dist/server/app-page-dispatch.d.ts +9 -1
- package/dist/server/app-page-dispatch.js +47 -30
- package/dist/server/app-page-element-builder.d.ts +7 -0
- package/dist/server/app-page-element-builder.js +27 -42
- package/dist/server/app-page-execution.d.ts +15 -12
- package/dist/server/app-page-execution.js +30 -40
- package/dist/server/app-page-head.d.ts +5 -0
- package/dist/server/app-page-head.js +9 -3
- package/dist/server/app-page-probe.d.ts +0 -109
- package/dist/server/app-page-probe.js +3 -136
- package/dist/server/app-page-render-identity.d.ts +0 -1
- package/dist/server/app-page-render-identity.js +1 -1
- package/dist/server/app-page-render-observation.js +2 -2
- package/dist/server/app-page-render.d.ts +13 -5
- package/dist/server/app-page-render.js +219 -119
- package/dist/server/app-page-request.d.ts +0 -1
- package/dist/server/app-page-request.js +1 -2
- package/dist/server/app-page-response.d.ts +4 -3
- package/dist/server/app-page-response.js +4 -3
- package/dist/server/app-page-route-wiring.d.ts +17 -5
- package/dist/server/app-page-route-wiring.js +55 -33
- package/dist/server/app-page-rsc-render-status.d.ts +48 -0
- package/dist/server/app-page-rsc-render-status.js +54 -0
- package/dist/server/app-page-stream.d.ts +15 -8
- package/dist/server/app-page-stream.js +6 -0
- package/dist/server/app-request-stage-dispatch.js +1 -1
- package/dist/server/app-request-stage-independent-entry.js +4 -4
- package/dist/server/app-response-header-provenance.d.ts +2 -0
- package/dist/server/app-response-header-provenance.js +10 -4
- package/dist/server/app-route-handler-dispatch.js +1 -1
- package/dist/server/app-router-entry.js +3 -3
- package/dist/server/app-rsc-handler.d.ts +4 -0
- package/dist/server/app-rsc-handler.js +212 -96
- package/dist/server/app-rsc-request-normalization.d.ts +3 -1
- package/dist/server/app-rsc-request-normalization.js +4 -2
- package/dist/server/app-rsc-response-stage.d.ts +1 -1
- package/dist/server/app-rsc-response-stage.js +6 -4
- package/dist/server/app-rsc-route-matching.d.ts +9 -1
- package/dist/server/app-rsc-route-matching.js +36 -19
- package/dist/server/app-server-action-execution.js +1 -1
- package/dist/server/app-ssr-entry.d.ts +3 -2
- package/dist/server/app-ssr-entry.js +6 -4
- package/dist/server/app-ssr-stream.d.ts +6 -1
- package/dist/server/app-ssr-stream.js +34 -15
- package/dist/server/app-worker-stages.d.ts +2 -2
- package/dist/server/app-worker-stages.js +3 -3
- package/dist/server/cacheability-request.js +9 -5
- package/dist/server/config-headers.js +2 -0
- package/dist/server/dev-server.js +12 -2
- package/dist/server/headers.d.ts +14 -0
- package/dist/server/headers.js +15 -1
- package/dist/server/image-optimization.d.ts +16 -0
- package/dist/server/image-optimization.js +26 -6
- package/dist/server/isr-cache.js +2 -2
- package/dist/server/middleware-matcher-pattern.js +8 -4
- package/dist/server/middleware-response-headers.js +2 -0
- package/dist/server/multi-stage.d.ts +8 -0
- package/dist/server/navigation-planner.js +1 -1
- package/dist/server/node-response-signal.d.ts +16 -0
- package/dist/server/node-response-signal.js +26 -0
- package/dist/server/open-redirect.d.ts +47 -0
- package/dist/server/open-redirect.js +74 -1
- package/dist/server/pages-page-handler.js +21 -4
- package/dist/server/pages-request-stage-entry.js +8 -4
- package/dist/server/prod-server.d.ts +8 -7
- package/dist/server/prod-server.js +153 -69
- package/dist/server/request-pipeline.d.ts +20 -8
- package/dist/server/request-pipeline.js +21 -7
- package/dist/server/response-compression.d.ts +43 -0
- package/dist/server/response-compression.js +111 -0
- package/dist/server/seed-cache.js +10 -7
- package/dist/server/socket-error-backstop.d.ts +14 -0
- package/dist/server/socket-error-backstop.js +27 -4
- package/dist/shims/cache-adapter-instantiate.d.ts +50 -0
- package/dist/shims/cache-adapter-instantiate.js +118 -0
- package/dist/shims/cache-callable-runtime.d.ts +2 -40
- package/dist/shims/cache-callable-runtime.js +50 -168
- package/dist/shims/cache-flight-arguments.d.ts +22 -0
- package/dist/shims/cache-flight-arguments.js +56 -0
- package/dist/shims/cache-request-state.d.ts +15 -0
- package/dist/shims/cache-request-state.js +31 -8
- package/dist/shims/cache-runtime.d.ts +6 -19
- package/dist/shims/cache-runtime.js +165 -225
- package/dist/shims/cache.d.ts +2 -2
- package/dist/shims/cache.js +5 -4
- package/dist/shims/cacheability-classification.d.ts +7 -0
- package/dist/shims/error-boundary-navigation.d.ts +7 -0
- package/dist/shims/error-boundary-navigation.js +19 -1
- package/dist/shims/error-boundary.d.ts +10 -10
- package/dist/shims/error-boundary.js +26 -7
- package/dist/shims/fetch-cache.d.ts +6 -0
- package/dist/shims/fetch-cache.js +19 -1
- package/dist/shims/form.js +1 -1
- package/dist/shims/headers.d.ts +13 -0
- package/dist/shims/headers.js +47 -4
- package/dist/shims/internal/app-page-props-cache-key.d.ts +5 -2
- package/dist/shims/internal/app-page-props-cache-key.js +24 -15
- package/dist/shims/internal/headers-state.d.ts +0 -1
- package/dist/shims/metadata.js +3 -3
- package/dist/shims/navigation-errors.d.ts +6 -0
- package/dist/shims/navigation-errors.js +11 -1
- package/dist/shims/navigation.d.ts +7 -0
- package/dist/shims/navigation.js +14 -4
- package/dist/shims/request-context.d.ts +9 -0
- package/dist/shims/thenable-params.d.ts +4 -0
- package/dist/shims/thenable-params.js +25 -3
- package/dist/shims/unified-request-context.js +1 -0
- package/dist/utils/mdx-scan.d.ts +5 -0
- package/dist/utils/mdx-scan.js +8 -1
- package/dist/utils/regex-safety.d.ts +10 -0
- package/dist/utils/regex-safety.js +70 -18
- package/package.json +3 -3
|
@@ -10,10 +10,10 @@ import { addBasePathToPathname, hasBasePath, stripBasePath } from "../utils/base
|
|
|
10
10
|
import { patternToNextFormat } from "../routing/route-validation.js";
|
|
11
11
|
import { getRequestExecutionContext } from "../shims/request-context.js";
|
|
12
12
|
import "./app-rsc-vary.js";
|
|
13
|
-
import { ACTION_REVALIDATED_HEADER, FLIGHT_HEADERS, VINEXT_PARAMS_HEADER, VINEXT_PRERENDER_REWRITTEN_HEADER, VINEXT_RENDERED_PATH_AND_SEARCH_HEADER } from "./headers.js";
|
|
13
|
+
import { ACTION_REVALIDATED_HEADER, FLIGHT_HEADERS, VINEXT_PARAMS_HEADER, VINEXT_PRERENDER_REWRITTEN_HEADER, VINEXT_RENDERED_PATH_AND_SEARCH_HEADER, VINEXT_SPECIAL_ERROR_STATUS_HEADER } from "./headers.js";
|
|
14
14
|
import { isInterceptionMatchedUrlPath, normalizePath } from "./normalize-path.js";
|
|
15
15
|
import { badRequestResponse, notFoundResponse, notFoundStaticAssetResponse } from "./http-error-responses.js";
|
|
16
|
-
import { cloneRequestWithHeaders, cloneRequestWithUrl, filterInternalHeaders, normalizeTrailingSlash, resolvePublicFileRoute } from "./request-pipeline.js";
|
|
16
|
+
import { cloneRequestWithHeaders, cloneRequestWithUrl, filterInternalHeaders, normalizeTrailingSlash, normalizeTrailingSlashPathname, resolvePublicFileRoute } from "./request-pipeline.js";
|
|
17
17
|
import { normalizeDefaultLocalePathname } from "./pages-i18n.js";
|
|
18
18
|
import { getCdnCacheAdapter } from "../shims/cdn-cache.js";
|
|
19
19
|
import { markRouteCacheabilityDynamic, preserveRouteCacheabilityResponsePolicy } from "../shims/cacheability-classification.js";
|
|
@@ -37,6 +37,8 @@ import { setFrameworkRequestRoute, traceFrameworkRequest } from "./request-traci
|
|
|
37
37
|
import { matchPrerenderRouteParamsPayload, readTrustedPrerenderRouteParams, serializePrerenderRouteParamsHeader } from "./prerender-route-params.js";
|
|
38
38
|
import { buildPageCacheTags } from "./implicit-tags.js";
|
|
39
39
|
import { createServerActionNotFoundResponse, getServerActionNotFoundMessage } from "./server-action-not-found.js";
|
|
40
|
+
import { isPossibleAppRouteActionRequest } from "./app-action-request.js";
|
|
41
|
+
import { normalizeInterceptionContextHeader } from "./app-interception-context-header.js";
|
|
40
42
|
import { buildPostMwRequestContext } from "./app-post-middleware-context.js";
|
|
41
43
|
import { pickRootParams, setRootParams } from "../shims/root-params.js";
|
|
42
44
|
import { createRouteTreePrefetchResponse, isRouteTreePrefetchRequest } from "./app-route-tree-prefetch.js";
|
|
@@ -51,6 +53,33 @@ import { consumePagesResponseStagePolicyOwner, withoutResponseStageVary } from "
|
|
|
51
53
|
const HAS_CONFIG_HEADERS = process.env.__VINEXT_HAS_CONFIG_HEADERS !== "false";
|
|
52
54
|
const HAS_CONFIG_REDIRECTS = process.env.__VINEXT_HAS_CONFIG_REDIRECTS !== "false";
|
|
53
55
|
const HAS_CONFIG_REWRITES = process.env.__VINEXT_HAS_CONFIG_REWRITES !== "false";
|
|
56
|
+
/**
|
|
57
|
+
* Decode an interception context to the canonical source pathname it names.
|
|
58
|
+
* Throws for any value the client could not send back as its context header.
|
|
59
|
+
*/
|
|
60
|
+
function decodeInterceptionSourcePathname(context) {
|
|
61
|
+
if (!isInterceptionMatchedUrlPath(context)) throw new Error("Invalid interception source pathname");
|
|
62
|
+
if (new URL(context, "http://n").pathname !== context) throw new Error("Interception source is not a parsed URL pathname");
|
|
63
|
+
const decodedSourcePathname = normalizePathnameForRouteMatchStrict(context);
|
|
64
|
+
if (/[\t\n\r]/.test(decodedSourcePathname)) throw new Error("Interception source contains a stripped URL character");
|
|
65
|
+
if (hasUrlParserDotSegment(decodedSourcePathname)) throw new Error("Interception source contains a URL dot segment");
|
|
66
|
+
const sourcePathname = normalizePath(decodedSourcePathname);
|
|
67
|
+
if (sourcePathname !== decodedSourcePathname) throw new Error("Non-canonical interception source pathname");
|
|
68
|
+
return sourcePathname;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Whether a server-derived context satisfies the whole inbound header
|
|
72
|
+
* contract, so the client can send it back unchanged on its next request.
|
|
73
|
+
*/
|
|
74
|
+
function isValidInterceptionSourceContext(context) {
|
|
75
|
+
if (normalizeInterceptionContextHeader(context) !== context) return false;
|
|
76
|
+
try {
|
|
77
|
+
decodeInterceptionSourcePathname(context);
|
|
78
|
+
return true;
|
|
79
|
+
} catch {
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
54
83
|
function ruleUsesUnkeyedRequestCondition(rule) {
|
|
55
84
|
return [...rule.has ?? [], ...rule.missing ?? []].some((condition) => condition.type === "header" || condition.type === "cookie" || condition.type === "host");
|
|
56
85
|
}
|
|
@@ -123,6 +152,26 @@ function applyMiddlewareContextToResponse(response, middlewareContext, appendToP
|
|
|
123
152
|
headers
|
|
124
153
|
}));
|
|
125
154
|
}
|
|
155
|
+
/**
|
|
156
|
+
* Next.js answers a Link's segment prefetch of a page whose notFound(),
|
|
157
|
+
* forbidden() or unauthorized() it stored with a 200, so its router renders
|
|
158
|
+
* the page's fallback without loading the document. A shared response stage
|
|
159
|
+
* gives that prefetch the navigation's response, so the request's own headers
|
|
160
|
+
* decide the status here.
|
|
161
|
+
*/
|
|
162
|
+
function withStoredSpecialErrorStatus(response, isSegmentPrefetch) {
|
|
163
|
+
const marker = response.headers.get(VINEXT_SPECIAL_ERROR_STATUS_HEADER);
|
|
164
|
+
if (marker === null) return response;
|
|
165
|
+
const headers = new Headers(response.headers);
|
|
166
|
+
headers.delete(VINEXT_SPECIAL_ERROR_STATUS_HEADER);
|
|
167
|
+
const storedStatus = Number(marker);
|
|
168
|
+
const status = !(storedStatus === 401 || storedStatus === 403 || storedStatus === 404) ? response.status : isSegmentPrefetch ? 200 : storedStatus;
|
|
169
|
+
return preserveFullyBufferedBodyMetadata(response, new Response(response.body, {
|
|
170
|
+
headers,
|
|
171
|
+
status,
|
|
172
|
+
statusText: status === response.status ? response.statusText : ""
|
|
173
|
+
}));
|
|
174
|
+
}
|
|
126
175
|
function hasProperty(value, key) {
|
|
127
176
|
return key in value;
|
|
128
177
|
}
|
|
@@ -206,7 +255,8 @@ function requestWithoutRscSuffix(request) {
|
|
|
206
255
|
url.pathname = pathname;
|
|
207
256
|
return cloneRequestWithUrl(request, url.toString());
|
|
208
257
|
}
|
|
209
|
-
|
|
258
|
+
/** Send a response with the never-cache policy, rebuilding immutable headers. */
|
|
259
|
+
function withNeverCacheControl(response) {
|
|
210
260
|
const applyNoStore = (headers) => {
|
|
211
261
|
applyCdnResponseHeaders(headers, { cacheControl: NEVER_CACHE_CONTROL });
|
|
212
262
|
};
|
|
@@ -224,6 +274,13 @@ function markUnverifiedInterceptionResponseUncacheable(response) {
|
|
|
224
274
|
}
|
|
225
275
|
return markedResponse;
|
|
226
276
|
}
|
|
277
|
+
const unmatchedRouteResponses = /* @__PURE__ */ new WeakSet();
|
|
278
|
+
/** Send an unmatched route's 404 with the never-cache policy, as Next.js does. */
|
|
279
|
+
function withUnmatchedRouteCacheControl(response) {
|
|
280
|
+
const markedResponse = withNeverCacheControl(response);
|
|
281
|
+
unmatchedRouteResponses.add(markedResponse);
|
|
282
|
+
return markedResponse;
|
|
283
|
+
}
|
|
227
284
|
async function handleAppRscRequest(options, request, preMiddlewareRequestContext, middlewareContext, isDataRequest, isMiddlewareDataRequest, pagesDataRequest, dispatchInternalRequest, allowInternalRscDocumentFallback, dispatchResponseStage, responseStageProbeMode = null, setInterceptionResponseUncacheable = () => {}) {
|
|
228
285
|
const handlerStart = process.env.NODE_ENV !== "production" ? performance.now() : 0;
|
|
229
286
|
if (process.env.NODE_ENV !== "production") {
|
|
@@ -248,12 +305,7 @@ async function handleAppRscRequest(options, request, preMiddlewareRequestContext
|
|
|
248
305
|
if (hasRawInterceptionContext) setInterceptionResponseUncacheable(true);
|
|
249
306
|
let interceptionSourcePathname = null;
|
|
250
307
|
if (isRscRequest && interceptionContextHeader !== null) try {
|
|
251
|
-
|
|
252
|
-
const decodedInterceptionSourcePathname = normalizePathnameForRouteMatchStrict(interceptionContextHeader);
|
|
253
|
-
if (/[\t\n\r]/.test(decodedInterceptionSourcePathname)) throw new Error("Interception source contains a stripped URL character");
|
|
254
|
-
if (hasUrlParserDotSegment(decodedInterceptionSourcePathname)) throw new Error("Interception source contains a URL dot segment");
|
|
255
|
-
interceptionSourcePathname = normalizePath(decodedInterceptionSourcePathname);
|
|
256
|
-
if (interceptionSourcePathname !== decodedInterceptionSourcePathname) throw new Error("Non-canonical interception source pathname");
|
|
308
|
+
interceptionSourcePathname = decodeInterceptionSourcePathname(interceptionContextHeader);
|
|
257
309
|
} catch {
|
|
258
310
|
options.clearRequestContext();
|
|
259
311
|
setInterceptionResponseUncacheable(true);
|
|
@@ -282,10 +334,10 @@ async function handleAppRscRequest(options, request, preMiddlewareRequestContext
|
|
|
282
334
|
});
|
|
283
335
|
if (selectorCacheBustingRedirect) return selectorCacheBustingRedirect;
|
|
284
336
|
}
|
|
285
|
-
const provesConcreteInterceptionSource = (sourceMatch) => {
|
|
337
|
+
const provesConcreteInterceptionSource = (sourceMatch, sourceContext = interceptionContextHeader) => {
|
|
286
338
|
if (sourceMatch === null) return false;
|
|
287
339
|
if (sourceMatch.interceptionSourceIsConcrete !== void 0) return sourceMatch.interceptionSourceIsConcrete;
|
|
288
|
-
return
|
|
340
|
+
return sourceContext !== null && (options.matchRequestRoute ?? options.matchRoute)(sourceContext)?.route === sourceMatch.route;
|
|
289
341
|
};
|
|
290
342
|
if (provesConcreteInterceptionSource(hadBasePath && hasRawInterceptionContext && interceptionContextHeader !== null ? options.matchInterceptRoute?.(requestCleanPathname, interceptionContextHeader, interceptionIdHeader) ?? null : null)) setInterceptionResponseUncacheable(false);
|
|
291
343
|
if (pathname === "/__vinext/prerender/static-params" || pathname === "/__vinext/prerender/pages-static-paths" || pathname === "/__vinext/prerender/metadata-routes") {
|
|
@@ -461,7 +513,7 @@ async function handleAppRscRequest(options, request, preMiddlewareRequestContext
|
|
|
461
513
|
isRscRequest,
|
|
462
514
|
middlewareCookieOverlay,
|
|
463
515
|
mountedSlotsHeader,
|
|
464
|
-
protocolVersion:
|
|
516
|
+
protocolVersion: 11,
|
|
465
517
|
requestOrigin: url.origin,
|
|
466
518
|
renderMode,
|
|
467
519
|
resolvedUrl,
|
|
@@ -618,8 +670,9 @@ async function handleAppRscRequest(options, request, preMiddlewareRequestContext
|
|
|
618
670
|
});
|
|
619
671
|
const directPreActionMatch = filesystemRouteEligible ? matchCleanPathname() : null;
|
|
620
672
|
const preActionRoutePathname = cleanPathnameIsRequestPathname ? requestCleanPathname : cleanPathname;
|
|
621
|
-
|
|
622
|
-
|
|
673
|
+
let interceptionSourceContext = interceptionContextHeader;
|
|
674
|
+
let interceptionSourceMatch = filesystemRouteEligible && interceptionSourcePathname !== null && interceptionContextHeader !== null ? options.matchInterceptRoute?.(preActionRoutePathname, interceptionContextHeader, interceptionIdHeader) ?? null : null;
|
|
675
|
+
let hasVerifiedInterceptionSource = provesConcreteInterceptionSource(interceptionSourceMatch);
|
|
623
676
|
if (interceptionIdHeader !== null && !hasVerifiedInterceptionSource) {
|
|
624
677
|
options.clearRequestContext();
|
|
625
678
|
setInterceptionResponseUncacheable(true);
|
|
@@ -634,81 +687,142 @@ async function handleAppRscRequest(options, request, preMiddlewareRequestContext
|
|
|
634
687
|
bypassInterceptionContextCache = true;
|
|
635
688
|
setInterceptionResponseUncacheable(true);
|
|
636
689
|
};
|
|
637
|
-
if (interceptionSourceMatch !== null &&
|
|
638
|
-
const
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
const targetHeadersContext = getHeadersContext();
|
|
646
|
-
const targetRequestHeaders = targetHeadersContext ? new Headers(targetHeadersContext.headers) : null;
|
|
647
|
-
targetRequestHeaders?.delete(VINEXT_MW_CTX_HEADER);
|
|
648
|
-
for (const header of FLIGHT_HEADERS) targetRequestHeaders?.delete(header);
|
|
649
|
-
if (targetRequestHeaders) {
|
|
650
|
-
const sourceHeaderNames = Array.from(sourceMiddlewareRequest.headers.keys());
|
|
651
|
-
for (const header of sourceHeaderNames) sourceMiddlewareRequest.headers.delete(header);
|
|
652
|
-
for (const [name, value] of targetRequestHeaders) sourceMiddlewareRequest.headers.append(name, value);
|
|
653
|
-
}
|
|
654
|
-
const sourceMiddlewareContext = {
|
|
655
|
-
headers: null,
|
|
656
|
-
requestHeaders: null,
|
|
657
|
-
status: null
|
|
658
|
-
};
|
|
659
|
-
const sourceHeadersContext = headersContextFromRequest(sourceMiddlewareRequest, { draftModeSecret: options.draftModeSecret });
|
|
660
|
-
let sourceMiddlewareResult;
|
|
661
|
-
try {
|
|
662
|
-
sourceMiddlewareResult = await runWithHeadersContext(sourceHeadersContext, () => runMiddleware({
|
|
663
|
-
cleanPathname: interceptionSourcePathname,
|
|
664
|
-
context: sourceMiddlewareContext,
|
|
665
|
-
externalRewriteRequest: normalizedUserlandRequest,
|
|
666
|
-
hadBasePath,
|
|
667
|
-
isDataRequest: isMiddlewareDataRequest,
|
|
668
|
-
request: sourceMiddlewareRequest,
|
|
669
|
-
validateExternalRewriteRequest: () => validateClaimedOutsideBasePathRsc(true)
|
|
670
|
-
}));
|
|
671
|
-
} finally {
|
|
672
|
-
if (sourceMiddlewareRequest.body && !sourceMiddlewareRequest.bodyUsed && !sourceMiddlewareRequest.body.locked) sourceMiddlewareRequest.body.cancel().catch(() => {});
|
|
673
|
-
if (sourceRequest !== userlandRequest && sourceRequest.body && !sourceRequest.bodyUsed && !sourceRequest.body.locked) sourceRequest.body.cancel().catch(() => {});
|
|
674
|
-
}
|
|
675
|
-
if (!dispatchResponseStage && sourceMiddlewareResult.pathnameEligible) markRouteCacheabilityDynamic(sourceMiddlewareResult.matched ? "middleware matched this request" : "middleware is eligible for this pathname");
|
|
676
|
-
if (sourceMiddlewareResult.kind === "response") {
|
|
677
|
-
options.clearRequestContext();
|
|
678
|
-
return sourceMiddlewareResult.response;
|
|
679
|
-
}
|
|
680
|
-
let sourceHeadersCompatible = true;
|
|
681
|
-
if (targetRequestHeaders) {
|
|
682
|
-
for (const [name, value] of targetRequestHeaders) if (sourceHeadersContext.headers.get(name) !== value) {
|
|
683
|
-
sourceHeadersCompatible = false;
|
|
684
|
-
break;
|
|
690
|
+
if (interceptionSourceMatch !== null && interceptionContextHeader !== null && interceptionSourcePathname !== null && !(interceptionSourceMatch.route === directPreActionMatch?.route && haveSamePageParams(interceptionSourceMatch.params, directPreActionMatch.params))) {
|
|
691
|
+
const sourceRequestPathname = normalizeTrailingSlashPathname(interceptionContextHeader, options.trailingSlash) ?? interceptionContextHeader;
|
|
692
|
+
const decodedSourceRequestPathname = normalizeTrailingSlashPathname(interceptionSourcePathname, options.trailingSlash) ?? interceptionSourcePathname;
|
|
693
|
+
const adoptRewrittenInterceptionSource = (rewrittenPathname) => {
|
|
694
|
+
const rewrittenInterceptionSourceMatch = isValidInterceptionSourceContext(rewrittenPathname) ? options.matchInterceptRoute?.(preActionRoutePathname, rewrittenPathname, interceptionIdHeader) ?? null : null;
|
|
695
|
+
if (rewrittenInterceptionSourceMatch !== null) {
|
|
696
|
+
interceptionSourceMatch = rewrittenInterceptionSourceMatch;
|
|
697
|
+
interceptionSourceContext = rewrittenPathname;
|
|
685
698
|
}
|
|
686
|
-
|
|
687
|
-
|
|
699
|
+
const rewrittenSourceMatch = options.matchRoute(rewrittenPathname);
|
|
700
|
+
return interceptionSourceMatch !== null && rewrittenSourceMatch?.route === interceptionSourceMatch.route && haveSamePageParams(rewrittenSourceMatch.params, interceptionSourceMatch.params);
|
|
701
|
+
};
|
|
702
|
+
if (configMatchers?.matchRedirect(matchPathname(sourceRequestPathname), options.configRedirects, preMiddlewareRequestContext, basePathState, dispatchResponseStage ? void 0 : markConditionalRedirectCacheability)) {
|
|
688
703
|
options.clearRequestContext();
|
|
689
704
|
return notFoundResponse();
|
|
690
705
|
}
|
|
691
|
-
let
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
706
|
+
let sourceRulesPathname = sourceRequestPathname;
|
|
707
|
+
if (runMiddleware) {
|
|
708
|
+
const sourceUrl = new URL(userlandRequest.url);
|
|
709
|
+
sourceUrl.search = new URL(resolvedUrl, url).search;
|
|
710
|
+
const sourceUrlPathname = hadBasePath && options.basePath ? sourceRequestPathname === "/" ? options.basePath : options.basePath + sourceRequestPathname : sourceRequestPathname;
|
|
711
|
+
sourceUrl.pathname = sourceUrlPathname;
|
|
712
|
+
if (sourceUrl.pathname !== sourceUrlPathname) {
|
|
713
|
+
options.clearRequestContext();
|
|
714
|
+
setInterceptionResponseUncacheable(true);
|
|
715
|
+
return badRequestResponse();
|
|
716
|
+
}
|
|
717
|
+
const sourceRequest = userlandRequest.body ? userlandRequest.clone() : userlandRequest;
|
|
718
|
+
const sourceMiddlewareRequest = cloneRequestWithUrl(sourceRequest, sourceUrl.href);
|
|
719
|
+
sourceMiddlewareRequest.headers.delete(VINEXT_MW_CTX_HEADER);
|
|
720
|
+
for (const header of FLIGHT_HEADERS) sourceMiddlewareRequest.headers.delete(header);
|
|
721
|
+
const targetHeadersContext = getHeadersContext();
|
|
722
|
+
const targetRequestHeaders = targetHeadersContext ? new Headers(targetHeadersContext.headers) : null;
|
|
723
|
+
targetRequestHeaders?.delete(VINEXT_MW_CTX_HEADER);
|
|
724
|
+
for (const header of FLIGHT_HEADERS) targetRequestHeaders?.delete(header);
|
|
725
|
+
if (targetRequestHeaders) {
|
|
726
|
+
const sourceHeaderNames = Array.from(sourceMiddlewareRequest.headers.keys());
|
|
727
|
+
for (const header of sourceHeaderNames) sourceMiddlewareRequest.headers.delete(header);
|
|
728
|
+
for (const [name, value] of targetRequestHeaders) sourceMiddlewareRequest.headers.append(name, value);
|
|
729
|
+
}
|
|
730
|
+
const sourceMiddlewareContext = {
|
|
731
|
+
headers: null,
|
|
732
|
+
requestHeaders: null,
|
|
733
|
+
status: null
|
|
734
|
+
};
|
|
735
|
+
const sourceHeadersContext = headersContextFromRequest(sourceMiddlewareRequest, { draftModeSecret: options.draftModeSecret });
|
|
736
|
+
let sourceMiddlewareResult;
|
|
737
|
+
try {
|
|
738
|
+
sourceMiddlewareResult = await runWithHeadersContext(sourceHeadersContext, () => runMiddleware({
|
|
739
|
+
cleanPathname: decodedSourceRequestPathname,
|
|
740
|
+
context: sourceMiddlewareContext,
|
|
741
|
+
externalRewriteRequest: normalizedUserlandRequest,
|
|
742
|
+
hadBasePath,
|
|
743
|
+
isDataRequest: isMiddlewareDataRequest,
|
|
744
|
+
request: sourceMiddlewareRequest,
|
|
745
|
+
validateExternalRewriteRequest: () => validateClaimedOutsideBasePathRsc(true)
|
|
746
|
+
}));
|
|
747
|
+
} finally {
|
|
748
|
+
if (sourceMiddlewareRequest.body && !sourceMiddlewareRequest.bodyUsed && !sourceMiddlewareRequest.body.locked) sourceMiddlewareRequest.body.cancel().catch(() => {});
|
|
749
|
+
if (sourceRequest !== userlandRequest && sourceRequest.body && !sourceRequest.bodyUsed && !sourceRequest.body.locked) sourceRequest.body.cancel().catch(() => {});
|
|
750
|
+
}
|
|
751
|
+
if (!dispatchResponseStage && sourceMiddlewareResult.pathnameEligible) markRouteCacheabilityDynamic(sourceMiddlewareResult.matched ? "middleware matched this request" : "middleware is eligible for this pathname");
|
|
752
|
+
if (sourceMiddlewareResult.kind === "response") {
|
|
753
|
+
options.clearRequestContext();
|
|
754
|
+
return sourceMiddlewareResult.response;
|
|
755
|
+
}
|
|
756
|
+
let sourceHeadersCompatible = true;
|
|
757
|
+
if (targetRequestHeaders) {
|
|
758
|
+
for (const [name, value] of targetRequestHeaders) if (sourceHeadersContext.headers.get(name) !== value) {
|
|
759
|
+
sourceHeadersCompatible = false;
|
|
760
|
+
break;
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
if (!targetHeadersContext || !targetRequestHeaders || !sourceHeadersCompatible || !haveSameRequestCookies(targetHeadersContext.cookies, sourceHeadersContext.cookies)) {
|
|
764
|
+
options.clearRequestContext();
|
|
765
|
+
return notFoundResponse();
|
|
766
|
+
}
|
|
767
|
+
let addedSourceHeader = false;
|
|
768
|
+
for (const [name, value] of sourceHeadersContext.headers) if (!targetRequestHeaders.has(name)) {
|
|
769
|
+
targetHeadersContext.headers.set(name, value);
|
|
770
|
+
addedSourceHeader = true;
|
|
771
|
+
}
|
|
772
|
+
if (addedSourceHeader) {
|
|
773
|
+
targetHeadersContext.readonlyHeaders = void 0;
|
|
774
|
+
canUseSharedWorkerResponseStage = false;
|
|
775
|
+
}
|
|
776
|
+
if (sourceMiddlewareResult.rewritten) {
|
|
777
|
+
if (sourceMiddlewareResult.search !== sourceUrl.search || !adoptRewrittenInterceptionSource(sourceMiddlewareResult.cleanPathname)) {
|
|
778
|
+
options.clearRequestContext();
|
|
779
|
+
return notFoundResponse();
|
|
780
|
+
}
|
|
781
|
+
sourceRulesPathname = sourceMiddlewareResult.cleanPathname;
|
|
782
|
+
}
|
|
699
783
|
}
|
|
700
|
-
if (
|
|
701
|
-
const
|
|
702
|
-
|
|
784
|
+
if (HAS_CONFIG_REWRITES && options.configRewrites.beforeFiles.length) {
|
|
785
|
+
const { matchRewrite } = await import("../config/config-matchers.js");
|
|
786
|
+
const sourceRewriteContext = requestContextForResolvedUrl(buildPostMwRequestContext(userlandRequest), resolvedUrl, url);
|
|
787
|
+
let didRewriteSource = false;
|
|
788
|
+
for (const rewrite of options.configRewrites.beforeFiles) {
|
|
789
|
+
const destination = matchRewrite(matchPathname(sourceRulesPathname), [rewrite], sourceRewriteContext, basePathState, matchPathname(sourceRulesPathname), dispatchResponseStage === void 0 ? markConditionalRewriteCacheability : void 0);
|
|
790
|
+
if (!destination) continue;
|
|
791
|
+
if (isExternalUrl(destination) || destination.includes("?")) {
|
|
792
|
+
options.clearRequestContext();
|
|
793
|
+
return notFoundResponse();
|
|
794
|
+
}
|
|
795
|
+
sourceRulesPathname = pathnameForResolvedUrl(destination);
|
|
796
|
+
didRewriteSource = true;
|
|
797
|
+
}
|
|
798
|
+
if (didRewriteSource && !adoptRewrittenInterceptionSource(sourceRulesPathname)) {
|
|
703
799
|
options.clearRequestContext();
|
|
704
800
|
return notFoundResponse();
|
|
705
801
|
}
|
|
706
802
|
}
|
|
707
803
|
}
|
|
804
|
+
if (interceptionSourceMatch !== null) {
|
|
805
|
+
hasVerifiedInterceptionSource = provesConcreteInterceptionSource(interceptionSourceMatch, interceptionSourceContext);
|
|
806
|
+
if (!hasVerifiedInterceptionSource) {
|
|
807
|
+
if (interceptionIdHeader !== null) {
|
|
808
|
+
options.clearRequestContext();
|
|
809
|
+
setInterceptionResponseUncacheable(true);
|
|
810
|
+
return badRequestResponse();
|
|
811
|
+
}
|
|
812
|
+
interceptionSourceMatch = null;
|
|
813
|
+
interceptionSourceContext = null;
|
|
814
|
+
}
|
|
815
|
+
bypassInterceptionContextCache = hasRawInterceptionContext && !hasVerifiedInterceptionSource;
|
|
816
|
+
setInterceptionResponseUncacheable(bypassInterceptionContextCache);
|
|
817
|
+
}
|
|
708
818
|
const interceptionPreActionMatch = filesystemRouteEligible && directPreActionMatch === null && isRscRequest && interceptionSourcePathname !== null ? interceptionSourceMatch : null;
|
|
709
819
|
const preActionMatch = directPreActionMatch ?? interceptionPreActionMatch;
|
|
710
820
|
const isInterceptionMatch = interceptionPreActionMatch !== null;
|
|
711
|
-
|
|
821
|
+
const rendersRawRequestMatch = options.matchRawRequestRoute !== void 0 && (isPossibleAppRouteActionRequest(request) || isDraftModeRequest(request, options.draftModeSecret) || isDraftModeEnabled());
|
|
822
|
+
const usesRawRequestMatch = () => rendersRawRequestMatch && cleanPathnameIsRequestPathname && !isInterceptionMatch;
|
|
823
|
+
const renderedRouteMatch = (routeMatch) => routeMatch !== null && usesRawRequestMatch() ? options.matchRawRequestRoute(requestCleanPathname) ?? routeMatch : routeMatch;
|
|
824
|
+
const renderedPreActionMatch = renderedRouteMatch(preActionMatch);
|
|
825
|
+
if (renderedPreActionMatch) setRootParams(pickRootParams(renderedPreActionMatch.params, renderedPreActionMatch.route.rootParamNames));
|
|
712
826
|
if (pagesDataRequest && didMiddlewareRewritePathname && preActionMatch && !preActionMatch.route.isDynamic) {
|
|
713
827
|
const headers = new Headers();
|
|
714
828
|
mergeMiddlewareResponseHeaders(headers, middlewareContext.headers);
|
|
@@ -729,7 +843,7 @@ async function handleAppRscRequest(options, request, preMiddlewareRequestContext
|
|
|
729
843
|
contentType,
|
|
730
844
|
middlewareContext,
|
|
731
845
|
request,
|
|
732
|
-
routeMatch:
|
|
846
|
+
routeMatch: renderedPreActionMatch
|
|
733
847
|
});
|
|
734
848
|
else if (preActionMatch?.route.__loadPage && !preActionMatch.route.__loadRouteHandler) return createMissingServerActionResponse(options, null);
|
|
735
849
|
}
|
|
@@ -761,13 +875,13 @@ async function handleAppRscRequest(options, request, preMiddlewareRequestContext
|
|
|
761
875
|
actionId,
|
|
762
876
|
cleanPathname,
|
|
763
877
|
contentType,
|
|
764
|
-
interceptionContext:
|
|
878
|
+
interceptionContext: interceptionSourceContext,
|
|
765
879
|
isRscRequest,
|
|
766
880
|
middlewareContext,
|
|
767
881
|
mountedSlotsHeader,
|
|
768
882
|
request,
|
|
769
883
|
scriptNonce,
|
|
770
|
-
routeMatch:
|
|
884
|
+
routeMatch: renderedPreActionMatch,
|
|
771
885
|
routePathname: preActionRoutePathname,
|
|
772
886
|
dispatchRedirectTargetRequest: dispatchInternalRequest,
|
|
773
887
|
sourceConfigHeaders,
|
|
@@ -803,7 +917,7 @@ async function handleAppRscRequest(options, request, preMiddlewareRequestContext
|
|
|
803
917
|
matchKind,
|
|
804
918
|
middlewareCookieOverlay,
|
|
805
919
|
preHandlerHeaders: cache === "shared" && resourceKind === "page" && dataKind === "static" ? null : [...await loadPreHandlerResponseHeaders()],
|
|
806
|
-
protocolVersion:
|
|
920
|
+
protocolVersion: 11,
|
|
807
921
|
requestOrigin: url.origin,
|
|
808
922
|
resourceKind,
|
|
809
923
|
requestUrl: request.url,
|
|
@@ -935,15 +1049,16 @@ async function handleAppRscRequest(options, request, preMiddlewareRequestContext
|
|
|
935
1049
|
}
|
|
936
1050
|
if (match) break;
|
|
937
1051
|
}
|
|
938
|
-
if (interceptionCacheProofInvalidated &&
|
|
1052
|
+
if (interceptionCacheProofInvalidated && interceptionSourceContext !== null) {
|
|
939
1053
|
const finalInterceptionTargetPathname = cleanPathnameIsRequestPathname ? requestCleanPathname : cleanPathname;
|
|
940
|
-
const finalInterceptionSourceMatch = filesystemRouteEligible && match !== null ? options.matchInterceptRoute?.(finalInterceptionTargetPathname,
|
|
1054
|
+
const finalInterceptionSourceMatch = filesystemRouteEligible && match !== null ? options.matchInterceptRoute?.(finalInterceptionTargetPathname, interceptionSourceContext, interceptionIdHeader) ?? null : null;
|
|
941
1055
|
const hasVerifiedFinalInterceptionSource = hasVerifiedInterceptionSource && interceptionSourceMatch !== null && provesConcreteInterceptionSource(finalInterceptionSourceMatch) && finalInterceptionSourceMatch?.route === interceptionSourceMatch.route && haveSamePageParams(finalInterceptionSourceMatch.params, interceptionSourceMatch.params);
|
|
942
1056
|
if (interceptionIdHeader !== null && !hasVerifiedFinalInterceptionSource) {
|
|
943
1057
|
options.clearRequestContext();
|
|
944
1058
|
setInterceptionResponseUncacheable(true);
|
|
945
1059
|
return badRequestResponse();
|
|
946
1060
|
}
|
|
1061
|
+
if (!hasVerifiedFinalInterceptionSource) interceptionSourceContext = null;
|
|
947
1062
|
bypassInterceptionContextCache = !hasVerifiedFinalInterceptionSource;
|
|
948
1063
|
setInterceptionResponseUncacheable(bypassInterceptionContextCache);
|
|
949
1064
|
}
|
|
@@ -958,7 +1073,7 @@ async function handleAppRscRequest(options, request, preMiddlewareRequestContext
|
|
|
958
1073
|
options.clearRequestContext();
|
|
959
1074
|
const headers = new Headers();
|
|
960
1075
|
mergeMiddlewareResponseHeaders(headers, middlewareContext.headers);
|
|
961
|
-
return notFoundResponse({ headers });
|
|
1076
|
+
return withUnmatchedRouteCacheControl(notFoundResponse({ headers }));
|
|
962
1077
|
}
|
|
963
1078
|
if (pagesDataRequest) {
|
|
964
1079
|
options.clearRequestContext();
|
|
@@ -982,7 +1097,7 @@ async function handleAppRscRequest(options, request, preMiddlewareRequestContext
|
|
|
982
1097
|
}
|
|
983
1098
|
setFrameworkRequestRoute("/404", isRscRequest);
|
|
984
1099
|
const notFoundResponseStage = transportedResponseStage ?? options.renderResponseStageLocally;
|
|
985
|
-
if (notFoundResponseStage) return composeResponseStageResponse(await notFoundResponseStage(responseStageRequest(), {
|
|
1100
|
+
if (notFoundResponseStage) return withUnmatchedRouteCacheControl(await composeResponseStageResponse(await notFoundResponseStage(responseStageRequest(), {
|
|
986
1101
|
kind: "app-not-found",
|
|
987
1102
|
buildId: options.buildId,
|
|
988
1103
|
cacheability: responseStageCacheability(resolvedUrl),
|
|
@@ -992,12 +1107,12 @@ async function handleAppRscRequest(options, request, preMiddlewareRequestContext
|
|
|
992
1107
|
isRscRequest,
|
|
993
1108
|
middlewareCookieOverlay,
|
|
994
1109
|
mountedSlotsHeader,
|
|
995
|
-
protocolVersion:
|
|
1110
|
+
protocolVersion: 11,
|
|
996
1111
|
requestOrigin: url.origin,
|
|
997
1112
|
renderMode,
|
|
998
1113
|
resolvedUrl,
|
|
999
1114
|
scriptNonce: scriptNonce ?? null
|
|
1000
|
-
}));
|
|
1115
|
+
})));
|
|
1001
1116
|
const renderedNotFoundResponse = await traceAppPageRender("/404", "render", () => options.renderNotFound({
|
|
1002
1117
|
isRscRequest,
|
|
1003
1118
|
middlewareContext,
|
|
@@ -1005,12 +1120,13 @@ async function handleAppRscRequest(options, request, preMiddlewareRequestContext
|
|
|
1005
1120
|
route: null,
|
|
1006
1121
|
scriptNonce
|
|
1007
1122
|
}));
|
|
1008
|
-
if (renderedNotFoundResponse) return renderedNotFoundResponse;
|
|
1123
|
+
if (renderedNotFoundResponse) return withUnmatchedRouteCacheControl(renderedNotFoundResponse);
|
|
1009
1124
|
options.clearRequestContext();
|
|
1010
1125
|
const headers = new Headers();
|
|
1011
1126
|
mergeMiddlewareResponseHeaders(headers, middlewareContext.headers);
|
|
1012
|
-
return notFoundResponse({ headers });
|
|
1127
|
+
return withUnmatchedRouteCacheControl(notFoundResponse({ headers }));
|
|
1013
1128
|
}
|
|
1129
|
+
match = renderedRouteMatch(match);
|
|
1014
1130
|
const { route, params } = match;
|
|
1015
1131
|
const requestPathnameDiffersFromCachePath = requestCleanPathname !== cleanPathname;
|
|
1016
1132
|
const rawRequestPathnameIsObservable = isInterceptionMatch || route.routeHandler != null || typeof route.__loadRouteHandler === "function";
|
|
@@ -1038,7 +1154,7 @@ async function handleAppRscRequest(options, request, preMiddlewareRequestContext
|
|
|
1038
1154
|
const prerenderRouteParams = prerenderRouteParamsMatch?.params ?? null;
|
|
1039
1155
|
const isPrerenderFallbackShell = prerenderRouteParamsMatch?.kind === "fallback-shell";
|
|
1040
1156
|
const renderParams = prerenderRouteParams ?? params;
|
|
1041
|
-
const responseStageMatchKind = isInterceptionMatch ? "interception" : cleanPathnameIsRequestPathname && options.matchRequestRoute ? "request" : "resolved";
|
|
1157
|
+
const responseStageMatchKind = isInterceptionMatch ? "interception" : cleanPathnameIsRequestPathname && options.matchRequestRoute ? usesRawRequestMatch() ? "raw-request" : "request" : "resolved";
|
|
1042
1158
|
const responseStageRoutePathname = isInterceptionMatch ? preActionRoutePathname : cleanPathnameIsRequestPathname ? requestCleanPathname || "/" : cleanPathname || "/";
|
|
1043
1159
|
const matchedResponseStage = transportedResponseStage ?? options.renderResponseStageLocally;
|
|
1044
1160
|
let runtimeFallbackShells = [];
|
|
@@ -1078,7 +1194,7 @@ async function handleAppRscRequest(options, request, preMiddlewareRequestContext
|
|
|
1078
1194
|
middlewareCookieOverlay,
|
|
1079
1195
|
mountedSlotsHeader,
|
|
1080
1196
|
params,
|
|
1081
|
-
protocolVersion:
|
|
1197
|
+
protocolVersion: 11,
|
|
1082
1198
|
requestOrigin: url.origin,
|
|
1083
1199
|
renderMode,
|
|
1084
1200
|
resolvedUrl,
|
|
@@ -1099,7 +1215,7 @@ async function handleAppRscRequest(options, request, preMiddlewareRequestContext
|
|
|
1099
1215
|
searchParams: resolvedSearchParams
|
|
1100
1216
|
});
|
|
1101
1217
|
}
|
|
1102
|
-
const pageResponse = matchedResponseStage ? await matchedResponseStage(responseStageRequest(), {
|
|
1218
|
+
const pageResponse = withStoredSpecialErrorStatus(matchedResponseStage ? await matchedResponseStage(responseStageRequest(), {
|
|
1103
1219
|
kind: "app-page",
|
|
1104
1220
|
buildId: options.buildId,
|
|
1105
1221
|
cacheability: responseStageCacheability(resolvedUrl),
|
|
@@ -1110,14 +1226,14 @@ async function handleAppRscRequest(options, request, preMiddlewareRequestContext
|
|
|
1110
1226
|
cleanPathname,
|
|
1111
1227
|
draftModeCookie,
|
|
1112
1228
|
forceDynamic: route.forceDynamic === true,
|
|
1113
|
-
interceptionContext: isRscRequest ?
|
|
1229
|
+
interceptionContext: isRscRequest ? interceptionSourceContext : null,
|
|
1114
1230
|
interceptionId: interceptionIdHeader,
|
|
1115
1231
|
isRscRequest,
|
|
1116
1232
|
matchKind: responseStageMatchKind,
|
|
1117
1233
|
middlewareCookieOverlay,
|
|
1118
1234
|
mountedSlotsHeader,
|
|
1119
1235
|
params,
|
|
1120
|
-
protocolVersion:
|
|
1236
|
+
protocolVersion: 11,
|
|
1121
1237
|
requestOrigin: url.origin,
|
|
1122
1238
|
renderMode,
|
|
1123
1239
|
resolvedUrl,
|
|
@@ -1134,7 +1250,7 @@ async function handleAppRscRequest(options, request, preMiddlewareRequestContext
|
|
|
1134
1250
|
actionError: normalizedProgressiveActionError,
|
|
1135
1251
|
actionFailed,
|
|
1136
1252
|
handlerStart,
|
|
1137
|
-
interceptionContext: isRscRequest ?
|
|
1253
|
+
interceptionContext: isRscRequest ? interceptionSourceContext : null,
|
|
1138
1254
|
interceptionId: interceptionIdHeader,
|
|
1139
1255
|
interceptionPathname: cleanPathnameIsRequestPathname ? requestCleanPathname : cleanPathname,
|
|
1140
1256
|
isProgressiveActionRender,
|
|
@@ -1157,7 +1273,7 @@ async function handleAppRscRequest(options, request, preMiddlewareRequestContext
|
|
|
1157
1273
|
scriptNonce,
|
|
1158
1274
|
searchParams: resolvedSearchParams,
|
|
1159
1275
|
renderMode
|
|
1160
|
-
});
|
|
1276
|
+
}), isRscRequest && request.headers.get("Next-Router-Prefetch") === "1" && request.headers.has("Next-Router-Segment-Prefetch"));
|
|
1161
1277
|
if (isProgressiveActionRender) return applyProgressiveActionSideEffects(pageResponse, progressiveActionFormState);
|
|
1162
1278
|
return pageResponse;
|
|
1163
1279
|
}
|
|
@@ -1261,7 +1377,7 @@ function createAppRscRequestHandler(options) {
|
|
|
1261
1377
|
recordCacheability: dispatchResponseStage === void 0,
|
|
1262
1378
|
requestContext: preMiddlewareRequestContext
|
|
1263
1379
|
});
|
|
1264
|
-
return interceptionResponseUncacheable ?
|
|
1380
|
+
return interceptionResponseUncacheable || unmatchedRouteResponses.has(response) ? withNeverCacheControl(response) : response;
|
|
1265
1381
|
}, {
|
|
1266
1382
|
cacheComponents: options.createPprFallbackShells !== void 0,
|
|
1267
1383
|
route: () => new URL(request.url).pathname
|
|
@@ -35,7 +35,9 @@ export type NormalizedRscRequest = {
|
|
|
35
35
|
*
|
|
36
36
|
* 1. Parse URL
|
|
37
37
|
* 2. Protocol-relative URL guard — on the raw pathname, BEFORE normalizePath collapses
|
|
38
|
-
* `//` to `/`.
|
|
38
|
+
* `//` to `/`. Literal repeated slashes / backslashes get Next.js's 308 to the
|
|
39
|
+
* collapsed path; encoded leading `%2F` / `%5C` get a 404. If the guard ran after
|
|
40
|
+
* normalization, `//evil.com` → `/evil.com`
|
|
39
41
|
* would bypass the check and reach the trailing-slash redirector, which echoes the
|
|
40
42
|
* path into a `Location` header that browsers interpret as protocol-relative.
|
|
41
43
|
* 3. Strict percent-decode each segment — throws on malformed sequences (→ 400). Must
|
|
@@ -69,7 +69,9 @@ function tryNormalizeComparablePathAndSearch(value, basePath, baseUrl) {
|
|
|
69
69
|
*
|
|
70
70
|
* 1. Parse URL
|
|
71
71
|
* 2. Protocol-relative URL guard — on the raw pathname, BEFORE normalizePath collapses
|
|
72
|
-
* `//` to `/`.
|
|
72
|
+
* `//` to `/`. Literal repeated slashes / backslashes get Next.js's 308 to the
|
|
73
|
+
* collapsed path; encoded leading `%2F` / `%5C` get a 404. If the guard ran after
|
|
74
|
+
* normalization, `//evil.com` → `/evil.com`
|
|
73
75
|
* would bypass the check and reach the trailing-slash redirector, which echoes the
|
|
74
76
|
* path into a `Location` header that browsers interpret as protocol-relative.
|
|
75
77
|
* 3. Strict percent-decode each segment — throws on malformed sequences (→ 400). Must
|
|
@@ -91,7 +93,7 @@ function tryNormalizeComparablePathAndSearch(value, basePath, baseUrl) {
|
|
|
91
93
|
*/
|
|
92
94
|
function normalizeRscRequest(request, basePath, allowOutsideBasePath = false, assetPrefix = "") {
|
|
93
95
|
const url = new URL(request.url);
|
|
94
|
-
const protoGuard = guardProtocolRelativeUrl(url.pathname);
|
|
96
|
+
const protoGuard = guardProtocolRelativeUrl(url.pathname, url.search);
|
|
95
97
|
if (protoGuard) return protoGuard;
|
|
96
98
|
let decoded;
|
|
97
99
|
try {
|
|
@@ -2,5 +2,5 @@ import { VinextResponseStageDispatchOptions } from "./multi-stage.js";
|
|
|
2
2
|
import { AppWorkerResponseStageProps } from "./app-worker-stages.js";
|
|
3
3
|
import { AppRscHandlerRoute, CreateAppRscHandlerOptions } from "./app-rsc-handler.js";
|
|
4
4
|
//#region src/server/app-rsc-response-stage.d.ts
|
|
5
|
-
export declare function renderAppWorkerResponseStage<TRoute extends AppRscHandlerRoute>(options: CreateAppRscHandlerOptions<TRoute>, rawRequest: Request, ctx: unknown, props: AppWorkerResponseStageProps,
|
|
5
|
+
export declare function renderAppWorkerResponseStage<TRoute extends AppRscHandlerRoute>(options: CreateAppRscHandlerOptions<TRoute>, rawRequest: Request, ctx: unknown, props: AppWorkerResponseStageProps, stageOptions?: VinextResponseStageDispatchOptions): Promise<Response>;
|
|
6
6
|
//#endregion
|
|
@@ -60,14 +60,15 @@ function rematchAppWorkerResponseStageRoute(options, normalized, props) {
|
|
|
60
60
|
let match = null;
|
|
61
61
|
if (props.matchKind === "interception") {
|
|
62
62
|
if (normalized.interceptionContextHeader !== null) match = options.matchInterceptRoute?.(props.routePathname, normalized.interceptionContextHeader, props.interceptionId) ?? null;
|
|
63
|
-
} else if (props.matchKind === "request") match = options.
|
|
63
|
+
} else if (props.matchKind === "raw-request") match = options.matchRawRequestRoute?.(props.routePathname) ?? null;
|
|
64
|
+
else if (props.matchKind === "request") match = options.matchRequestRoute?.(props.routePathname) ?? null;
|
|
64
65
|
else match = options.matchRoute(props.routePathname);
|
|
65
66
|
if (!match) return null;
|
|
66
67
|
if (match.route.pattern !== props.routePattern || !haveSamePageParams(match.params, props.params)) return null;
|
|
67
68
|
return match;
|
|
68
69
|
}
|
|
69
|
-
async function renderAppWorkerResponseStage(options, rawRequest, ctx, props,
|
|
70
|
-
if (props.protocolVersion !==
|
|
70
|
+
async function renderAppWorkerResponseStage(options, rawRequest, ctx, props, stageOptions) {
|
|
71
|
+
if (props.protocolVersion !== 11 || props.buildId !== options.buildId) return new Response("Incompatible vinext App response stage", { status: 409 });
|
|
71
72
|
if (props.kind === "app-full-request") return new Response("Invalid vinext App response stage", { status: 400 });
|
|
72
73
|
options.registerCacheAdapters();
|
|
73
74
|
await options.ensureInstrumentation?.();
|
|
@@ -232,7 +233,8 @@ async function renderAppWorkerResponseStage(options, rawRequest, ctx, props, _st
|
|
|
232
233
|
route,
|
|
233
234
|
searchParams,
|
|
234
235
|
scriptNonce: props.scriptNonce ?? void 0,
|
|
235
|
-
renderMode: normalized.renderMode
|
|
236
|
+
renderMode: normalized.renderMode,
|
|
237
|
+
renderWholeDocument: stageOptions?.renderWholeDocument === true
|
|
236
238
|
});
|
|
237
239
|
}, {
|
|
238
240
|
cacheComponents: options.createPprFallbackShells !== void 0,
|
|
@@ -120,7 +120,11 @@ type AppRscInterceptLookupEntry = {
|
|
|
120
120
|
slotId: string | null;
|
|
121
121
|
targetRouteGraphId: string | null;
|
|
122
122
|
};
|
|
123
|
-
|
|
123
|
+
/**
|
|
124
|
+
* @param decodedPathnamePatterns Static page patterns that answer their
|
|
125
|
+
* segment-decoded pathname (built by `buildAppDecodedPathnamePatterns`).
|
|
126
|
+
*/
|
|
127
|
+
export declare function createAppRscRouteMatcher<Route extends AppRscRouteForMatching>(routes: Route[], decodedPathnamePatterns?: readonly string[]): {
|
|
124
128
|
hasInterceptionId(interceptionId: string): boolean;
|
|
125
129
|
matchRoute(url: string): {
|
|
126
130
|
route: Route;
|
|
@@ -130,6 +134,10 @@ export declare function createAppRscRouteMatcher<Route extends AppRscRouteForMat
|
|
|
130
134
|
route: Route;
|
|
131
135
|
params: AppRscRouteParams;
|
|
132
136
|
} | null;
|
|
137
|
+
matchRawRequestRoute(url: string): {
|
|
138
|
+
route: Route;
|
|
139
|
+
params: AppRscRouteParams;
|
|
140
|
+
} | null;
|
|
133
141
|
findIntercept(pathname: string, sourcePathname?: string | null, interceptionId?: string | null): AppRscInterceptMatch | null;
|
|
134
142
|
};
|
|
135
143
|
//#endregion
|