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
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { decodeMatchedParams, splitPathSegments, splitPathnameForRouteMatch } from "../routing/utils.js";
|
|
1
|
+
import { decodeMatchedParams, decodeRouteSegment, splitPathSegments, splitPathnameForRouteMatch } from "../routing/utils.js";
|
|
2
2
|
import { createAppRouteGraphInterceptionId } from "../routing/app-route-ids.js";
|
|
3
3
|
import { buildRouteTrie, trieMatchRaw } from "../routing/route-trie.js";
|
|
4
4
|
import { extractRawRoutePatternParams, matchRoutePatternPrefix, matchRoutePatternRaw } from "../routing/route-pattern.js";
|
|
@@ -18,17 +18,6 @@ function appRscPathnameParts(pathname, isNormalized = false) {
|
|
|
18
18
|
const normalizedPathname = pathOnly === "/" ? "/" : pathOnly.replace(/\/$/, "");
|
|
19
19
|
return isNormalized ? splitPathSegments(normalizedPathname) : splitPathnameForRouteMatch(normalizedPathname);
|
|
20
20
|
}
|
|
21
|
-
function appRscInterceptionSourcePathnameParts(pathname) {
|
|
22
|
-
const pathOnly = pathname.split("?")[0];
|
|
23
|
-
const normalizedPathname = pathOnly === "/" ? "/" : pathOnly.replace(/\/$/, "");
|
|
24
|
-
return splitPathSegments(normalizedPathname).map((segment) => {
|
|
25
|
-
try {
|
|
26
|
-
return decodeURIComponent(segment);
|
|
27
|
-
} catch {
|
|
28
|
-
return segment;
|
|
29
|
-
}
|
|
30
|
-
});
|
|
31
|
-
}
|
|
32
21
|
function isAppRouteHandlerRoute(route) {
|
|
33
22
|
return route.routeHandler != null || typeof route.__loadRouteHandler === "function";
|
|
34
23
|
}
|
|
@@ -36,11 +25,38 @@ function normalizeMatchedParamsForRoute(result) {
|
|
|
36
25
|
if (isAppRouteHandlerRoute(result.route)) decodeMatchedParams(result.params);
|
|
37
26
|
else canonicalizeAppPageParams(result.params);
|
|
38
27
|
}
|
|
39
|
-
|
|
28
|
+
/**
|
|
29
|
+
* @param decodedPathnamePatterns Static page patterns that answer their
|
|
30
|
+
* segment-decoded pathname (built by `buildAppDecodedPathnamePatterns`).
|
|
31
|
+
*/
|
|
32
|
+
function createAppRscRouteMatcher(routes, decodedPathnamePatterns = []) {
|
|
40
33
|
const routeTrie = buildRouteTrie(routes);
|
|
41
34
|
const interceptLookup = createInterceptLookup(routes);
|
|
42
35
|
const interceptionIds = new Set(interceptLookup.flatMap((entry) => entry.interceptionId === null ? [] : [entry.interceptionId]));
|
|
43
36
|
const routeIndexes = new Map(routes.map((route, index) => [route, index]));
|
|
37
|
+
const decodedPathnamePatternSet = new Set(decodedPathnamePatterns);
|
|
38
|
+
const decodedPathnameRoutes = /* @__PURE__ */ new Map();
|
|
39
|
+
for (const route of routes) {
|
|
40
|
+
const key = route.patternParts.join("/");
|
|
41
|
+
if (decodedPathnamePatternSet.has(route.pattern) && !decodedPathnameRoutes.has(key)) decodedPathnameRoutes.set(key, route);
|
|
42
|
+
}
|
|
43
|
+
function matchRawRequestParts(rawParts) {
|
|
44
|
+
const result = trieMatchRaw(routeTrie, rawParts);
|
|
45
|
+
if (result) normalizeMatchedParamsForRoute(result);
|
|
46
|
+
return result;
|
|
47
|
+
}
|
|
48
|
+
function matchRequestParts(rawParts) {
|
|
49
|
+
const result = matchRawRequestParts(rawParts);
|
|
50
|
+
if (!result) return null;
|
|
51
|
+
if (decodedPathnameRoutes.size > 0 && result.route.patternParts.some((part) => part.startsWith(":")) && !isAppRouteHandlerRoute(result.route) && rawParts.some((part) => part.includes("%"))) {
|
|
52
|
+
const route = decodedPathnameRoutes.get(rawParts.map(decodeRouteSegment).join("/"));
|
|
53
|
+
if (route && route !== result.route) return {
|
|
54
|
+
route,
|
|
55
|
+
params: createRouteParams()
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
return result;
|
|
59
|
+
}
|
|
44
60
|
return {
|
|
45
61
|
hasInterceptionId(interceptionId) {
|
|
46
62
|
return interceptionIds.has(interceptionId);
|
|
@@ -54,16 +70,16 @@ function createAppRscRouteMatcher(routes) {
|
|
|
54
70
|
return result;
|
|
55
71
|
},
|
|
56
72
|
matchRequestRoute(url) {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
return
|
|
73
|
+
return matchRequestParts(appRscPathnameParts(url, true));
|
|
74
|
+
},
|
|
75
|
+
matchRawRequestRoute(url) {
|
|
76
|
+
return matchRawRequestParts(appRscPathnameParts(url, true));
|
|
61
77
|
},
|
|
62
78
|
findIntercept(pathname, sourcePathname = null, interceptionId = null) {
|
|
63
79
|
if (sourcePathname === null) return null;
|
|
64
80
|
const urlParts = appRscPathnameParts(pathname, true);
|
|
65
|
-
const sourceParts =
|
|
66
|
-
const matchedSourceRoute =
|
|
81
|
+
const sourceParts = appRscPathnameParts(sourcePathname, true);
|
|
82
|
+
const matchedSourceRoute = matchRawRequestParts(sourceParts);
|
|
67
83
|
for (const entry of interceptLookup) {
|
|
68
84
|
if (interceptionId !== null && entry.interceptionId !== interceptionId) continue;
|
|
69
85
|
if (!matchInterceptSource(sourceParts, entry)) continue;
|
|
@@ -75,6 +91,7 @@ function createAppRscRouteMatcher(routes) {
|
|
|
75
91
|
const sourceRoute = routes[concreteSourceRouteIndex];
|
|
76
92
|
if (sourceRoute && isAppRouteHandlerRoute(sourceRoute)) continue;
|
|
77
93
|
const matchedSourceParams = concreteSourceRoute ? concreteSourceRoute.params : sourceRoute ? matchSlotOwnerSourceParams(sourceParts, sourceRoute.patternParts, entry.sourceMatchPatternParts !== null) : null;
|
|
94
|
+
if (!concreteSourceRoute && matchedSourceParams) canonicalizeAppPageParams(matchedSourceParams);
|
|
78
95
|
if (matchedSourceParams === null && entry.sourceMatchPatternParts === null) continue;
|
|
79
96
|
const sourceParams = matchedSourceParams && entry.sourceMatchPatternParts !== null ? pickPatternParams(matchedSourceParams, entry.sourceMatchPatternParts) : matchedSourceParams ?? createRouteParams();
|
|
80
97
|
return {
|
|
@@ -19,10 +19,10 @@ import { mergeMiddlewareResponseHeaders } from "./middleware-response-headers.js
|
|
|
19
19
|
import { applyEdgeRuntimeHeader } from "./app-page-response.js";
|
|
20
20
|
import { getNextErrorDigest, parseNextHttpErrorDigest, parseNextRedirectDigest } from "./next-error-digest.js";
|
|
21
21
|
import { buildAppPageTags } from "./implicit-tags.js";
|
|
22
|
+
import "./app-page-stream.js";
|
|
22
23
|
import { getSetCookieName } from "./cookie-utils.js";
|
|
23
24
|
import { createServerActionNotFoundResponse, getServerActionNotFoundMessage, isServerActionNotFoundError } from "./server-action-not-found.js";
|
|
24
25
|
import { resolveAppPageNavigationParams } from "./app-page-element-builder.js";
|
|
25
|
-
import "./app-page-stream.js";
|
|
26
26
|
import { resolveAppPageActionRerenderTarget } from "./app-page-request.js";
|
|
27
27
|
import { createRootParamsUsageController, runWithRootParamsUsage } from "../shims/root-params.js";
|
|
28
28
|
import { createStaticGenerationHeadersContext } from "./app-static-generation.js";
|
|
@@ -46,8 +46,9 @@ export declare function handleSsr(rscStream: ReadableStream<Uint8Array>, navCont
|
|
|
46
46
|
initialDevServerError?: unknown;
|
|
47
47
|
/** Report an SSR/Fizz render failure through instrumentation. */
|
|
48
48
|
onSsrError?: (error: unknown) => unknown;
|
|
49
|
-
/** Mirror inline Flight chunks into Next.js's `self.__next_f` transport
|
|
50
|
-
|
|
49
|
+
/** Mirror inline Flight chunks into Next.js's `self.__next_f` transport, or start
|
|
50
|
+
* mirroring once a function returns true. */
|
|
51
|
+
mirrorNextFlight?: boolean | (() => boolean);
|
|
51
52
|
/** When true, wait for the full React tree (including Suspense boundaries)
|
|
52
53
|
* to resolve before returning the HTML stream. Used for static prerender
|
|
53
54
|
* and ISR cache writes to avoid caching fallback content. */
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import "./server-globals.js";
|
|
2
2
|
import { notFoundResponse } from "./http-error-responses.js";
|
|
3
|
-
import { isOpenRedirectShaped } from "./open-redirect.js";
|
|
4
|
-
import { AppElementsWire } from "./app-elements-wire.js";
|
|
5
|
-
import "./app-elements.js";
|
|
3
|
+
import { isOpenRedirectShaped, repeatedSlashRedirectResponse } from "./open-redirect.js";
|
|
6
4
|
import { isPprFallbackShellAbortError } from "../shims/ppr-fallback-shell.js";
|
|
7
5
|
import { onRenderDynamicLatched } from "../shims/internal/headers-state.js";
|
|
8
6
|
import { AppRouterContext } from "../shims/internal/app-router-context.js";
|
|
@@ -12,18 +10,20 @@ import { createInlineScriptTag, createNonceAttribute, escapeHtmlAttr, safeJsonSt
|
|
|
12
10
|
import { getClientTraceMetadataHTML, markClientTraceMetadataBlock } from "./client-trace-metadata.js";
|
|
13
11
|
import { deferUntilStreamConsumed } from "./defer-until-stream-consumed.js";
|
|
14
12
|
import { setPagesClientAssets } from "./pages-client-assets.js";
|
|
13
|
+
import { AppElementsWire } from "./app-elements-wire.js";
|
|
14
|
+
import "./app-elements.js";
|
|
15
15
|
import { ServerInsertedHTMLContext, clearServerInsertedHTML, getBfcacheIdMapContext, registerServerInsertedHTMLCallback, renderServerInsertedHTML, setNavigationContext } from "../shims/navigation-context-state.js";
|
|
16
16
|
import "../shims/navigation-server.js";
|
|
17
17
|
import DefaultGlobalError from "../shims/default-global-error.js";
|
|
18
18
|
import { BfcacheIdentityMapContext, ElementsContext, Slot } from "../shims/slot.js";
|
|
19
19
|
import { getNextErrorDigest } from "./next-error-digest.js";
|
|
20
20
|
import { createSsrErrorMetaRenderer } from "./app-ssr-error-meta.js";
|
|
21
|
-
import { createClientPageSsrSearchParamsSource } from "./app-page-search-params-observation.js";
|
|
22
21
|
import { isAppRenderAbortError } from "./app-render-abort-error.js";
|
|
23
22
|
import { RSC_FORM_STATE_GLOBAL } from "../client/browser-globals.js";
|
|
24
23
|
import { createNavigationRuntimeRenderedSearchScript, createNavigationRuntimeRscMetadataScript, createRscEmbedTransform, createTickBufferedTransform, waitAtLeastOneReactRenderTask } from "./app-ssr-stream.js";
|
|
25
24
|
import { createInitialBfcacheMaps } from "./app-bfcache-identity.js";
|
|
26
25
|
import { createClientReferencePreloader } from "./app-client-reference-preloader.js";
|
|
26
|
+
import { createClientPageSsrSearchParamsSource } from "./app-page-search-params-observation.js";
|
|
27
27
|
import { runWithRootParamsScope } from "../shims/root-params.js";
|
|
28
28
|
import { runWithNavigationContext } from "../shims/navigation-state.js";
|
|
29
29
|
import { startCandidateSearchParamsGate } from "./app-ssr-search-params-gate.js";
|
|
@@ -357,6 +357,8 @@ async function handleSsr(rscStream, navContext, fontData, options) {
|
|
|
357
357
|
}
|
|
358
358
|
var app_ssr_entry_default = { async fetch(request) {
|
|
359
359
|
const url = new URL(request.url);
|
|
360
|
+
const slashRedirect = repeatedSlashRedirectResponse(url.pathname + url.search);
|
|
361
|
+
if (slashRedirect) return slashRedirect;
|
|
360
362
|
if (isOpenRedirectShaped(url.pathname)) return notFoundResponse();
|
|
361
363
|
const result = await (await import.meta.viteRsc.loadModule("rsc", "index")).default(request);
|
|
362
364
|
if (result instanceof Response) return result;
|
|
@@ -6,7 +6,12 @@ type RscEmbedTransform = {
|
|
|
6
6
|
getRawBuffer(): Promise<ArrayBuffer>;
|
|
7
7
|
};
|
|
8
8
|
type RscEmbedTransformOptions = {
|
|
9
|
-
|
|
9
|
+
/**
|
|
10
|
+
* Mirror inline Flight chunks into Next.js's `self.__next_f` transport. A
|
|
11
|
+
* function is checked at each flush; once it returns true, the chunks
|
|
12
|
+
* already embedded are mirrored first.
|
|
13
|
+
*/
|
|
14
|
+
mirrorNextFlight?: boolean | (() => boolean);
|
|
10
15
|
scriptNonce?: string;
|
|
11
16
|
getInitialNavigationCacheMetadata?: () => InitialNavigationCacheMetadata;
|
|
12
17
|
};
|
|
@@ -49,12 +49,36 @@ function createRscEmbedTransform(embedStream, optionsOrNonce = {}) {
|
|
|
49
49
|
const reader = embedStream.getReader();
|
|
50
50
|
let pendingChunks = [];
|
|
51
51
|
const rawChunks = [];
|
|
52
|
+
let embeddedRawChunkCount = 0;
|
|
53
|
+
let rawBufferTaken = false;
|
|
54
|
+
let finalized = false;
|
|
52
55
|
let reading = false;
|
|
53
56
|
let mirroredNextFlightBootstrap = false;
|
|
54
57
|
const textDecoder = new TextDecoder("utf-8", {
|
|
55
58
|
fatal: true,
|
|
56
59
|
ignoreBOM: true
|
|
57
60
|
});
|
|
61
|
+
function decodeChunk(bytes) {
|
|
62
|
+
try {
|
|
63
|
+
return textDecoder.decode(bytes);
|
|
64
|
+
} catch {
|
|
65
|
+
return [3, bytesToBase64(bytes)];
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
function releaseRawChunks() {
|
|
69
|
+
const mayStartMirror = typeof options.mirrorNextFlight === "function" && !mirroredNextFlightBootstrap && !finalized;
|
|
70
|
+
if (rawBufferTaken && !mayStartMirror) rawChunks.length = 0;
|
|
71
|
+
}
|
|
72
|
+
function startNextFlightMirror() {
|
|
73
|
+
if (mirroredNextFlightBootstrap) return "";
|
|
74
|
+
const mirror = options.mirrorNextFlight;
|
|
75
|
+
if (mirror !== true && (typeof mirror !== "function" || !mirror())) return "";
|
|
76
|
+
mirroredNextFlightBootstrap = true;
|
|
77
|
+
let scripts = createInlineScriptTag(createNextFlightBootstrapScript(), options.scriptNonce) + createInlineScriptTag(createNextFlightCleanupScript(), options.scriptNonce);
|
|
78
|
+
for (let index = 0; index < embeddedRawChunkCount; index++) scripts += createInlineScriptTag(createNextFlightChunkScript(decodeChunk(rawChunks[index])), options.scriptNonce);
|
|
79
|
+
releaseRawChunks();
|
|
80
|
+
return scripts;
|
|
81
|
+
}
|
|
58
82
|
async function pumpReader() {
|
|
59
83
|
if (reading) return;
|
|
60
84
|
reading = true;
|
|
@@ -63,11 +87,7 @@ function createRscEmbedTransform(embedStream, optionsOrNonce = {}) {
|
|
|
63
87
|
const result = await reader.read();
|
|
64
88
|
if (result.done) break;
|
|
65
89
|
rawChunks.push(result.value);
|
|
66
|
-
|
|
67
|
-
pendingChunks.push(textDecoder.decode(result.value));
|
|
68
|
-
} catch {
|
|
69
|
-
pendingChunks.push([3, bytesToBase64(result.value)]);
|
|
70
|
-
}
|
|
90
|
+
pendingChunks.push(decodeChunk(result.value));
|
|
71
91
|
}
|
|
72
92
|
} catch (error) {
|
|
73
93
|
if (process.env.NODE_ENV !== "production") console.warn("[vinext] RSC embed stream read error:", error);
|
|
@@ -80,8 +100,10 @@ function createRscEmbedTransform(embedStream, optionsOrNonce = {}) {
|
|
|
80
100
|
return {
|
|
81
101
|
flush() {
|
|
82
102
|
if (pendingChunks.length === 0) return "";
|
|
103
|
+
const mirrorScripts = startNextFlightMirror();
|
|
83
104
|
const chunks = pendingChunks;
|
|
84
105
|
pendingChunks = [];
|
|
106
|
+
embeddedRawChunkCount += chunks.length;
|
|
85
107
|
const embeddedChunks = [];
|
|
86
108
|
let textChunks = [];
|
|
87
109
|
let textLength = 0;
|
|
@@ -100,30 +122,27 @@ function createRscEmbedTransform(embedStream, optionsOrNonce = {}) {
|
|
|
100
122
|
embeddedChunks.push(chunk);
|
|
101
123
|
}
|
|
102
124
|
flushTextChunks();
|
|
103
|
-
let scripts =
|
|
125
|
+
let scripts = mirrorScripts;
|
|
104
126
|
for (const chunk of embeddedChunks) {
|
|
105
127
|
scripts += createInlineScriptTag(createNavigationRuntimeRscChunkScript(chunk), options.scriptNonce);
|
|
106
|
-
if (options.
|
|
107
|
-
if (!mirroredNextFlightBootstrap) {
|
|
108
|
-
scripts += createInlineScriptTag(createNextFlightBootstrapScript(), options.scriptNonce);
|
|
109
|
-
scripts += createInlineScriptTag(createNextFlightCleanupScript(), options.scriptNonce);
|
|
110
|
-
mirroredNextFlightBootstrap = true;
|
|
111
|
-
}
|
|
112
|
-
scripts += createInlineScriptTag(createNextFlightChunkScript(chunk), options.scriptNonce);
|
|
113
|
-
}
|
|
128
|
+
if (mirroredNextFlightBootstrap) scripts += createInlineScriptTag(createNextFlightChunkScript(chunk), options.scriptNonce);
|
|
114
129
|
}
|
|
115
130
|
return scripts;
|
|
116
131
|
},
|
|
117
132
|
async finalize() {
|
|
118
133
|
await pumpPromise;
|
|
119
134
|
let scripts = this.flush();
|
|
135
|
+
scripts += startNextFlightMirror();
|
|
136
|
+
finalized = true;
|
|
137
|
+
releaseRawChunks();
|
|
120
138
|
scripts += createInlineScriptTag(createNavigationRuntimeRscDoneScript(options.getInitialNavigationCacheMetadata?.()), options.scriptNonce);
|
|
121
139
|
return scripts;
|
|
122
140
|
},
|
|
123
141
|
async getRawBuffer() {
|
|
124
142
|
await pumpPromise;
|
|
125
143
|
const buffer = concatUint8Arrays(rawChunks);
|
|
126
|
-
|
|
144
|
+
rawBufferTaken = true;
|
|
145
|
+
releaseRawChunks();
|
|
127
146
|
return buffer.buffer;
|
|
128
147
|
}
|
|
129
148
|
};
|
|
@@ -3,7 +3,7 @@ import { VinextResponseStageCacheability, VinextResponseStageDispatchOptions, Vi
|
|
|
3
3
|
import { AppRscRenderMode } from "./app-rsc-render-mode.js";
|
|
4
4
|
import { TrustedPrerenderState } from "./prerender-route-params.js";
|
|
5
5
|
//#region src/server/app-worker-stages.d.ts
|
|
6
|
-
export declare const APP_WORKER_RESPONSE_STAGE_PROTOCOL_VERSION =
|
|
6
|
+
export declare const APP_WORKER_RESPONSE_STAGE_PROTOCOL_VERSION = 11;
|
|
7
7
|
export declare const APP_METADATA_RESPONSE_STAGE_NO_MATCH_HEADER = "x-vinext-app-metadata-stage-no-match";
|
|
8
8
|
type AppPageParams = Record<string, string | string[]>;
|
|
9
9
|
type AppWorkerResponseStageEnvelope = {
|
|
@@ -34,7 +34,7 @@ export type AppMatchedWorkerResponseStageProps = AppWorkerResponseStageEnvelope
|
|
|
34
34
|
interceptionContext: string | null;
|
|
35
35
|
interceptionId: string | null;
|
|
36
36
|
isRscRequest: boolean;
|
|
37
|
-
matchKind: "interception" | "request" | "resolved";
|
|
37
|
+
matchKind: "interception" | "raw-request" | "request" | "resolved";
|
|
38
38
|
mountedSlotsHeader: string | null;
|
|
39
39
|
params: AppPageParams;
|
|
40
40
|
resolvedUrl: string;
|
|
@@ -3,7 +3,7 @@ import { cacheabilityManifestPageState, cacheabilityRequestIdentity, cacheabilit
|
|
|
3
3
|
import { keepOnlyValidatedRscCacheBustingSearchParam } from "./app-rsc-cache-busting.js";
|
|
4
4
|
import { isTrustedPrerenderState } from "./prerender-route-params.js";
|
|
5
5
|
//#region src/server/app-worker-stages.ts
|
|
6
|
-
const APP_WORKER_RESPONSE_STAGE_PROTOCOL_VERSION =
|
|
6
|
+
const APP_WORKER_RESPONSE_STAGE_PROTOCOL_VERSION = 11;
|
|
7
7
|
const APP_METADATA_RESPONSE_STAGE_NO_MATCH_HEADER = "x-vinext-app-metadata-stage-no-match";
|
|
8
8
|
const STATIC_FILE_SIGNAL_TOKEN_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
9
9
|
/** Normalize a shared App page HEAD request onto its method-invariant GET representation. */
|
|
@@ -80,7 +80,7 @@ function isCanonicalHttpOrigin(value) {
|
|
|
80
80
|
function isAppWorkerResponseStageProps(value) {
|
|
81
81
|
if (!value || typeof value !== "object") return false;
|
|
82
82
|
const props = value;
|
|
83
|
-
if (props.protocolVersion !==
|
|
83
|
+
if (props.protocolVersion !== 11 || props.buildId !== null && typeof props.buildId !== "string" || !isResponseStageCacheability(props.cacheability) || props.draftModeCookie !== null && typeof props.draftModeCookie !== "string" || props.middlewareCookieOverlay !== null && typeof props.middlewareCookieOverlay !== "string" || !isCanonicalHttpOrigin(props.requestOrigin) || props.scriptNonce !== null && typeof props.scriptNonce !== "string") return false;
|
|
84
84
|
if (props.kind === "app-full-request") return typeof props.prerenderDiscovery === "boolean" && typeof props.staticFileSignalToken === "string" && STATIC_FILE_SIGNAL_TOKEN_RE.test(props.staticFileSignalToken) && (props.trustedPrerenderState === null || isTrustedPrerenderState(props.trustedPrerenderState));
|
|
85
85
|
if (props.kind === "hybrid-pages") {
|
|
86
86
|
const hybrid = props;
|
|
@@ -91,7 +91,7 @@ function isAppWorkerResponseStageProps(value) {
|
|
|
91
91
|
const special = props;
|
|
92
92
|
return typeof special.canonicalPathname === "string" && special.canonicalPathname.startsWith("/") && typeof special.cleanPathname === "string" && special.cleanPathname.startsWith("/") && typeof special.isRscRequest === "boolean" && (special.mountedSlotsHeader === null || typeof special.mountedSlotsHeader === "string") && typeof special.resolvedUrl === "string" && special.resolvedUrl.startsWith("/") && (props.kind !== "app-metadata" || typeof props.routePathname === "string" && props.routePathname.startsWith("/")) && (special.renderMode === "navigation" || special.renderMode === "prefetch-empty" || special.renderMode === "prefetch-dynamic-shell" || special.renderMode === "prefetch-loading-shell");
|
|
93
93
|
}
|
|
94
|
-
return (props.kind === "app-page" || props.kind === "app-route-handler") && typeof props.bypassInterceptionContextCache === "boolean" && typeof props.cachePathname === "string" && props.cachePathname.startsWith("/") && typeof props.canUseCanonicalLoadingShell === "boolean" && typeof props.canonicalPathname === "string" && props.canonicalPathname.startsWith("/") && typeof props.cleanPathname === "string" && props.cleanPathname.startsWith("/") && (props.forceDynamic === void 0 || typeof props.forceDynamic === "boolean") && (props.interceptionContext === null || typeof props.interceptionContext === "string") && (props.interceptionId === null || typeof props.interceptionId === "string") && typeof props.isRscRequest === "boolean" && (props.matchKind === "interception" || props.matchKind === "request" || props.matchKind === "resolved") && (props.mountedSlotsHeader === null || typeof props.mountedSlotsHeader === "string") && isAppPageParams(props.params) && typeof props.resolvedUrl === "string" && props.resolvedUrl.startsWith("/") && typeof props.routePattern === "string" && props.routePattern.startsWith("/") && typeof props.routePathname === "string" && props.routePathname.startsWith("/") && (props.renderMode === "navigation" || props.renderMode === "prefetch-empty" || props.renderMode === "prefetch-dynamic-shell" || props.renderMode === "prefetch-loading-shell");
|
|
94
|
+
return (props.kind === "app-page" || props.kind === "app-route-handler") && typeof props.bypassInterceptionContextCache === "boolean" && typeof props.cachePathname === "string" && props.cachePathname.startsWith("/") && typeof props.canUseCanonicalLoadingShell === "boolean" && typeof props.canonicalPathname === "string" && props.canonicalPathname.startsWith("/") && typeof props.cleanPathname === "string" && props.cleanPathname.startsWith("/") && (props.forceDynamic === void 0 || typeof props.forceDynamic === "boolean") && (props.interceptionContext === null || typeof props.interceptionContext === "string") && (props.interceptionId === null || typeof props.interceptionId === "string") && typeof props.isRscRequest === "boolean" && (props.matchKind === "interception" || props.matchKind === "raw-request" || props.matchKind === "request" || props.matchKind === "resolved") && (props.mountedSlotsHeader === null || typeof props.mountedSlotsHeader === "string") && isAppPageParams(props.params) && typeof props.resolvedUrl === "string" && props.resolvedUrl.startsWith("/") && typeof props.routePattern === "string" && props.routePattern.startsWith("/") && typeof props.routePathname === "string" && props.routePathname.startsWith("/") && (props.renderMode === "navigation" || props.renderMode === "prefetch-empty" || props.renderMode === "prefetch-dynamic-shell" || props.renderMode === "prefetch-loading-shell");
|
|
95
95
|
}
|
|
96
96
|
function isSerializedHeaders(value) {
|
|
97
97
|
return Array.isArray(value) && value.every((entry) => Array.isArray(entry) && entry.length === 2 && typeof entry[0] === "string" && typeof entry[1] === "string");
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import "../utils/protocol-headers.js";
|
|
2
2
|
import { isVinextRscVaryField } from "./app-rsc-vary.js";
|
|
3
|
-
import { VINEXT_CACHEABILITY_PROBE_HEADER, VINEXT_CACHEABILITY_PROBE_ROUTE_HEADER } from "./headers.js";
|
|
3
|
+
import { VINEXT_CACHEABILITY_PROBE_HEADER, VINEXT_CACHEABILITY_PROBE_ROUTE_HEADER, VINEXT_SPECIAL_ERROR_STATUS_HEADER } from "./headers.js";
|
|
4
4
|
import { isNonCacheableCacheControl } from "../shims/cdn-cache.js";
|
|
5
5
|
import { CACHEABILITY_REQUEST_STATE, recordConfigCdnCachePolicyHeader } from "../shims/cacheability-classification.js";
|
|
6
6
|
import { NO_STORE_CACHE_CONTROL, applyCdnResponseBuildIdentityHeaders, applyCdnResponseHeaders, hasExplicitNonCacheableResponsePolicy, isCdnResponsePolicyHeader, readCdnResponseCacheControl, readCdnResponsePolicyHeaderName } from "./cache-control.js";
|
|
@@ -348,9 +348,10 @@ async function captureCacheabilityAdmissionBody(body, deadlineAt, limit = CACHEA
|
|
|
348
348
|
throw error;
|
|
349
349
|
}
|
|
350
350
|
}
|
|
351
|
-
function responseWithCachePolicy(response, body, outcome, browserCacheControl) {
|
|
351
|
+
function responseWithCachePolicy(response, body, outcome, browserCacheControl, completedStatus) {
|
|
352
352
|
const headers = new Headers(response.headers);
|
|
353
353
|
if (typeof body === "string") headers.delete("Content-Length");
|
|
354
|
+
for (const [name, value] of Object.entries(completedStatus?.headers ?? {})) if (!headers.has(name)) headers.set(name, value);
|
|
354
355
|
applyCdnResponseHeaders(headers, outcome?.cacheable === true && outcome.cacheControl ? {
|
|
355
356
|
cacheControl: outcome.cacheControl,
|
|
356
357
|
tags: outcome.tags,
|
|
@@ -359,10 +360,13 @@ function responseWithCachePolicy(response, body, outcome, browserCacheControl) {
|
|
|
359
360
|
cacheControl: NO_STORE_CACHE_CONTROL,
|
|
360
361
|
browserCacheControl
|
|
361
362
|
});
|
|
363
|
+
const status = completedStatus?.status ?? response.status;
|
|
364
|
+
const completed = completedStatus?.status;
|
|
365
|
+
if (completed === 401 || completed === 403 || completed === 404) headers.set(VINEXT_SPECIAL_ERROR_STATUS_HEADER, String(completed));
|
|
362
366
|
return new Response(body, {
|
|
363
367
|
headers,
|
|
364
|
-
status
|
|
365
|
-
statusText: response.statusText
|
|
368
|
+
status,
|
|
369
|
+
statusText: status === response.status ? response.statusText : ""
|
|
366
370
|
});
|
|
367
371
|
}
|
|
368
372
|
async function stripSharedHtmlClientTraceMetadata(body, marker) {
|
|
@@ -499,7 +503,7 @@ async function finalizeWorkerCacheabilityAdmission(response, state) {
|
|
|
499
503
|
const effectivePolicyHeader = readCdnResponsePolicyHeaderName(response.headers);
|
|
500
504
|
const replacesRendererPolicy = outcome !== rendererOutcome && effectivePolicyHeader !== null && state.configCdnCachePolicy?.get(effectivePolicyHeader) === response.headers.get(effectivePolicyHeader);
|
|
501
505
|
if (state.route.kind === "app-page" && !replacesRendererPolicy && rendererOutcome?.searchParamsUnread !== true) return responseWithCachePolicy(response, captured.body, null, browserCacheControl);
|
|
502
|
-
return responseWithCachePolicy(response, representation === "html" && state.clientTraceMetadataMarker ? await stripSharedHtmlClientTraceMetadata(captured.body, state.clientTraceMetadataMarker) : captured.body, outcome, browserCacheControl);
|
|
506
|
+
return responseWithCachePolicy(response, representation === "html" && state.clientTraceMetadataMarker ? await stripSharedHtmlClientTraceMetadata(captured.body, state.clientTraceMetadataMarker) : captured.body, outcome, browserCacheControl, rendererOutcome ?? void 0);
|
|
503
507
|
}
|
|
504
508
|
async function finalizeWorkerCacheabilityResponse(response, ctx) {
|
|
505
509
|
const state = readState(ctx);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { matchHeaders } from "../config/config-matchers.js";
|
|
2
|
+
import "./headers.js";
|
|
2
3
|
import { markRouteCacheabilityConfigCdnCachePolicy, markRouteCacheabilityDynamic, markRouteCacheabilityFinalResponseUncacheable } from "../shims/cacheability-classification.js";
|
|
3
4
|
import { isCdnResponsePolicyHeader, isNonCacheableCdnResponsePolicy } from "./cache-control.js";
|
|
4
5
|
import { mergeVaryHeader } from "./middleware-response-headers.js";
|
|
@@ -77,6 +78,7 @@ function applyConfigHeadersToResponse(responseHeaders, options) {
|
|
|
77
78
|
if (options.recordCacheability !== false) markExplicitConfigResponseVeto(matched);
|
|
78
79
|
for (const header of matched) {
|
|
79
80
|
const lowerName = header.key.toLowerCase();
|
|
81
|
+
if (lowerName === "x-vinext-special-error-status") continue;
|
|
80
82
|
if (lowerName === "link") {
|
|
81
83
|
if (options.middlewareHeaders?.get(lowerName)) continue;
|
|
82
84
|
const postConfigLink = options.appendToPostConfigLink ? responseHeaders.get(lowerName) : null;
|
|
@@ -21,6 +21,7 @@ import { createInlineScriptTag, createNonceAttribute, safeJsonStringify } from "
|
|
|
21
21
|
import { applyDocumentAssetProps, extractDocumentAssetProps, markDocumentAssetPropsProtectedTags, stripDocumentAssetPropsProtectionMarkers } from "./pages-document-asset-props.js";
|
|
22
22
|
import { getClientTraceMetadataHTML } from "./client-trace-metadata.js";
|
|
23
23
|
import { getScriptNonceFromNodeHeaderSources } from "./csp.js";
|
|
24
|
+
import { stripViteModuleQuery } from "../utils/path.js";
|
|
24
25
|
import { logRequest, now } from "./request-log.js";
|
|
25
26
|
import { buildDefaultPagesNotFoundResponse } from "./pages-default-404.js";
|
|
26
27
|
import { buildPagesReadinessNextData } from "./pages-readiness.js";
|
|
@@ -148,6 +149,15 @@ function stripDevPagesNotFoundFramingHeaders(res) {
|
|
|
148
149
|
res.removeHeader("Transfer-Encoding");
|
|
149
150
|
}
|
|
150
151
|
/**
|
|
152
|
+
* Vite's `transformIndexHtml` treats its URL as the HTML document's path, so
|
|
153
|
+
* the request query carries no meaning there. Since Vite 8.3.3 it also maps
|
|
154
|
+
* `/?q` onto the project root directory and then watches the filesystem root
|
|
155
|
+
* for each inline `<style>` proxy module, so pass the bare path.
|
|
156
|
+
*/
|
|
157
|
+
function toHtmlTransformPath(url) {
|
|
158
|
+
return stripViteModuleQuery(url);
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
151
161
|
* Stream a Pages Router page response using progressive SSR.
|
|
152
162
|
*
|
|
153
163
|
* Sends the HTML shell (head, layout, Suspense fallbacks) immediately
|
|
@@ -244,7 +254,7 @@ async function streamPageToResponseImpl(routePattern, res, element, options) {
|
|
|
244
254
|
</body>
|
|
245
255
|
</html>`;
|
|
246
256
|
}
|
|
247
|
-
let transformedShell = await server.transformIndexHtml(url, shellTemplate);
|
|
257
|
+
let transformedShell = await server.transformIndexHtml(toHtmlTransformPath(url), shellTemplate);
|
|
248
258
|
transformedShell = stripDocumentAssetPropsProtectionMarkers(applyDocumentAssetProps(transformedShell, documentAssetProps, {
|
|
249
259
|
configuredCrossOrigin: crossOrigin,
|
|
250
260
|
protectedAssetMarker,
|
|
@@ -1130,7 +1140,7 @@ async function renderErrorPage(server, runner, req, res, url, pagesDir, statusCo
|
|
|
1130
1140
|
${protectedErrorScripts}
|
|
1131
1141
|
</body>
|
|
1132
1142
|
</html>`;
|
|
1133
|
-
const transformedHtml = stripDocumentAssetPropsProtectionMarkers(applyDocumentAssetProps(await server.transformIndexHtml(url, html), {}, {
|
|
1143
|
+
const transformedHtml = stripDocumentAssetPropsProtectionMarkers(applyDocumentAssetProps(await server.transformIndexHtml(toHtmlTransformPath(url), html), {}, {
|
|
1134
1144
|
configuredCrossOrigin: context.crossOrigin,
|
|
1135
1145
|
protectedAssetMarker
|
|
1136
1146
|
}), protectedAssetMarker);
|
package/dist/server/headers.d.ts
CHANGED
|
@@ -58,6 +58,12 @@ export declare const VINEXT_PARAMS_HEADER = "X-Vinext-Params";
|
|
|
58
58
|
export declare const VINEXT_DYNAMIC_STALE_TIME_HEADER = "X-Vinext-Dynamic-Stale-Time";
|
|
59
59
|
/** Marks an RSC body carrying completion metadata after the Flight payload. */
|
|
60
60
|
export declare const VINEXT_RSC_COMPLETION_METADATA_HEADER = "X-Vinext-Rsc-Completion-Metadata";
|
|
61
|
+
/**
|
|
62
|
+
* Carries the stored status of an App page RSC response whose 401, 403 or 404
|
|
63
|
+
* is its page's notFound(), forbidden() or unauthorized(). Middleware and
|
|
64
|
+
* config headers can't set it, and the request stage strips it.
|
|
65
|
+
*/
|
|
66
|
+
export declare const VINEXT_SPECIAL_ERROR_STATUS_HEADER = "x-vinext-special-error-status";
|
|
61
67
|
/** URL-encoded rendered path and search after middleware/config rewrites. */
|
|
62
68
|
export declare const VINEXT_RENDERED_PATH_AND_SEARCH_HEADER = "X-Vinext-Rendered-Path-And-Search";
|
|
63
69
|
/** Prerender-only JSON side channel carrying request cacheLife metadata. */
|
|
@@ -66,6 +72,14 @@ export declare const VINEXT_PRERENDER_CACHE_LIFE_HEADER = "x-vinext-prerender-ca
|
|
|
66
72
|
export declare const VINEXT_PRERENDER_REWRITTEN_HEADER = "x-vinext-prerender-rewritten";
|
|
67
73
|
/** Marks a local prerender-server 500 that originated from a thrown render error. */
|
|
68
74
|
export declare const VINEXT_PRERENDER_RENDER_ERROR_HEADER = "x-vinext-prerender-render-error";
|
|
75
|
+
/**
|
|
76
|
+
* Prerender-only marker: the response's 401, 403, 404 or redirect is its page's
|
|
77
|
+
* notFound(), forbidden(), unauthorized() or redirect(), which escaped the shell.
|
|
78
|
+
* Its value is the JSON of the headers to store: a redirect's own location, or
|
|
79
|
+
* `{}`. Only the prerender server sets it, from the special error the page
|
|
80
|
+
* recorded on its ctx, and it drops one that the response carries.
|
|
81
|
+
*/
|
|
82
|
+
export declare const VINEXT_PRERENDER_SPECIAL_ERROR_HEADER = "x-vinext-prerender-special-error";
|
|
69
83
|
/** Internal marker persisted only inside metadata-route APP_ROUTE cache values. */
|
|
70
84
|
export declare const VINEXT_METADATA_ROUTE_CACHE_HEADER = "x-vinext-metadata-route-cache";
|
|
71
85
|
/** Disabled-by-default client hint describing already-held App Router payload entries. */
|
package/dist/server/headers.js
CHANGED
|
@@ -58,6 +58,12 @@ const VINEXT_PARAMS_HEADER = "X-Vinext-Params";
|
|
|
58
58
|
const VINEXT_DYNAMIC_STALE_TIME_HEADER = "X-Vinext-Dynamic-Stale-Time";
|
|
59
59
|
/** Marks an RSC body carrying completion metadata after the Flight payload. */
|
|
60
60
|
const VINEXT_RSC_COMPLETION_METADATA_HEADER = "X-Vinext-Rsc-Completion-Metadata";
|
|
61
|
+
/**
|
|
62
|
+
* Carries the stored status of an App page RSC response whose 401, 403 or 404
|
|
63
|
+
* is its page's notFound(), forbidden() or unauthorized(). Middleware and
|
|
64
|
+
* config headers can't set it, and the request stage strips it.
|
|
65
|
+
*/
|
|
66
|
+
const VINEXT_SPECIAL_ERROR_STATUS_HEADER = "x-vinext-special-error-status";
|
|
61
67
|
/** URL-encoded rendered path and search after middleware/config rewrites. */
|
|
62
68
|
const VINEXT_RENDERED_PATH_AND_SEARCH_HEADER = "X-Vinext-Rendered-Path-And-Search";
|
|
63
69
|
/** Prerender-only JSON side channel carrying request cacheLife metadata. */
|
|
@@ -66,6 +72,14 @@ const VINEXT_PRERENDER_CACHE_LIFE_HEADER = "x-vinext-prerender-cache-life";
|
|
|
66
72
|
const VINEXT_PRERENDER_REWRITTEN_HEADER = "x-vinext-prerender-rewritten";
|
|
67
73
|
/** Marks a local prerender-server 500 that originated from a thrown render error. */
|
|
68
74
|
const VINEXT_PRERENDER_RENDER_ERROR_HEADER = "x-vinext-prerender-render-error";
|
|
75
|
+
/**
|
|
76
|
+
* Prerender-only marker: the response's 401, 403, 404 or redirect is its page's
|
|
77
|
+
* notFound(), forbidden(), unauthorized() or redirect(), which escaped the shell.
|
|
78
|
+
* Its value is the JSON of the headers to store: a redirect's own location, or
|
|
79
|
+
* `{}`. Only the prerender server sets it, from the special error the page
|
|
80
|
+
* recorded on its ctx, and it drops one that the response carries.
|
|
81
|
+
*/
|
|
82
|
+
const VINEXT_PRERENDER_SPECIAL_ERROR_HEADER = "x-vinext-prerender-special-error";
|
|
69
83
|
/** Internal marker persisted only inside metadata-route APP_ROUTE cache values. */
|
|
70
84
|
const VINEXT_METADATA_ROUTE_CACHE_HEADER = "x-vinext-metadata-route-cache";
|
|
71
85
|
/** Disabled-by-default client hint describing already-held App Router payload entries. */
|
|
@@ -175,4 +189,4 @@ const VINEXT_INTERNAL_HEADERS = [
|
|
|
175
189
|
VINEXT_TRACE_ROUTE_HEADER.toLowerCase()
|
|
176
190
|
];
|
|
177
191
|
//#endregion
|
|
178
|
-
export { ACTION_FORWARDED_HEADER, ACTION_REDIRECT_HEADER, ACTION_REDIRECT_STATUS_HEADER, ACTION_REDIRECT_TYPE_HEADER, ACTION_REVALIDATED_HEADER, FLIGHT_HEADERS, INTERNAL_HEADERS, MIDDLEWARE_HEADER_PREFIX, MIDDLEWARE_NEXT_HEADER, MIDDLEWARE_REWRITE_HEADER, MIDDLEWARE_SET_COOKIE_HEADER, MIDDLEWARE_SKIP_HEADER, NEXTJS_ACTION_NOT_FOUND_HEADER, NEXTJS_CACHE_HEADER, NEXTJS_DEPLOYMENT_ID_HEADER, NEXT_ACTION_HEADER, NEXT_CACHE_TAGS_HEADER, NEXT_HTML_REQUEST_ID_HEADER, NEXT_REQUEST_ID_HEADER, NEXT_ROUTER_PREFETCH_HEADER, NEXT_ROUTER_SEGMENT_PREFETCH_HEADER, NEXT_ROUTER_STALE_TIME_HEADER, NEXT_ROUTER_STATE_TREE_HEADER, NEXT_URL_HEADER, RSC_ACTION_HEADER, RSC_HEADER, VINEXT_CACHEABILITY_PROBE_HEADER, VINEXT_CACHEABILITY_PROBE_QUERY_PARAM, VINEXT_CACHEABILITY_PROBE_ROUTE_HEADER, VINEXT_CACHE_HEADER, VINEXT_CLIENT_REUSE_MANIFEST_HEADER, VINEXT_DYNAMIC_STALE_TIME_HEADER, VINEXT_EXPECTED_WORKER_VERSION_HEADER, VINEXT_INTERCEPTION_CONTEXT_HEADER, VINEXT_INTERCEPTION_ID_HEADER, VINEXT_INTERNAL_HEADERS, VINEXT_METADATA_ROUTE_CACHE_HEADER, VINEXT_MOUNTED_SLOTS_HEADER, VINEXT_MW_CTX_HEADER, VINEXT_PARAMS_HEADER, VINEXT_PRERENDER_CACHE_LIFE_HEADER, VINEXT_PRERENDER_METADATA_ROUTES_PATH, VINEXT_PRERENDER_PAGES_STATIC_PATHS_PATH, VINEXT_PRERENDER_READINESS_HEADER, VINEXT_PRERENDER_READINESS_PATH, VINEXT_PRERENDER_RENDER_ERROR_HEADER, VINEXT_PRERENDER_REWRITTEN_HEADER, VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, VINEXT_PRERENDER_SECRET_HEADER, VINEXT_PRERENDER_SPECULATIVE_HEADER, VINEXT_PRERENDER_STATIC_PARAMS_PATH, VINEXT_RENDERED_PATH_AND_SEARCH_HEADER, VINEXT_REVALIDATED_CACHE_TAG_HEADER, VINEXT_REVALIDATE_HEADER, VINEXT_REVALIDATE_HOST_HEADER, VINEXT_RSC_COMPLETION_METADATA_HEADER, VINEXT_RSC_MARKER_HEADER, VINEXT_RSC_REDIRECT_HEADER, VINEXT_RSC_REDIRECT_TYPE_HEADER, VINEXT_RSC_RENDER_MODE_HEADER, VINEXT_RSC_STATE_FINGERPRINT_HEADER, VINEXT_RSC_VARY_HEADER, VINEXT_STALE_TIME_PENDING_HEADER, VINEXT_TIMING_HEADER, VINEXT_TRACE_BUFFERED_BODY_HEADER, VINEXT_TRACE_ERROR_HEADER, VINEXT_TRACE_ROUTE_HEADER, isVinextRscVaryField };
|
|
192
|
+
export { ACTION_FORWARDED_HEADER, ACTION_REDIRECT_HEADER, ACTION_REDIRECT_STATUS_HEADER, ACTION_REDIRECT_TYPE_HEADER, ACTION_REVALIDATED_HEADER, FLIGHT_HEADERS, INTERNAL_HEADERS, MIDDLEWARE_HEADER_PREFIX, MIDDLEWARE_NEXT_HEADER, MIDDLEWARE_REWRITE_HEADER, MIDDLEWARE_SET_COOKIE_HEADER, MIDDLEWARE_SKIP_HEADER, NEXTJS_ACTION_NOT_FOUND_HEADER, NEXTJS_CACHE_HEADER, NEXTJS_DEPLOYMENT_ID_HEADER, NEXT_ACTION_HEADER, NEXT_CACHE_TAGS_HEADER, NEXT_HTML_REQUEST_ID_HEADER, NEXT_REQUEST_ID_HEADER, NEXT_ROUTER_PREFETCH_HEADER, NEXT_ROUTER_SEGMENT_PREFETCH_HEADER, NEXT_ROUTER_STALE_TIME_HEADER, NEXT_ROUTER_STATE_TREE_HEADER, NEXT_URL_HEADER, RSC_ACTION_HEADER, RSC_HEADER, VINEXT_CACHEABILITY_PROBE_HEADER, VINEXT_CACHEABILITY_PROBE_QUERY_PARAM, VINEXT_CACHEABILITY_PROBE_ROUTE_HEADER, VINEXT_CACHE_HEADER, VINEXT_CLIENT_REUSE_MANIFEST_HEADER, VINEXT_DYNAMIC_STALE_TIME_HEADER, VINEXT_EXPECTED_WORKER_VERSION_HEADER, VINEXT_INTERCEPTION_CONTEXT_HEADER, VINEXT_INTERCEPTION_ID_HEADER, VINEXT_INTERNAL_HEADERS, VINEXT_METADATA_ROUTE_CACHE_HEADER, VINEXT_MOUNTED_SLOTS_HEADER, VINEXT_MW_CTX_HEADER, VINEXT_PARAMS_HEADER, VINEXT_PRERENDER_CACHE_LIFE_HEADER, VINEXT_PRERENDER_METADATA_ROUTES_PATH, VINEXT_PRERENDER_PAGES_STATIC_PATHS_PATH, VINEXT_PRERENDER_READINESS_HEADER, VINEXT_PRERENDER_READINESS_PATH, VINEXT_PRERENDER_RENDER_ERROR_HEADER, VINEXT_PRERENDER_REWRITTEN_HEADER, VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, VINEXT_PRERENDER_SECRET_HEADER, VINEXT_PRERENDER_SPECIAL_ERROR_HEADER, VINEXT_PRERENDER_SPECULATIVE_HEADER, VINEXT_PRERENDER_STATIC_PARAMS_PATH, VINEXT_RENDERED_PATH_AND_SEARCH_HEADER, VINEXT_REVALIDATED_CACHE_TAG_HEADER, VINEXT_REVALIDATE_HEADER, VINEXT_REVALIDATE_HOST_HEADER, VINEXT_RSC_COMPLETION_METADATA_HEADER, VINEXT_RSC_MARKER_HEADER, VINEXT_RSC_REDIRECT_HEADER, VINEXT_RSC_REDIRECT_TYPE_HEADER, VINEXT_RSC_RENDER_MODE_HEADER, VINEXT_RSC_STATE_FINGERPRINT_HEADER, VINEXT_RSC_VARY_HEADER, VINEXT_SPECIAL_ERROR_STATUS_HEADER, VINEXT_STALE_TIME_PENDING_HEADER, VINEXT_TIMING_HEADER, VINEXT_TRACE_BUFFERED_BODY_HEADER, VINEXT_TRACE_ERROR_HEADER, VINEXT_TRACE_ROUTE_HEADER, isVinextRscVaryField };
|
|
@@ -117,6 +117,22 @@ export declare const IMAGE_CONTENT_SECURITY_POLICY = "script-src 'none'; frame-s
|
|
|
117
117
|
* Returns false for SVG (unless dangerouslyAllowSVG is true), HTML, or any non-image type.
|
|
118
118
|
*/
|
|
119
119
|
export declare function isSafeImageContentType(contentType: string | null, dangerouslyAllowSVG?: boolean): boolean;
|
|
120
|
+
/**
|
|
121
|
+
* Cache state for a successful image response, sent as both `X-Nextjs-Cache`
|
|
122
|
+
* and `X-Vinext-Cache`.
|
|
123
|
+
*
|
|
124
|
+
* Next.js keeps optimized images in an image cache and labels each 200 with
|
|
125
|
+
* where the bytes came from: `MISS` when the optimizer ran for this request,
|
|
126
|
+
* `HIT` or `STALE` when the cache served them. vinext has no image cache on any
|
|
127
|
+
* runtime, so every successful response is transformed (or passed through) from
|
|
128
|
+
* the source for the request that receives it. Next.js behaves the same way
|
|
129
|
+
* when its image cache is disabled (`images.maximumDiskCacheSize: 0`): every
|
|
130
|
+
* 200 is a `MISS`. `HIT` and `STALE` need a cache of optimized images.
|
|
131
|
+
*
|
|
132
|
+
* Next.js sets the header after its ETag check, so a 304 and error responses
|
|
133
|
+
* go out without it. Only full image responses carry either header here too.
|
|
134
|
+
*/
|
|
135
|
+
export declare const IMAGE_RESPONSE_CACHE_STATE = "MISS";
|
|
120
136
|
/**
|
|
121
137
|
* Handlers for image optimization I/O operations.
|
|
122
138
|
* Workers provide these callbacks to adapt their specific bindings.
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { badRequestResponse } from "./http-error-responses.js";
|
|
2
|
+
import { setCacheStateHeaders } from "./cache-headers.js";
|
|
2
3
|
//#region src/server/image-optimization.ts
|
|
3
4
|
/**
|
|
4
5
|
* Image optimization request handler.
|
|
@@ -182,11 +183,32 @@ function setImageSecurityHeaders(headers, config) {
|
|
|
182
183
|
headers.set("X-Content-Type-Options", "nosniff");
|
|
183
184
|
headers.set("Content-Disposition", config?.contentDispositionType === "attachment" ? "attachment" : "inline");
|
|
184
185
|
}
|
|
185
|
-
|
|
186
|
-
|
|
186
|
+
/**
|
|
187
|
+
* Cache state for a successful image response, sent as both `X-Nextjs-Cache`
|
|
188
|
+
* and `X-Vinext-Cache`.
|
|
189
|
+
*
|
|
190
|
+
* Next.js keeps optimized images in an image cache and labels each 200 with
|
|
191
|
+
* where the bytes came from: `MISS` when the optimizer ran for this request,
|
|
192
|
+
* `HIT` or `STALE` when the cache served them. vinext has no image cache on any
|
|
193
|
+
* runtime, so every successful response is transformed (or passed through) from
|
|
194
|
+
* the source for the request that receives it. Next.js behaves the same way
|
|
195
|
+
* when its image cache is disabled (`images.maximumDiskCacheSize: 0`): every
|
|
196
|
+
* 200 is a `MISS`. `HIT` and `STALE` need a cache of optimized images.
|
|
197
|
+
*
|
|
198
|
+
* Next.js sets the header after its ETag check, so a 304 and error responses
|
|
199
|
+
* go out without it. Only full image responses carry either header here too.
|
|
200
|
+
*/
|
|
201
|
+
const IMAGE_RESPONSE_CACHE_STATE = "MISS";
|
|
202
|
+
/** Headers shared by every successful image response, transformed or not. */
|
|
203
|
+
function setImageResponseHeaders(headers, config) {
|
|
187
204
|
headers.set("Cache-Control", IMAGE_CACHE_CONTROL);
|
|
188
205
|
headers.set("Vary", "Accept");
|
|
189
206
|
setImageSecurityHeaders(headers, config);
|
|
207
|
+
setCacheStateHeaders(headers, IMAGE_RESPONSE_CACHE_STATE);
|
|
208
|
+
}
|
|
209
|
+
function createPassthroughImageResponse(source, config) {
|
|
210
|
+
const headers = new Headers(source.headers);
|
|
211
|
+
setImageResponseHeaders(headers, config);
|
|
190
212
|
return new Response(source.body, {
|
|
191
213
|
status: 200,
|
|
192
214
|
headers
|
|
@@ -216,9 +238,7 @@ async function handleImageOptimization(request, handlers, allowedWidths, imageCo
|
|
|
216
238
|
quality
|
|
217
239
|
});
|
|
218
240
|
const headers = new Headers(transformed.headers);
|
|
219
|
-
headers
|
|
220
|
-
headers.set("Vary", "Accept");
|
|
221
|
-
setImageSecurityHeaders(headers, imageConfig);
|
|
241
|
+
setImageResponseHeaders(headers, imageConfig);
|
|
222
242
|
if (!isSafeImageContentType(headers.get("Content-Type"), imageConfig?.dangerouslyAllowSVG)) headers.set("Content-Type", format);
|
|
223
243
|
return new Response(transformed.body, {
|
|
224
244
|
status: 200,
|
|
@@ -287,4 +307,4 @@ function handleConfiguredImageOptimization(request, fetchAsset, allowedWidths, i
|
|
|
287
307
|
}, allowedWidths, imageConfig);
|
|
288
308
|
}
|
|
289
309
|
//#endregion
|
|
290
|
-
export { DEFAULT_DEVICE_SIZES, DEFAULT_IMAGE_SIZES, IMAGE_CACHE_CONTROL, IMAGE_CONTENT_SECURITY_POLICY, IMAGE_OPTIMIZATION_PATH, VINEXT_IMAGE_OPTIMIZATION_PATH, getImageOptimizer, handleConfiguredImageOptimization, handleImageOptimization, isImageOptimizationPath, isSafeImageContentType, negotiateImageFormat, parseImageParams, resolveDevImageRedirect, setImageOptimizer };
|
|
310
|
+
export { DEFAULT_DEVICE_SIZES, DEFAULT_IMAGE_SIZES, IMAGE_CACHE_CONTROL, IMAGE_CONTENT_SECURITY_POLICY, IMAGE_OPTIMIZATION_PATH, IMAGE_RESPONSE_CACHE_STATE, VINEXT_IMAGE_OPTIMIZATION_PATH, getImageOptimizer, handleConfiguredImageOptimization, handleImageOptimization, isImageOptimizationPath, isSafeImageContentType, negotiateImageFormat, parseImageParams, resolveDevImageRedirect, setImageOptimizer };
|
package/dist/server/isr-cache.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { PRERENDER_REVALIDATE_HEADER, PRERENDER_REVALIDATE_ONLY_GENERATED_HEADER } from "../utils/protocol-headers.js";
|
|
2
2
|
import { normalizePathnameForRouteMatch } from "../routing/utils.js";
|
|
3
3
|
import { getRequestExecutionContext } from "../shims/request-context.js";
|
|
4
|
+
import { isInterceptionMatchedUrlPath } from "./normalize-path.js";
|
|
4
5
|
import { reportRequestError } from "./instrumentation.js";
|
|
5
6
|
import { getCdnCacheAdapter } from "../shims/cdn-cache.js";
|
|
6
7
|
import { fnv1a64 } from "../utils/hash.js";
|
|
7
8
|
import { normalizeMountedSlotsHeader } from "./app-mounted-slots-header.js";
|
|
8
9
|
import { APP_RSC_RENDER_MODE_NAVIGATION, getRscRenderModeCacheVariant } from "./app-rsc-render-mode.js";
|
|
9
|
-
import { normalizeAppPageInterceptionProofPathname } from "./app-page-render-identity.js";
|
|
10
10
|
import { getRevalidateSecret, isOnDemandRevalidateRequest, isRevalidateSecret } from "./revalidation-request.js";
|
|
11
11
|
//#region src/server/isr-cache.ts
|
|
12
12
|
/**
|
|
@@ -183,7 +183,7 @@ function appIsrHtmlKey(pathname) {
|
|
|
183
183
|
return appIsrCacheKey(pathname, "html");
|
|
184
184
|
}
|
|
185
185
|
function normalizeInterceptionContextForCacheKey(interceptionContext) {
|
|
186
|
-
return
|
|
186
|
+
return isInterceptionMatchedUrlPath(interceptionContext) ? interceptionContext : null;
|
|
187
187
|
}
|
|
188
188
|
/**
|
|
189
189
|
* Build the ISR cache key for an RSC payload.
|