vinext 1.0.0-beta.7 → 1.0.0-beta.9
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 +43 -0
- package/dist/build/prerender-paths.d.ts +50 -1
- package/dist/build/prerender-paths.js +627 -88
- package/dist/build/prerender.d.ts +4 -2
- package/dist/build/prerender.js +40 -23
- package/dist/build/report.d.ts +16 -1
- package/dist/build/report.js +64 -3
- package/dist/build/static-export.d.ts +1 -1
- package/dist/cache/cache-adapters-virtual.d.ts +47 -1
- package/dist/cache/cache-adapters-virtual.js +10 -1
- package/dist/cli.js +7 -0
- package/dist/client/vinext-next-data.d.ts +2 -0
- package/dist/config/config-matchers.d.ts +3 -3
- package/dist/config/config-matchers.js +8 -3
- package/dist/config/prerender.d.ts +2 -2
- package/dist/config/prerender.js +2 -2
- package/dist/entries/app-browser-entry.d.ts +1 -1
- package/dist/entries/app-browser-entry.js +4 -1
- package/dist/entries/app-rsc-entry.d.ts +2 -0
- package/dist/entries/app-rsc-entry.js +28 -4
- package/dist/entries/app-rsc-manifest.js +1 -0
- package/dist/entries/pages-server-entry.d.ts +1 -1
- package/dist/entries/pages-server-entry.js +4 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.js +146 -28
- package/dist/init-cloudflare.d.ts +5 -3
- package/dist/init-cloudflare.js +58 -25
- package/dist/plugins/import-meta-url.js +7 -6
- package/dist/routing/app-router.d.ts +3 -1
- package/dist/routing/app-router.js +5 -1
- package/dist/server/app-browser-entry.js +41 -13
- package/dist/server/app-browser-navigation-controller.js +1 -1
- package/dist/server/app-browser-state.js +1 -1
- package/dist/server/app-browser-visible-commit.js +1 -1
- package/dist/server/app-middleware.d.ts +8 -0
- package/dist/server/app-middleware.js +28 -2
- package/dist/server/app-optimistic-routing.d.ts +6 -1
- package/dist/server/app-optimistic-routing.js +13 -1
- package/dist/server/app-page-boundary.js +2 -1
- package/dist/server/app-page-cache-finalizer.d.ts +16 -1
- package/dist/server/app-page-cache-finalizer.js +73 -4
- package/dist/server/app-page-cache.js +2 -2
- package/dist/server/app-page-dispatch.d.ts +2 -0
- package/dist/server/app-page-dispatch.js +12 -4
- package/dist/server/app-page-execution.js +1 -1
- package/dist/server/app-page-render.d.ts +1 -0
- package/dist/server/app-page-render.js +27 -9
- package/dist/server/app-page-response.js +2 -2
- package/dist/server/app-page-stream.js +2 -1
- package/dist/server/app-pages-bridge.js +2 -0
- package/dist/server/app-prerender-endpoints.js +23 -11
- package/dist/server/app-prerender-static-params.js +12 -5
- package/dist/server/app-route-handler-dispatch.js +13 -9
- package/dist/server/app-route-handler-execution.d.ts +1 -0
- package/dist/server/app-route-handler-execution.js +127 -14
- package/dist/server/app-route-handler-policy.d.ts +15 -3
- package/dist/server/app-route-handler-policy.js +19 -4
- package/dist/server/app-route-tree-prefetch.js +2 -2
- package/dist/server/app-router-entry.js +52 -8
- package/dist/server/app-rsc-cache-busting.d.ts +22 -4
- package/dist/server/app-rsc-cache-busting.js +68 -18
- package/dist/server/app-rsc-handler.d.ts +2 -0
- package/dist/server/app-rsc-handler.js +95 -30
- package/dist/server/app-rsc-response-finalizer.js +24 -7
- package/dist/server/app-rsc-route-matching.d.ts +1 -0
- package/dist/server/app-rsc-route-matching.js +1 -0
- package/dist/server/app-segment-config.d.ts +3 -1
- package/dist/server/app-segment-config.js +23 -11
- package/dist/server/app-server-action-execution.js +4 -4
- package/dist/server/app-ssr-entry.js +4 -4
- package/dist/server/cache-control.d.ts +8 -2
- package/dist/server/cache-control.js +35 -4
- package/dist/server/cacheability-limits.d.ts +9 -0
- package/dist/server/cacheability-limits.js +9 -0
- package/dist/server/cacheability-manifest.d.ts +42 -0
- package/dist/server/cacheability-manifest.js +191 -0
- package/dist/server/cacheability-request.d.ts +20 -0
- package/dist/server/cacheability-request.js +457 -0
- package/dist/server/config-headers.js +23 -3
- package/dist/server/headers.d.ts +16 -3
- package/dist/server/headers.js +29 -3
- package/dist/server/middleware-matcher.d.ts +9 -1
- package/dist/server/middleware-matcher.js +22 -1
- package/dist/server/middleware-runtime.d.ts +6 -0
- package/dist/server/middleware-runtime.js +18 -3
- package/dist/server/normalize-path.js +1 -1
- package/dist/server/pages-api-route.js +1 -1
- package/dist/server/pages-i18n.js +3 -2
- package/dist/server/pages-page-handler.js +22 -9
- package/dist/server/pages-request-pipeline.d.ts +2 -0
- package/dist/server/pages-request-pipeline.js +17 -6
- package/dist/server/pages-router-entry.js +56 -9
- package/dist/server/prerender-route-params.js +1 -0
- package/dist/server/prerender-work-unit-setup.d.ts +3 -2
- package/dist/server/prerender-work-unit-setup.js +34 -11
- package/dist/server/prod-server.js +5 -5
- package/dist/server/request-pipeline.d.ts +1 -5
- package/dist/server/request-pipeline.js +2 -9
- package/dist/server/revalidation-host.js +1 -0
- package/dist/server/server-globals.js +13 -0
- package/dist/server/static-file-signal.d.ts +13 -0
- package/dist/server/static-file-signal.js +33 -0
- package/dist/server/worker-prerender-discovery.d.ts +19 -0
- package/dist/server/worker-prerender-discovery.js +55 -0
- package/dist/server/worker-utils.js +2 -3
- package/dist/shims/cache-runtime.js +140 -102
- package/dist/shims/cache.js +11 -1
- package/dist/shims/cacheability-classification.d.ts +85 -0
- package/dist/shims/cacheability-classification.js +146 -0
- package/dist/shims/cdn-cache.d.ts +30 -1
- package/dist/shims/cdn-cache.js +25 -1
- package/dist/shims/error.js +1 -1
- package/dist/shims/fetch-cache.js +8 -3
- package/dist/shims/headers.d.ts +3 -1
- package/dist/shims/headers.js +9 -5
- package/dist/shims/internal/app-prefetch-rsc-request.d.ts +25 -0
- package/dist/shims/internal/app-prefetch-rsc-request.js +21 -0
- package/dist/shims/internal/app-route-prefetch-policy.d.ts +2 -0
- package/dist/shims/internal/app-route-prefetch-policy.js +2 -0
- package/dist/shims/internal/hybrid-client-route-owner-direct.d.ts +2 -2
- package/dist/shims/internal/hybrid-client-route-owner-direct.js +29 -11
- package/dist/shims/internal/hybrid-client-route-owner.js +2 -2
- package/dist/shims/internal/make-hanging-promise.d.ts +7 -1
- package/dist/shims/internal/make-hanging-promise.js +13 -2
- package/dist/shims/internal/work-unit-async-storage.d.ts +2 -0
- package/dist/shims/link.js +25 -14
- package/dist/shims/navigation.js +31 -11
- package/dist/shims/navigation.react-server.js +1 -1
- package/dist/shims/request-context.d.ts +2 -0
- package/dist/shims/server.js +1 -1
- package/dist/shims/url-utils.d.ts +8 -1
- package/dist/shims/url-utils.js +10 -1
- package/dist/utils/prerender-output-paths.d.ts +7 -7
- package/dist/utils/prerender-output-paths.js +16 -9
- package/dist/utils/react-compiler-support.d.ts +20 -0
- package/dist/utils/react-compiler-support.js +29 -0
- package/package.json +10 -2
|
@@ -1,15 +1,77 @@
|
|
|
1
|
-
import { NEVER_CACHE_CONTROL, applyCdnResponseHeaders } from "./cache-control.js";
|
|
1
|
+
import { NEVER_CACHE_CONTROL, applyCdnResponseHeaders, hasExplicitNonCacheableResponsePolicy } from "./cache-control.js";
|
|
2
2
|
import { isrCacheControl } from "./isr-cache.js";
|
|
3
3
|
import { isDraftModeRequest, setHeadersContext } from "../shims/headers.js";
|
|
4
4
|
import { _drainPendingRevalidations } from "../shims/cache-request-state.js";
|
|
5
5
|
import { copyLinkHeaderProvenance } from "./app-response-header-provenance.js";
|
|
6
|
+
import { CACHEABILITY_POLICY_HEADERS, getRouteCacheabilityCaptureOptions, getRouteCacheabilityDynamicReason, isRouteCacheabilityEvaluation, markRouteCacheabilityExplicitResponsePolicy, markRouteCacheabilityResponseBodyComplete } from "../shims/cacheability-classification.js";
|
|
6
7
|
import { applyRouteHandlerMiddlewareContext, applyRouteHandlerRevalidateHeader, assertSupportedAppRouteHandlerResponse, buildAppRouteCacheValue, finalizeRouteHandlerResponse, markRouteHandlerCacheMiss } from "./app-route-handler-response.js";
|
|
7
|
-
import { runWithRootParamsUsage } from "../shims/root-params.js";
|
|
8
8
|
import { isPossibleAppRouteActionRequest } from "./app-action-request.js";
|
|
9
|
+
import { runWithRootParamsUsage } from "../shims/root-params.js";
|
|
9
10
|
import { createTrackedAppRouteRequest, markKnownDynamicAppRoute } from "./app-route-handler-runtime.js";
|
|
10
11
|
import { createStaticGenerationHeadersContext, getAppRouteStaticGenerationErrorMessage } from "./app-static-generation.js";
|
|
11
|
-
import { resolveAppRouteHandlerSpecialError, shouldApplyAppRouteHandlerRevalidateHeader, shouldWriteAppRouteHandlerCache } from "./app-route-handler-policy.js";
|
|
12
|
+
import { resolveAppRouteHandlerSpecialError, shouldApplyAppRouteHandlerRevalidateHeader, shouldCompleteAppRouteHandlerResponse, shouldWriteAppRouteHandlerCache } from "./app-route-handler-policy.js";
|
|
13
|
+
import { CACHEABILITY_ADMISSION_RESPONSE_BODY_LIMIT } from "./cacheability-limits.js";
|
|
12
14
|
//#region src/server/app-route-handler-execution.ts
|
|
15
|
+
function hasExplicitCacheableResponsePolicy(headers) {
|
|
16
|
+
return !hasExplicitNonCacheableResponsePolicy(headers) && CACHEABILITY_POLICY_HEADERS.some((name) => headers.has(name));
|
|
17
|
+
}
|
|
18
|
+
async function completeAppRouteHandlerResponse(response) {
|
|
19
|
+
const { captureCacheabilityAdmissionBody } = await import("./cacheability-request.js");
|
|
20
|
+
const captureOptions = getRouteCacheabilityCaptureOptions();
|
|
21
|
+
const captured = await captureCacheabilityAdmissionBody(response.body, captureOptions?.captureDeadlineAt ?? Date.now() + 2e4, CACHEABILITY_ADMISSION_RESPONSE_BODY_LIMIT, captureOptions?.captureBudget);
|
|
22
|
+
const completed = new Response(captured.body, {
|
|
23
|
+
headers: response.headers,
|
|
24
|
+
status: response.status,
|
|
25
|
+
statusText: response.statusText
|
|
26
|
+
});
|
|
27
|
+
copyLinkHeaderProvenance(response.headers, completed.headers);
|
|
28
|
+
return {
|
|
29
|
+
completed: captured.kind === "captured",
|
|
30
|
+
response: completed
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
function deferAppRouteHandlerCleanup(response, cleanup) {
|
|
34
|
+
if (!response.body) {
|
|
35
|
+
cleanup();
|
|
36
|
+
return response;
|
|
37
|
+
}
|
|
38
|
+
const reader = response.body.getReader();
|
|
39
|
+
let cleaned = false;
|
|
40
|
+
const cleanOnce = async () => {
|
|
41
|
+
if (cleaned) return;
|
|
42
|
+
cleaned = true;
|
|
43
|
+
reader.releaseLock();
|
|
44
|
+
await cleanup();
|
|
45
|
+
};
|
|
46
|
+
const body = new ReadableStream({
|
|
47
|
+
async pull(controller) {
|
|
48
|
+
try {
|
|
49
|
+
const result = await reader.read();
|
|
50
|
+
if (result.done) {
|
|
51
|
+
await cleanOnce();
|
|
52
|
+
controller.close();
|
|
53
|
+
} else controller.enqueue(result.value);
|
|
54
|
+
} catch (error) {
|
|
55
|
+
await cleanOnce();
|
|
56
|
+
controller.error(error);
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
async cancel(reason) {
|
|
60
|
+
try {
|
|
61
|
+
await reader.cancel(reason);
|
|
62
|
+
} finally {
|
|
63
|
+
await cleanOnce();
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}, { highWaterMark: 0 });
|
|
67
|
+
const deferred = new Response(body, {
|
|
68
|
+
headers: response.headers,
|
|
69
|
+
status: response.status,
|
|
70
|
+
statusText: response.statusText
|
|
71
|
+
});
|
|
72
|
+
copyLinkHeaderProvenance(response.headers, deferred.headers);
|
|
73
|
+
return deferred;
|
|
74
|
+
}
|
|
13
75
|
function applyDraftModeCachePolicy(response, isDraftMode) {
|
|
14
76
|
if (!isDraftMode) return response;
|
|
15
77
|
const headers = new Headers(response.headers);
|
|
@@ -54,13 +116,16 @@ async function runAppRouteHandler(options) {
|
|
|
54
116
|
kind: "route-handler",
|
|
55
117
|
routePattern: options.routePattern ?? new URL(options.request.url).pathname
|
|
56
118
|
}, () => options.handlerFn(trackedRequest.request, { params: options.params }));
|
|
119
|
+
const dynamicUsedInContext = options.consumeDynamicUsage();
|
|
57
120
|
return {
|
|
58
|
-
|
|
121
|
+
didAccessDynamicRequest: () => trackedRequest.didAccessDynamicRequest(),
|
|
122
|
+
dynamicUsedInHandler: trackedRequest.didAccessDynamicRequest() || dynamicUsedInContext,
|
|
59
123
|
response
|
|
60
124
|
};
|
|
61
125
|
}
|
|
62
126
|
async function executeAppRouteHandler(options) {
|
|
63
127
|
const previousHeadersPhase = options.setHeadersAccessPhase("route-handler");
|
|
128
|
+
let cleanupDeferredToBody = false;
|
|
64
129
|
const middlewareMergeOptions = { appendResponseLink: options.handler.runtime === "edge" || options.handler.runtime === "experimental-edge" };
|
|
65
130
|
try {
|
|
66
131
|
let handlerResult;
|
|
@@ -72,19 +137,44 @@ async function executeAppRouteHandler(options) {
|
|
|
72
137
|
} finally {
|
|
73
138
|
await _drainPendingRevalidations();
|
|
74
139
|
}
|
|
75
|
-
|
|
140
|
+
let { dynamicUsedInHandler, response } = handlerResult;
|
|
76
141
|
assertSupportedAppRouteHandlerResponse(response);
|
|
77
|
-
const
|
|
142
|
+
const handlerSetCachePolicy = CACHEABILITY_POLICY_HEADERS.some((name) => response.headers.has(name));
|
|
143
|
+
const hasExplicitCacheablePolicy = hasExplicitCacheableResponsePolicy(response.headers);
|
|
144
|
+
if (hasExplicitCacheablePolicy) markRouteCacheabilityExplicitResponsePolicy();
|
|
145
|
+
const draftModeBeforeCompletion = options.getActiveDraftModeState?.() ?? options.isDraftMode === true;
|
|
146
|
+
const handlerDraftCookieBeforeCompletion = options.getDraftModeCookieHeader() ?? options.initialDraftModeCookie;
|
|
147
|
+
if (shouldCompleteAppRouteHandlerResponse({
|
|
148
|
+
dynamicConfig: options.handler.dynamic,
|
|
149
|
+
dynamicUsedInHandler,
|
|
150
|
+
hasExplicitCacheablePolicy,
|
|
151
|
+
handlerSetCachePolicy,
|
|
152
|
+
isAutoHead: options.isAutoHead,
|
|
153
|
+
isDraftMode: draftModeBeforeCompletion || handlerDraftCookieBeforeCompletion != null,
|
|
154
|
+
isProduction: options.isProduction,
|
|
155
|
+
method: options.method,
|
|
156
|
+
revalidateSeconds: options.revalidateSeconds,
|
|
157
|
+
requiresCompletedResponseAdmission: isRouteCacheabilityEvaluation()
|
|
158
|
+
})) {
|
|
159
|
+
const completed = await completeAppRouteHandlerResponse(response);
|
|
160
|
+
response = completed.response;
|
|
161
|
+
cleanupDeferredToBody = !completed.completed;
|
|
162
|
+
if (completed.completed) markRouteCacheabilityResponseBodyComplete();
|
|
163
|
+
const dynamicUsedDuringCompletion = options.consumeDynamicUsage();
|
|
164
|
+
dynamicUsedInHandler = handlerResult.didAccessDynamicRequest() || dynamicUsedDuringCompletion || dynamicUsedInHandler;
|
|
165
|
+
}
|
|
166
|
+
const requestCacheabilityVeto = getRouteCacheabilityDynamicReason();
|
|
167
|
+
const responseMustStayPrivate = Boolean(options.handler.dynamic === "force-dynamic" || dynamicUsedInHandler || requestCacheabilityVeto || cleanupDeferredToBody);
|
|
78
168
|
if (dynamicUsedInHandler) markKnownDynamicAppRoute(options.routePattern);
|
|
79
169
|
const pendingCookies = options.getAndClearPendingCookies();
|
|
80
|
-
const handlerDraftCookie = options.getDraftModeCookieHeader();
|
|
170
|
+
const handlerDraftCookie = options.getDraftModeCookieHeader() ?? handlerDraftCookieBeforeCompletion;
|
|
81
171
|
const draftCookie = handlerDraftCookie ?? options.initialDraftModeCookie;
|
|
82
172
|
const shouldApplyDraftPolicy = (options.getActiveDraftModeState?.() ?? options.isDraftMode === true) || draftCookie != null;
|
|
83
173
|
if (handlerDraftCookie != null) markKnownDynamicAppRoute(options.routePattern);
|
|
84
174
|
const routeTags = options.buildPageCacheTags(options.cleanPathname, options.getCollectedFetchTags());
|
|
85
175
|
if (shouldApplyAppRouteHandlerRevalidateHeader({
|
|
86
|
-
dynamicUsedInHandler,
|
|
87
|
-
|
|
176
|
+
dynamicUsedInHandler: responseMustStayPrivate,
|
|
177
|
+
handlerSetCachePolicy,
|
|
88
178
|
isAutoHead: options.isAutoHead,
|
|
89
179
|
isDraftMode: shouldApplyDraftPolicy,
|
|
90
180
|
method: options.method,
|
|
@@ -96,8 +186,8 @@ async function executeAppRouteHandler(options) {
|
|
|
96
186
|
}
|
|
97
187
|
if (shouldWriteAppRouteHandlerCache({
|
|
98
188
|
dynamicConfig: options.handler.dynamic,
|
|
99
|
-
dynamicUsedInHandler,
|
|
100
|
-
|
|
189
|
+
dynamicUsedInHandler: responseMustStayPrivate,
|
|
190
|
+
handlerSetCachePolicy,
|
|
101
191
|
isAutoHead: options.isAutoHead,
|
|
102
192
|
isDraftMode: shouldApplyDraftPolicy,
|
|
103
193
|
isProduction: options.isProduction,
|
|
@@ -123,12 +213,35 @@ async function executeAppRouteHandler(options) {
|
|
|
123
213
|
})();
|
|
124
214
|
options.executionContext?.waitUntil(routeWritePromise);
|
|
125
215
|
}
|
|
126
|
-
|
|
127
|
-
return applyDraftModeCachePolicy(applyRouteHandlerMiddlewareContext(finalizeRouteHandlerResponse(response, {
|
|
216
|
+
let finalized = applyDraftModeCachePolicy(applyRouteHandlerMiddlewareContext(finalizeRouteHandlerResponse(response, {
|
|
128
217
|
pendingCookies,
|
|
129
218
|
draftCookie,
|
|
130
219
|
isHead: options.isAutoHead
|
|
131
220
|
}), options.middlewareContext, middlewareMergeOptions), shouldApplyDraftPolicy);
|
|
221
|
+
const preserveHandlerPolicy = isRouteCacheabilityEvaluation() ? hasExplicitCacheablePolicy : handlerSetCachePolicy;
|
|
222
|
+
if (responseMustStayPrivate && !preserveHandlerPolicy) {
|
|
223
|
+
const headers = new Headers(finalized.headers);
|
|
224
|
+
applyCdnResponseHeaders(headers, { cacheControl: NEVER_CACHE_CONTROL });
|
|
225
|
+
finalized = new Response(finalized.body, {
|
|
226
|
+
headers,
|
|
227
|
+
status: finalized.status,
|
|
228
|
+
statusText: finalized.statusText
|
|
229
|
+
});
|
|
230
|
+
copyLinkHeaderProvenance(response.headers, finalized.headers);
|
|
231
|
+
}
|
|
232
|
+
if (!cleanupDeferredToBody) {
|
|
233
|
+
options.clearRequestContext();
|
|
234
|
+
return finalized;
|
|
235
|
+
}
|
|
236
|
+
return deferAppRouteHandlerCleanup(finalized, async () => {
|
|
237
|
+
try {
|
|
238
|
+
await _drainPendingRevalidations();
|
|
239
|
+
options.consumeDynamicUsage();
|
|
240
|
+
} finally {
|
|
241
|
+
options.clearRequestContext();
|
|
242
|
+
options.setHeadersAccessPhase(previousHeadersPhase);
|
|
243
|
+
}
|
|
244
|
+
});
|
|
132
245
|
} catch (error) {
|
|
133
246
|
const pendingCookies = options.getAndClearPendingCookies();
|
|
134
247
|
const handlerDraftCookie = options.getDraftModeCookieHeader();
|
|
@@ -160,7 +273,7 @@ async function executeAppRouteHandler(options) {
|
|
|
160
273
|
});
|
|
161
274
|
return applyDraftModeCachePolicy(applyRouteHandlerMiddlewareContext(new Response(null, { status: 500 }), options.middlewareContext, middlewareMergeOptions), shouldApplyDraftPolicy);
|
|
162
275
|
} finally {
|
|
163
|
-
options.setHeadersAccessPhase(previousHeadersPhase);
|
|
276
|
+
if (!cleanupDeferredToBody) options.setHeadersAccessPhase(previousHeadersPhase);
|
|
164
277
|
}
|
|
165
278
|
}
|
|
166
279
|
//#endregion
|
|
@@ -4,6 +4,7 @@ import { RouteHandlerHttpMethod, RouteHandlerModule } from "./app-route-handler-
|
|
|
4
4
|
type AppRouteHandlerModule = {
|
|
5
5
|
dynamic?: string;
|
|
6
6
|
fetchCache?: unknown;
|
|
7
|
+
generateStaticParams?: unknown;
|
|
7
8
|
revalidate?: unknown;
|
|
8
9
|
runtime?: string;
|
|
9
10
|
} & RouteHandlerModule;
|
|
@@ -28,12 +29,14 @@ type AppRouteHandlerCacheReadOptions = {
|
|
|
28
29
|
type AppRouteHandlerResponseCacheOptions = {
|
|
29
30
|
dynamicConfig?: string;
|
|
30
31
|
dynamicUsedInHandler: boolean;
|
|
31
|
-
|
|
32
|
+
hasExplicitCacheablePolicy?: boolean;
|
|
33
|
+
handlerSetCachePolicy: boolean;
|
|
32
34
|
isAutoHead: boolean;
|
|
33
35
|
isDraftMode?: boolean;
|
|
34
36
|
isProduction: boolean;
|
|
35
37
|
method: string;
|
|
36
38
|
revalidateSeconds: number | null;
|
|
39
|
+
requiresCompletedResponseAdmission?: boolean;
|
|
37
40
|
};
|
|
38
41
|
type AppRouteHandlerSpecialError = {
|
|
39
42
|
kind: "redirect";
|
|
@@ -46,12 +49,21 @@ type AppRouteHandlerSpecialError = {
|
|
|
46
49
|
type AppRouteHandlerSpecialErrorOptions = {
|
|
47
50
|
isAction: boolean;
|
|
48
51
|
};
|
|
49
|
-
declare function getAppRouteHandlerRevalidateSeconds(handler: Pick<AppRouteHandlerModule, "revalidate">): number | null;
|
|
52
|
+
declare function getAppRouteHandlerRevalidateSeconds(handler: Pick<AppRouteHandlerModule, "dynamic" | "generateStaticParams" | "revalidate">): number | null;
|
|
50
53
|
declare function hasAppRouteHandlerDefaultExport(handler: RouteHandlerModule): boolean;
|
|
54
|
+
/** Match Next.js methods that force an App Route out of static generation. */
|
|
55
|
+
declare function hasNonStaticAppRouteHandlerMethods(handler: RouteHandlerModule): boolean;
|
|
51
56
|
declare function resolveAppRouteHandlerMethod(handler: AppRouteHandlerModule, method: string): ResolvedAppRouteHandlerMethod;
|
|
52
57
|
declare function shouldReadAppRouteHandlerCache(options: AppRouteHandlerCacheReadOptions): boolean;
|
|
53
58
|
declare function shouldApplyAppRouteHandlerRevalidateHeader(options: Omit<AppRouteHandlerResponseCacheOptions, "dynamicConfig" | "isProduction">): boolean;
|
|
59
|
+
/**
|
|
60
|
+
* Next.js consumes the full body before completing static generation for an
|
|
61
|
+
* App Route. Keep that completion boundary for every response that can still
|
|
62
|
+
* receive a public framework cache policy, including
|
|
63
|
+
* `revalidate = false` (represented by Infinity).
|
|
64
|
+
*/
|
|
65
|
+
declare function shouldCompleteAppRouteHandlerResponse(options: AppRouteHandlerResponseCacheOptions): boolean;
|
|
54
66
|
declare function shouldWriteAppRouteHandlerCache(options: AppRouteHandlerResponseCacheOptions): boolean;
|
|
55
67
|
declare function resolveAppRouteHandlerSpecialError(error: unknown, requestUrl: string, options?: AppRouteHandlerSpecialErrorOptions): AppRouteHandlerSpecialError | null;
|
|
56
68
|
//#endregion
|
|
57
|
-
export { AppRouteHandlerModule, getAppRouteHandlerRevalidateSeconds, hasAppRouteHandlerDefaultExport, isPossibleAppRouteActionRequest, resolveAppRouteHandlerMethod, resolveAppRouteHandlerSpecialError, shouldApplyAppRouteHandlerRevalidateHeader, shouldReadAppRouteHandlerCache, shouldWriteAppRouteHandlerCache };
|
|
69
|
+
export { AppRouteHandlerModule, getAppRouteHandlerRevalidateSeconds, hasAppRouteHandlerDefaultExport, hasNonStaticAppRouteHandlerMethods, isPossibleAppRouteActionRequest, resolveAppRouteHandlerMethod, resolveAppRouteHandlerSpecialError, shouldApplyAppRouteHandlerRevalidateHeader, shouldCompleteAppRouteHandlerResponse, shouldReadAppRouteHandlerCache, shouldWriteAppRouteHandlerCache };
|
|
@@ -5,12 +5,18 @@ import { buildRouteHandlerAllowHeader, collectRouteHandlerMethods } from "./app-
|
|
|
5
5
|
function getAppRouteHandlerRevalidateSeconds(handler) {
|
|
6
6
|
const { revalidate } = handler;
|
|
7
7
|
if (revalidate === false) return Infinity;
|
|
8
|
-
if (typeof revalidate
|
|
9
|
-
return
|
|
8
|
+
if (typeof revalidate === "number" && Number.isFinite(revalidate) && revalidate >= 0) return revalidate;
|
|
9
|
+
if (revalidate !== void 0) return null;
|
|
10
|
+
if (handler.dynamic === "force-static" || handler.dynamic === "error" || typeof handler.generateStaticParams === "function") return Infinity;
|
|
11
|
+
return null;
|
|
10
12
|
}
|
|
11
13
|
function hasAppRouteHandlerDefaultExport(handler) {
|
|
12
14
|
return typeof handler.default === "function";
|
|
13
15
|
}
|
|
16
|
+
/** Match Next.js methods that force an App Route out of static generation. */
|
|
17
|
+
function hasNonStaticAppRouteHandlerMethods(handler) {
|
|
18
|
+
return Boolean(handler.POST || handler.PUT || handler.DELETE || handler.PATCH || handler.OPTIONS);
|
|
19
|
+
}
|
|
14
20
|
function resolveAppRouteHandlerMethod(handler, method) {
|
|
15
21
|
const exportedMethods = collectRouteHandlerMethods(handler);
|
|
16
22
|
const allowHeaderForOptions = buildRouteHandlerAllowHeader(exportedMethods);
|
|
@@ -33,7 +39,16 @@ function shouldReadAppRouteHandlerCache(options) {
|
|
|
33
39
|
return options.isProduction && options.revalidateSeconds !== null && options.revalidateSeconds > 0 && options.revalidateSeconds !== Infinity && options.dynamicConfig !== "force-dynamic" && !options.isDraftMode && !options.isKnownDynamic && (options.method === "GET" || options.isAutoHead) && typeof options.handlerFn === "function";
|
|
34
40
|
}
|
|
35
41
|
function shouldApplyAppRouteHandlerRevalidateHeader(options) {
|
|
36
|
-
return options.revalidateSeconds !== null && !options.isDraftMode && !options.dynamicUsedInHandler && (options.method === "GET" || options.isAutoHead) && !options.
|
|
42
|
+
return options.revalidateSeconds !== null && !options.isDraftMode && !options.dynamicUsedInHandler && (options.method === "GET" || options.isAutoHead) && !options.handlerSetCachePolicy;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Next.js consumes the full body before completing static generation for an
|
|
46
|
+
* App Route. Keep that completion boundary for every response that can still
|
|
47
|
+
* receive a public framework cache policy, including
|
|
48
|
+
* `revalidate = false` (represented by Infinity).
|
|
49
|
+
*/
|
|
50
|
+
function shouldCompleteAppRouteHandlerResponse(options) {
|
|
51
|
+
return options.isProduction && (options.revalidateSeconds !== null && options.revalidateSeconds > 0 || options.requiresCompletedResponseAdmission === true || options.hasExplicitCacheablePolicy === true) && options.dynamicConfig !== "force-dynamic" && !options.isDraftMode && !options.dynamicUsedInHandler && (options.method === "GET" || options.isAutoHead);
|
|
37
52
|
}
|
|
38
53
|
function shouldWriteAppRouteHandlerCache(options) {
|
|
39
54
|
return options.isProduction && options.revalidateSeconds !== null && options.revalidateSeconds > 0 && options.revalidateSeconds !== Infinity && options.dynamicConfig !== "force-dynamic" && !options.isDraftMode && shouldApplyAppRouteHandlerRevalidateHeader(options);
|
|
@@ -55,4 +70,4 @@ function resolveAppRouteHandlerSpecialError(error, requestUrl, options) {
|
|
|
55
70
|
return null;
|
|
56
71
|
}
|
|
57
72
|
//#endregion
|
|
58
|
-
export { getAppRouteHandlerRevalidateSeconds, hasAppRouteHandlerDefaultExport, isPossibleAppRouteActionRequest, resolveAppRouteHandlerMethod, resolveAppRouteHandlerSpecialError, shouldApplyAppRouteHandlerRevalidateHeader, shouldReadAppRouteHandlerCache, shouldWriteAppRouteHandlerCache };
|
|
73
|
+
export { getAppRouteHandlerRevalidateSeconds, hasAppRouteHandlerDefaultExport, hasNonStaticAppRouteHandlerMethods, isPossibleAppRouteActionRequest, resolveAppRouteHandlerMethod, resolveAppRouteHandlerSpecialError, shouldApplyAppRouteHandlerRevalidateHeader, shouldCompleteAppRouteHandlerResponse, shouldReadAppRouteHandlerCache, shouldWriteAppRouteHandlerCache };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { NEXTJS_DEPLOYMENT_ID_HEADER } from "./headers.js";
|
|
1
|
+
import { NEXTJS_DEPLOYMENT_ID_HEADER, VINEXT_RSC_VARY_HEADER } from "./headers.js";
|
|
2
2
|
import { getDeploymentId } from "../utils/deployment-id.js";
|
|
3
|
-
import { VINEXT_RSC_CONTENT_TYPE,
|
|
3
|
+
import { VINEXT_RSC_CONTENT_TYPE, applyRscCompatibilityIdHeader } from "./app-rsc-cache-busting.js";
|
|
4
4
|
//#region src/server/app-route-tree-prefetch.ts
|
|
5
5
|
const PARENT_INLINED_INTO_SELF = 32;
|
|
6
6
|
const INLINED_INTO_CHILD = 64;
|
|
@@ -1,27 +1,65 @@
|
|
|
1
|
-
import "./server-globals.js";
|
|
2
1
|
import { runWithExecutionContext } from "../shims/request-context.js";
|
|
3
|
-
import
|
|
2
|
+
import "./server-globals.js";
|
|
3
|
+
import { VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, VINEXT_PRERENDER_SECRET_HEADER, VINEXT_REVALIDATE_HOST_HEADER } from "../utils/protocol-headers.js";
|
|
4
|
+
import { VINEXT_CACHEABILITY_PROBE_QUERY_PARAM } from "./headers.js";
|
|
4
5
|
import { badRequestResponse, notFoundResponse, notFoundStaticAssetResponse } from "./http-error-responses.js";
|
|
5
6
|
import { isOpenRedirectShaped } from "./open-redirect.js";
|
|
6
7
|
import { cloneRequestWithHeaders, filterInternalHeaders } from "./request-pipeline.js";
|
|
8
|
+
import { getCdnCacheAdapter } from "../shims/cdn-cache.js";
|
|
9
|
+
import { applyCdnResponseIdentityHeaders, validateCdnRequest } from "./cache-control.js";
|
|
7
10
|
import { assetPrefixPathname, isNextStaticPath } from "../utils/asset-prefix.js";
|
|
8
11
|
import { getImageOptimizer, handleConfiguredImageOptimization, isImageOptimizationPath } from "./image-optimization.js";
|
|
9
12
|
import { createStaticAssetRequest, finalizeMissingStaticAssetResponse, resolveStaticAssetSignal } from "./worker-utils.js";
|
|
10
13
|
import { readTrustedPrerenderRouteParams, serializePrerenderRouteParamsHeader } from "./prerender-route-params.js";
|
|
11
14
|
import { createWorkerRevalidationContext } from "./worker-revalidation-context.js";
|
|
12
|
-
import
|
|
15
|
+
import { createWorkerPrerenderDiscoveryContext, createWorkerPrerenderReadinessResponse } from "./worker-prerender-discovery.js";
|
|
16
|
+
import rscHandler, { __assetPrefix, __basePath, __cacheabilityManifest, __imageAllowedWidths, __imageConfig, __prerenderSecret } from "virtual:vinext-rsc-entry";
|
|
13
17
|
import { registerConfiguredCacheAdapters } from "virtual:vinext-cache-adapters";
|
|
14
18
|
import { registerConfiguredImageOptimizer } from "virtual:vinext-image-adapters";
|
|
15
19
|
//#region src/server/app-router-entry.ts
|
|
16
20
|
const __workerBasePath = typeof __basePath === "string" ? __basePath : "";
|
|
17
21
|
const __workerAssetPathPrefix = assetPrefixPathname(typeof __assetPrefix === "string" ? __assetPrefix : "");
|
|
22
|
+
function isPotentialCompletedAdmissionRequest(request) {
|
|
23
|
+
if (request.method !== "GET" && request.method !== "HEAD") return false;
|
|
24
|
+
if (request.headers.has("next-action") || request.headers.has("x-rsc-action")) return false;
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
18
27
|
var app_router_entry_default = { async fetch(request, env, ctx) {
|
|
19
|
-
return handleRequest(request, env, ctx);
|
|
28
|
+
return applyCdnResponseIdentityHeaders(await handleRequest(request, env, ctx), request);
|
|
20
29
|
} };
|
|
21
30
|
async function handleRequest(request, env, platformCtx) {
|
|
22
|
-
const
|
|
31
|
+
const requestCtx = platformCtx?.trustedRevalidateOrigin ? platformCtx : createWorkerRevalidationContext(platformCtx, (internalRequest, internalCtx) => handleRequest(internalRequest, env, internalCtx));
|
|
23
32
|
registerConfiguredCacheAdapters(env);
|
|
33
|
+
const cdnCacheAdapter = getCdnCacheAdapter();
|
|
34
|
+
let ctx = createWorkerPrerenderDiscoveryContext(requestCtx, request, __prerenderSecret);
|
|
35
|
+
const readinessResponse = createWorkerPrerenderReadinessResponse(ctx, request);
|
|
36
|
+
if (readinessResponse) return await validateCdnRequest(request) ?? readinessResponse;
|
|
37
|
+
let finalizeCacheabilityResponse;
|
|
38
|
+
if (request.headers.has("X-Vinext-Cacheability-Probe")) {
|
|
39
|
+
const cacheability = await import("./cacheability-request.js");
|
|
40
|
+
const probeContext = cacheability.createWorkerCacheabilityContext(ctx, request, __prerenderSecret, cdnCacheAdapter.responseVary);
|
|
41
|
+
if (probeContext !== ctx) {
|
|
42
|
+
ctx = probeContext;
|
|
43
|
+
finalizeCacheabilityResponse = cacheability.finalizeWorkerCacheabilityResponse;
|
|
44
|
+
const probeUrl = new URL(request.url);
|
|
45
|
+
if (probeUrl.searchParams.has("__vinext_cacheability_probe")) {
|
|
46
|
+
probeUrl.searchParams.delete(VINEXT_CACHEABILITY_PROBE_QUERY_PARAM);
|
|
47
|
+
request = new Request(probeUrl, request);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
const requiresCompletedResponseAdmission = cdnCacheAdapter.requiresCompletedResponseAdmission === true;
|
|
52
|
+
if (!finalizeCacheabilityResponse && (__cacheabilityManifest || requiresCompletedResponseAdmission) && isPotentialCompletedAdmissionRequest(request)) {
|
|
53
|
+
const cacheability = await import("./cacheability-request.js");
|
|
54
|
+
const admissionContext = cacheability.createWorkerCacheabilityAdmissionContext(ctx, request, __cacheabilityManifest, process.env.__VINEXT_BUILD_ID, requiresCompletedResponseAdmission, cdnCacheAdapter.responseVary);
|
|
55
|
+
if (admissionContext !== ctx) {
|
|
56
|
+
ctx = admissionContext;
|
|
57
|
+
finalizeCacheabilityResponse = cacheability.finalizeWorkerCacheabilityResponse;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
24
60
|
registerConfiguredImageOptimizer(env);
|
|
61
|
+
const cdnValidationResponse = await validateCdnRequest(request);
|
|
62
|
+
if (cdnValidationResponse) return cdnValidationResponse;
|
|
25
63
|
const url = new URL(request.url);
|
|
26
64
|
if (isImageOptimizationPath(url.pathname) && env?.ASSETS && getImageOptimizer()) {
|
|
27
65
|
const assetFetcher = env.ASSETS;
|
|
@@ -37,6 +75,7 @@ async function handleRequest(request, env, platformCtx) {
|
|
|
37
75
|
{
|
|
38
76
|
const trustedPrerenderRouteParams = ctx.hostRuntime === "node" ? readTrustedPrerenderRouteParams(request) : null;
|
|
39
77
|
const filteredHeaders = ctx.isInternalPagesRevalidation ? new Headers(request.headers) : filterInternalHeaders(request.headers);
|
|
78
|
+
filteredHeaders.delete(VINEXT_PRERENDER_SECRET_HEADER);
|
|
40
79
|
filteredHeaders.delete(VINEXT_REVALIDATE_HOST_HEADER);
|
|
41
80
|
const prerenderRouteParamsHeader = serializePrerenderRouteParamsHeader(trustedPrerenderRouteParams);
|
|
42
81
|
if (prerenderRouteParamsHeader !== null) filteredHeaders.set(VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, prerenderRouteParamsHeader);
|
|
@@ -51,10 +90,15 @@ async function handleRequest(request, env, platformCtx) {
|
|
|
51
90
|
const assetResponse = await resolveStaticAssetSignal(response, { fetchAsset: (path) => Promise.resolve(assetFetcher.fetch(createStaticAssetRequest(path, request))) });
|
|
52
91
|
if (assetResponse) response = assetResponse;
|
|
53
92
|
}
|
|
54
|
-
|
|
93
|
+
response = finalizeMissingStaticAssetResponse(response, missingBuildAsset);
|
|
94
|
+
return finalizeCacheabilityResponse ? finalizeCacheabilityResponse(response, ctx) : response;
|
|
95
|
+
}
|
|
96
|
+
if (result === null || result === void 0) {
|
|
97
|
+
const response = missingBuildAsset ? notFoundStaticAssetResponse() : notFoundResponse();
|
|
98
|
+
return finalizeCacheabilityResponse ? finalizeCacheabilityResponse(response, ctx) : response;
|
|
55
99
|
}
|
|
56
|
-
|
|
57
|
-
return
|
|
100
|
+
const response = new Response(String(result), { status: 200 });
|
|
101
|
+
return finalizeCacheabilityResponse ? finalizeCacheabilityResponse(response, ctx) : response;
|
|
58
102
|
}
|
|
59
103
|
//#endregion
|
|
60
104
|
export { app_router_entry_default as default };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { VINEXT_RSC_RENDER_MODE_HEADER } from "./headers.js";
|
|
1
|
+
import { VINEXT_RSC_RENDER_MODE_HEADER, VINEXT_RSC_VARY_HEADER } from "./headers.js";
|
|
2
2
|
import { AppRscRenderMode } from "./app-rsc-render-mode.js";
|
|
3
3
|
import { AppRscStateFingerprintInput } from "./app-rsc-state-fingerprint.js";
|
|
4
4
|
//#region src/server/app-rsc-cache-busting.d.ts
|
|
@@ -9,9 +9,9 @@ import { AppRscStateFingerprintInput } from "./app-rsc-state-fingerprint.js";
|
|
|
9
9
|
* repeated canonicalization redirects.
|
|
10
10
|
*/
|
|
11
11
|
declare const VINEXT_RSC_CACHE_BUSTING_SEARCH_PARAM = "_rsc";
|
|
12
|
+
declare const VINEXT_RSC_BUILD_ID_HEADER = "X-Vinext-RSC-Build-Id";
|
|
12
13
|
declare const VINEXT_RSC_COMPATIBILITY_ID_HEADER = "X-Vinext-RSC-Compatibility-Id";
|
|
13
14
|
declare const VINEXT_RSC_CONTENT_TYPE = "text/x-component";
|
|
14
|
-
declare const VINEXT_RSC_VARY_HEADER: string;
|
|
15
15
|
type CreateRscRequestHeadersOptions = {
|
|
16
16
|
clientReuseManifestHeader?: string | null;
|
|
17
17
|
interceptionContext?: string | null;
|
|
@@ -26,13 +26,14 @@ type CreateRscRequestHeadersOptions = {
|
|
|
26
26
|
routeId: string;
|
|
27
27
|
} | null;
|
|
28
28
|
routerState?: AppRscStateFingerprintInput | null;
|
|
29
|
+
deploymentId?: string | null;
|
|
29
30
|
};
|
|
30
31
|
type ResolveInvalidRscCacheBustingRequestOptions = {
|
|
31
32
|
isRscRequest: boolean;
|
|
32
33
|
request: Request;
|
|
33
34
|
};
|
|
34
35
|
declare function getVinextRscCompatibilityId(): string | null;
|
|
35
|
-
declare function applyRscCompatibilityIdHeader(headers: Headers, compatibilityId?: string | null | undefined): void;
|
|
36
|
+
declare function applyRscCompatibilityIdHeader(headers: Headers, compatibilityId?: string | null | undefined, buildId?: string | null | undefined): void;
|
|
36
37
|
declare function applyRscDeploymentIdHeader(headers: Headers): void;
|
|
37
38
|
declare function isRscCompatibilityIdCompatible(responseCompatibilityId: string | null | undefined, clientCompatibilityId?: string | null | undefined): boolean;
|
|
38
39
|
type RscCompatibilityNavigationDecision = {
|
|
@@ -65,10 +66,27 @@ declare function stripRscCacheBustingSearchParam(url: URL): void;
|
|
|
65
66
|
* unchanged when the suffix is absent.
|
|
66
67
|
*/
|
|
67
68
|
declare function stripRscSuffix(pathname: string): string;
|
|
69
|
+
declare function createCanonicalRscRequestHeaders(deploymentId?: string | null | undefined): Headers;
|
|
70
|
+
/** Headers for the deterministic loading-boundary prefetch representation. */
|
|
71
|
+
declare function createCanonicalLoadingShellRscRequestHeaders(deploymentId?: string | null | undefined): Headers;
|
|
68
72
|
declare function createRscRequestHeaders(options?: CreateRscRequestHeadersOptions): Headers;
|
|
73
|
+
/**
|
|
74
|
+
* Convert a full, non-contextual navigation request to the definitive ISR RSC
|
|
75
|
+
* variant. Partial/intercepted/mounted-slot payloads remain contextual because
|
|
76
|
+
* their bytes can legitimately differ from the destination's full route.
|
|
77
|
+
*/
|
|
78
|
+
declare function canonicalizePrewarmableRscRequestHeaders(headers: Headers): boolean;
|
|
79
|
+
/**
|
|
80
|
+
* Convert an ordinary loading-boundary prefetch to the shared loading-shell
|
|
81
|
+
* variant. The payload is selected by the three retained mode headers; visible
|
|
82
|
+
* router state is transport context and must not fragment the shared response.
|
|
83
|
+
*/
|
|
84
|
+
declare function canonicalizeLoadingShellRscRequestHeaders(headers: Headers): boolean;
|
|
69
85
|
declare function createRscRequestUrl(href: string, headers: Headers): Promise<string>;
|
|
86
|
+
/** Build the definitive full-route RSC URL shared by prefetch and navigation. */
|
|
87
|
+
declare function createCanonicalRscRequestUrl(href: string): string;
|
|
70
88
|
declare function createServerActionRequestUrl(href: string): string;
|
|
71
89
|
declare function createRscRedirectLocation(location: string, request: Request): Promise<string>;
|
|
72
90
|
declare function resolveInvalidRscCacheBustingRequest(options: ResolveInvalidRscCacheBustingRequestOptions): Promise<Response | null>;
|
|
73
91
|
//#endregion
|
|
74
|
-
export { VINEXT_RSC_CACHE_BUSTING_SEARCH_PARAM, VINEXT_RSC_COMPATIBILITY_ID_HEADER, VINEXT_RSC_CONTENT_TYPE, VINEXT_RSC_RENDER_MODE_HEADER, VINEXT_RSC_VARY_HEADER, applyRscCompatibilityIdHeader, applyRscDeploymentIdHeader, computeRscCacheBustingSearchParam, createRscRedirectLocation, createRscRequestHeaders, createRscRequestUrl, createServerActionRequestUrl, getVinextRscCompatibilityId, hasRscCacheBustingSearchParam, isRscCompatibilityIdCompatible, resolveHardNavigationTargetFromRscResponse, resolveInvalidRscCacheBustingRequest, resolveRscCompatibilityNavigationDecision, setRscCacheBustingSearchParam, stripRscCacheBustingSearchParam, stripRscSuffix };
|
|
92
|
+
export { VINEXT_RSC_BUILD_ID_HEADER, VINEXT_RSC_CACHE_BUSTING_SEARCH_PARAM, VINEXT_RSC_COMPATIBILITY_ID_HEADER, VINEXT_RSC_CONTENT_TYPE, VINEXT_RSC_RENDER_MODE_HEADER, VINEXT_RSC_VARY_HEADER, applyRscCompatibilityIdHeader, applyRscDeploymentIdHeader, canonicalizeLoadingShellRscRequestHeaders, canonicalizePrewarmableRscRequestHeaders, computeRscCacheBustingSearchParam, createCanonicalLoadingShellRscRequestHeaders, createCanonicalRscRequestHeaders, createCanonicalRscRequestUrl, createRscRedirectLocation, createRscRequestHeaders, createRscRequestUrl, createServerActionRequestUrl, getVinextRscCompatibilityId, hasRscCacheBustingSearchParam, isRscCompatibilityIdCompatible, resolveHardNavigationTargetFromRscResponse, resolveInvalidRscCacheBustingRequest, resolveRscCompatibilityNavigationDecision, setRscCacheBustingSearchParam, stripRscCacheBustingSearchParam, stripRscSuffix };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { NEXTJS_DEPLOYMENT_ID_HEADER, NEXT_ROUTER_PREFETCH_HEADER, NEXT_ROUTER_SEGMENT_PREFETCH_HEADER, NEXT_ROUTER_STATE_TREE_HEADER, NEXT_URL_HEADER, VINEXT_CLIENT_REUSE_MANIFEST_HEADER, VINEXT_INTERCEPTION_CONTEXT_HEADER, VINEXT_INTERCEPTION_ID_HEADER, VINEXT_MOUNTED_SLOTS_HEADER, VINEXT_RSC_RENDER_MODE_HEADER, VINEXT_RSC_STATE_FINGERPRINT_HEADER } from "./headers.js";
|
|
1
|
+
import { NEXTJS_DEPLOYMENT_ID_HEADER, NEXT_ROUTER_PREFETCH_HEADER, NEXT_ROUTER_SEGMENT_PREFETCH_HEADER, NEXT_ROUTER_STATE_TREE_HEADER, NEXT_URL_HEADER, VINEXT_CLIENT_REUSE_MANIFEST_HEADER, VINEXT_INTERCEPTION_CONTEXT_HEADER, VINEXT_INTERCEPTION_ID_HEADER, VINEXT_MOUNTED_SLOTS_HEADER, VINEXT_RSC_RENDER_MODE_HEADER, VINEXT_RSC_STATE_FINGERPRINT_HEADER, VINEXT_RSC_VARY_HEADER } from "./headers.js";
|
|
2
2
|
import { fnv1a64 } from "../utils/hash.js";
|
|
3
|
-
import { parseAppRscRenderMode } from "./app-rsc-render-mode.js";
|
|
3
|
+
import { APP_RSC_RENDER_MODE_PREFETCH_LOADING_SHELL, parseAppRscRenderMode } from "./app-rsc-render-mode.js";
|
|
4
4
|
import { applyDeploymentIdHeader, getDeploymentId } from "../utils/deployment-id.js";
|
|
5
5
|
import { createAppRscStateFingerprint } from "./app-rsc-state-fingerprint.js";
|
|
6
6
|
//#region src/server/app-rsc-cache-busting.ts
|
|
@@ -11,20 +11,9 @@ import { createAppRscStateFingerprint } from "./app-rsc-state-fingerprint.js";
|
|
|
11
11
|
* repeated canonicalization redirects.
|
|
12
12
|
*/
|
|
13
13
|
const VINEXT_RSC_CACHE_BUSTING_SEARCH_PARAM = "_rsc";
|
|
14
|
+
const VINEXT_RSC_BUILD_ID_HEADER = "X-Vinext-RSC-Build-Id";
|
|
14
15
|
const VINEXT_RSC_COMPATIBILITY_ID_HEADER = "X-Vinext-RSC-Compatibility-Id";
|
|
15
16
|
const VINEXT_RSC_CONTENT_TYPE = "text/x-component";
|
|
16
|
-
const VINEXT_RSC_VARY_HEADER = [
|
|
17
|
-
"RSC",
|
|
18
|
-
NEXT_ROUTER_STATE_TREE_HEADER,
|
|
19
|
-
NEXT_ROUTER_PREFETCH_HEADER,
|
|
20
|
-
NEXT_ROUTER_SEGMENT_PREFETCH_HEADER,
|
|
21
|
-
NEXT_URL_HEADER,
|
|
22
|
-
VINEXT_INTERCEPTION_CONTEXT_HEADER,
|
|
23
|
-
VINEXT_INTERCEPTION_ID_HEADER,
|
|
24
|
-
VINEXT_MOUNTED_SLOTS_HEADER,
|
|
25
|
-
VINEXT_RSC_RENDER_MODE_HEADER,
|
|
26
|
-
VINEXT_RSC_STATE_FINGERPRINT_HEADER
|
|
27
|
-
].join(", ");
|
|
28
17
|
const CACHE_BUSTING_DIGEST_BYTES = 12;
|
|
29
18
|
const textEncoder = new TextEncoder();
|
|
30
19
|
function encodeBase64Url(bytes) {
|
|
@@ -41,10 +30,16 @@ function normalizeCompatibilityId(value) {
|
|
|
41
30
|
function getVinextRscCompatibilityId() {
|
|
42
31
|
return normalizeCompatibilityId(process.env.__VINEXT_RSC_COMPATIBILITY_ID);
|
|
43
32
|
}
|
|
44
|
-
function
|
|
33
|
+
function getVinextRscBuildId() {
|
|
34
|
+
return normalizeCompatibilityId(process.env.__VINEXT_RSC_BUILD_IDENTITY);
|
|
35
|
+
}
|
|
36
|
+
function applyRscCompatibilityIdHeader(headers, compatibilityId = getVinextRscCompatibilityId(), buildId = getVinextRscBuildId()) {
|
|
45
37
|
const normalized = normalizeCompatibilityId(compatibilityId);
|
|
46
38
|
if (normalized) headers.set(VINEXT_RSC_COMPATIBILITY_ID_HEADER, normalized);
|
|
47
39
|
else headers.delete(VINEXT_RSC_COMPATIBILITY_ID_HEADER);
|
|
40
|
+
const normalizedBuildId = normalizeCompatibilityId(buildId);
|
|
41
|
+
if (normalizedBuildId) headers.set(VINEXT_RSC_BUILD_ID_HEADER, normalizedBuildId);
|
|
42
|
+
else headers.delete(VINEXT_RSC_BUILD_ID_HEADER);
|
|
48
43
|
}
|
|
49
44
|
function applyRscDeploymentIdHeader(headers) {
|
|
50
45
|
const deploymentId = getDeploymentId();
|
|
@@ -147,12 +142,24 @@ function stripRscCacheBustingSearchParam(url) {
|
|
|
147
142
|
function stripRscSuffix(pathname) {
|
|
148
143
|
return pathname.endsWith(".rsc") ? pathname.slice(0, -4) : pathname;
|
|
149
144
|
}
|
|
150
|
-
function
|
|
145
|
+
function createCanonicalRscRequestHeaders(deploymentId = getDeploymentId()) {
|
|
151
146
|
const headers = new Headers({
|
|
152
147
|
Accept: VINEXT_RSC_CONTENT_TYPE,
|
|
153
148
|
["RSC"]: "1"
|
|
154
149
|
});
|
|
155
|
-
applyDeploymentIdHeader(headers);
|
|
150
|
+
applyDeploymentIdHeader(headers, deploymentId ?? void 0);
|
|
151
|
+
return headers;
|
|
152
|
+
}
|
|
153
|
+
/** Headers for the deterministic loading-boundary prefetch representation. */
|
|
154
|
+
function createCanonicalLoadingShellRscRequestHeaders(deploymentId = getDeploymentId()) {
|
|
155
|
+
const headers = createCanonicalRscRequestHeaders(deploymentId);
|
|
156
|
+
headers.set(NEXT_ROUTER_PREFETCH_HEADER, "1");
|
|
157
|
+
headers.set(NEXT_ROUTER_SEGMENT_PREFETCH_HEADER, "1");
|
|
158
|
+
headers.set(VINEXT_RSC_RENDER_MODE_HEADER, APP_RSC_RENDER_MODE_PREFETCH_LOADING_SHELL);
|
|
159
|
+
return headers;
|
|
160
|
+
}
|
|
161
|
+
function createRscRequestHeaders(options = {}) {
|
|
162
|
+
const headers = createCanonicalRscRequestHeaders(options.deploymentId);
|
|
156
163
|
if (options.prefetchRouterState) {
|
|
157
164
|
if (options.includePrefetchHeader !== false) headers.set(NEXT_ROUTER_PREFETCH_HEADER, "1");
|
|
158
165
|
headers.set(NEXT_ROUTER_STATE_TREE_HEADER, encodeURIComponent(JSON.stringify(options.prefetchRouterState)));
|
|
@@ -169,15 +176,58 @@ function createRscRequestHeaders(options = {}) {
|
|
|
169
176
|
if (renderMode !== "navigation") headers.set(VINEXT_RSC_RENDER_MODE_HEADER, renderMode);
|
|
170
177
|
return headers;
|
|
171
178
|
}
|
|
179
|
+
/**
|
|
180
|
+
* Convert a full, non-contextual navigation request to the definitive ISR RSC
|
|
181
|
+
* variant. Partial/intercepted/mounted-slot payloads remain contextual because
|
|
182
|
+
* their bytes can legitimately differ from the destination's full route.
|
|
183
|
+
*/
|
|
184
|
+
function canonicalizePrewarmableRscRequestHeaders(headers) {
|
|
185
|
+
if (headers.has("X-Vinext-Rsc-Render-Mode") || headers.has("X-Vinext-Interception-Context") || headers.has("X-Vinext-Interception-Id") || headers.has("X-Vinext-Mounted-Slots")) return false;
|
|
186
|
+
headers.delete(NEXT_ROUTER_PREFETCH_HEADER);
|
|
187
|
+
headers.delete(NEXT_ROUTER_SEGMENT_PREFETCH_HEADER);
|
|
188
|
+
headers.delete(NEXT_ROUTER_STATE_TREE_HEADER);
|
|
189
|
+
headers.delete(NEXT_URL_HEADER);
|
|
190
|
+
headers.delete(VINEXT_RSC_STATE_FINGERPRINT_HEADER);
|
|
191
|
+
headers.delete(VINEXT_CLIENT_REUSE_MANIFEST_HEADER);
|
|
192
|
+
return true;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Convert an ordinary loading-boundary prefetch to the shared loading-shell
|
|
196
|
+
* variant. The payload is selected by the three retained mode headers; visible
|
|
197
|
+
* router state is transport context and must not fragment the shared response.
|
|
198
|
+
*/
|
|
199
|
+
function canonicalizeLoadingShellRscRequestHeaders(headers) {
|
|
200
|
+
if (headers.get("X-Vinext-Rsc-Render-Mode") !== "prefetch-loading-shell" || headers.has("X-Vinext-Interception-Context") || headers.has("X-Vinext-Interception-Id") || headers.has("X-Vinext-Mounted-Slots")) return false;
|
|
201
|
+
headers.set(NEXT_ROUTER_PREFETCH_HEADER, "1");
|
|
202
|
+
headers.set(NEXT_ROUTER_SEGMENT_PREFETCH_HEADER, "1");
|
|
203
|
+
headers.delete(NEXT_ROUTER_STATE_TREE_HEADER);
|
|
204
|
+
headers.delete(NEXT_URL_HEADER);
|
|
205
|
+
headers.delete(VINEXT_RSC_STATE_FINGERPRINT_HEADER);
|
|
206
|
+
headers.delete(VINEXT_CLIENT_REUSE_MANIFEST_HEADER);
|
|
207
|
+
return true;
|
|
208
|
+
}
|
|
172
209
|
function toRscRequestPath(href) {
|
|
173
210
|
const hashIndex = href.indexOf("#");
|
|
174
211
|
return hashIndex === -1 ? href : href.slice(0, hashIndex);
|
|
175
212
|
}
|
|
176
213
|
async function createRscRequestUrl(href, headers) {
|
|
177
214
|
const url = new URL(toRscRequestPath(href), "http://vinext.local");
|
|
215
|
+
if (typeof window !== "undefined" && process.env.NODE_ENV === "production" && process.env.__NEXT_CONFIG_OUTPUT === "export") {
|
|
216
|
+
const basePath = process.env.__NEXT_ROUTER_BASEPATH ?? "";
|
|
217
|
+
const isBasePathRoot = basePath !== "" && url.pathname === basePath;
|
|
218
|
+
if (url.pathname.endsWith("/")) url.pathname += "index.txt";
|
|
219
|
+
else url.pathname += isBasePathRoot ? "/index.txt" : ".txt";
|
|
220
|
+
return `${url.pathname}${url.search}`;
|
|
221
|
+
}
|
|
178
222
|
setRscCacheBustingSearchParam(url, await computeRscCacheBustingSearchParam(headers));
|
|
179
223
|
return `${url.pathname}${url.search}`;
|
|
180
224
|
}
|
|
225
|
+
/** Build the definitive full-route RSC URL shared by prefetch and navigation. */
|
|
226
|
+
function createCanonicalRscRequestUrl(href) {
|
|
227
|
+
const url = new URL(toRscRequestPath(href), "http://vinext.local");
|
|
228
|
+
setRscCacheBustingSearchParam(url, "");
|
|
229
|
+
return `${url.pathname}${url.search}`;
|
|
230
|
+
}
|
|
181
231
|
function createServerActionRequestUrl(href) {
|
|
182
232
|
const hashIndex = href.indexOf("#");
|
|
183
233
|
const beforeHash = hashIndex === -1 ? href : href.slice(0, hashIndex);
|
|
@@ -247,4 +297,4 @@ async function resolveInvalidRscCacheBustingRequest(options) {
|
|
|
247
297
|
});
|
|
248
298
|
}
|
|
249
299
|
//#endregion
|
|
250
|
-
export { VINEXT_RSC_CACHE_BUSTING_SEARCH_PARAM, VINEXT_RSC_COMPATIBILITY_ID_HEADER, VINEXT_RSC_CONTENT_TYPE, VINEXT_RSC_RENDER_MODE_HEADER, VINEXT_RSC_VARY_HEADER, applyRscCompatibilityIdHeader, applyRscDeploymentIdHeader, computeRscCacheBustingSearchParam, createRscRedirectLocation, createRscRequestHeaders, createRscRequestUrl, createServerActionRequestUrl, getVinextRscCompatibilityId, hasRscCacheBustingSearchParam, isRscCompatibilityIdCompatible, resolveHardNavigationTargetFromRscResponse, resolveInvalidRscCacheBustingRequest, resolveRscCompatibilityNavigationDecision, setRscCacheBustingSearchParam, stripRscCacheBustingSearchParam, stripRscSuffix };
|
|
300
|
+
export { VINEXT_RSC_BUILD_ID_HEADER, VINEXT_RSC_CACHE_BUSTING_SEARCH_PARAM, VINEXT_RSC_COMPATIBILITY_ID_HEADER, VINEXT_RSC_CONTENT_TYPE, VINEXT_RSC_RENDER_MODE_HEADER, VINEXT_RSC_VARY_HEADER, applyRscCompatibilityIdHeader, applyRscDeploymentIdHeader, canonicalizeLoadingShellRscRequestHeaders, canonicalizePrewarmableRscRequestHeaders, computeRscCacheBustingSearchParam, createCanonicalLoadingShellRscRequestHeaders, createCanonicalRscRequestHeaders, createCanonicalRscRequestUrl, createRscRedirectLocation, createRscRequestHeaders, createRscRequestUrl, createServerActionRequestUrl, getVinextRscCompatibilityId, hasRscCacheBustingSearchParam, isRscCompatibilityIdCompatible, resolveHardNavigationTargetFromRscResponse, resolveInvalidRscCacheBustingRequest, resolveRscCompatibilityNavigationDecision, setRscCacheBustingSearchParam, stripRscCacheBustingSearchParam, stripRscSuffix };
|
|
@@ -38,10 +38,12 @@ type AppRscHandlerRoute = {
|
|
|
38
38
|
slots?: AppRouteTreePrefetchRoute["slots"];
|
|
39
39
|
};
|
|
40
40
|
type AppRscRouteMatch<TRoute> = {
|
|
41
|
+
interceptionSourceIsConcrete?: boolean;
|
|
41
42
|
params: AppPageParams;
|
|
42
43
|
route: TRoute;
|
|
43
44
|
};
|
|
44
45
|
type DispatchMatchedPageOptions<TRoute> = {
|
|
46
|
+
bypassInterceptionContextCache: boolean;
|
|
45
47
|
clientReuseManifest: ClientReuseManifestParseResult;
|
|
46
48
|
cleanPathname: string;
|
|
47
49
|
displayPathname: string;
|