vinext 0.2.0 → 1.0.0-beta.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 +50 -29
- package/dist/build/client-build-config.d.ts +13 -92
- package/dist/build/client-build-config.js +17 -95
- package/dist/build/css-url-assets.d.ts +3 -1
- package/dist/build/css-url-assets.js +18 -1
- package/dist/build/inject-pregenerated-paths.d.ts +3 -0
- package/dist/build/inject-pregenerated-paths.js +4 -1
- package/dist/build/prerender-paths.d.ts +23 -0
- package/dist/build/prerender-paths.js +296 -0
- package/dist/build/prerender-server-entry.d.ts +1 -0
- package/dist/build/prerender-server-entry.js +49 -0
- package/dist/build/prerender-server-pool.d.ts +44 -0
- package/dist/build/prerender-server-pool.js +194 -0
- package/dist/build/prerender.d.ts +12 -1
- package/dist/build/prerender.js +87 -23
- package/dist/build/run-prerender.js +2 -1
- package/dist/cache/cache-adapters-virtual.d.ts +7 -1
- package/dist/cache/cache-adapters-virtual.js +26 -2
- package/dist/check.d.ts +4 -6
- package/dist/check.js +13 -9
- package/dist/cli.js +34 -54
- package/dist/client/vinext-next-data.d.ts +4 -1
- package/dist/config/config-matchers.js +33 -11
- package/dist/config/next-config.d.ts +36 -6
- package/dist/config/next-config.js +17 -4
- package/dist/config/prerender.d.ts +11 -1
- package/dist/config/prerender.js +19 -1
- package/dist/config/tsconfig-paths.js +5 -1
- package/dist/entries/app-browser-entry.js +5 -1
- package/dist/entries/app-rsc-entry.d.ts +3 -2
- package/dist/entries/app-rsc-entry.js +4 -0
- package/dist/entries/pages-client-entry.js +14 -3
- package/dist/entries/pages-server-entry.js +18 -4
- package/dist/image/image-adapters-virtual.js +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +549 -152
- package/dist/init-cloudflare.js +12 -3
- package/dist/init-platform.d.ts +10 -1
- package/dist/init-platform.js +78 -12
- package/dist/init.d.ts +11 -3
- package/dist/init.js +129 -31
- package/dist/plugins/fonts.js +1 -1
- package/dist/plugins/ignore-dynamic-requests.js +1 -1
- package/dist/plugins/og-assets.js +2 -1
- package/dist/plugins/optimize-imports.js +1 -1
- package/dist/plugins/rsc-reference-validation-normalizer.d.ts +12 -0
- package/dist/plugins/rsc-reference-validation-normalizer.js +48 -0
- package/dist/plugins/sass.d.ts +17 -1
- package/dist/plugins/sass.js +74 -1
- package/dist/plugins/styled-jsx.d.ts +16 -0
- package/dist/plugins/styled-jsx.js +149 -0
- package/dist/routing/app-route-graph.js +22 -16
- package/dist/routing/file-matcher.d.ts +8 -1
- package/dist/routing/file-matcher.js +15 -3
- package/dist/server/app-browser-entry.js +44 -14
- package/dist/server/app-browser-navigation-controller.js +5 -2
- package/dist/server/app-browser-server-action-client.js +5 -3
- package/dist/server/app-browser-state.d.ts +1 -0
- package/dist/server/app-browser-state.js +1 -1
- package/dist/server/app-layout-param-observation.d.ts +1 -1
- package/dist/server/app-page-boundary.js +2 -1
- package/dist/server/app-page-cache-finalizer.d.ts +1 -0
- package/dist/server/app-page-cache-finalizer.js +3 -3
- package/dist/server/app-page-cache-render.d.ts +1 -1
- package/dist/server/app-page-cache.js +23 -6
- package/dist/server/app-page-dispatch.d.ts +2 -1
- package/dist/server/app-page-dispatch.js +27 -10
- package/dist/server/app-page-element-builder.js +1 -0
- package/dist/server/app-page-execution.js +2 -1
- package/dist/server/app-page-render-identity.d.ts +1 -0
- package/dist/server/app-page-render-identity.js +2 -1
- package/dist/server/app-page-render.d.ts +3 -1
- package/dist/server/app-page-render.js +67 -16
- package/dist/server/app-page-response.d.ts +7 -0
- package/dist/server/app-page-response.js +16 -4
- package/dist/server/app-page-route-wiring.js +3 -3
- package/dist/server/app-page-stream.d.ts +2 -1
- package/dist/server/app-page-stream.js +1 -1
- package/dist/server/app-route-handler-execution.d.ts +1 -1
- package/dist/server/app-route-tree-prefetch.d.ts +43 -0
- package/dist/server/app-route-tree-prefetch.js +187 -0
- package/dist/server/app-router-entry.js +1 -1
- package/dist/server/app-rsc-cache-busting.d.ts +2 -1
- package/dist/server/app-rsc-cache-busting.js +9 -5
- package/dist/server/app-rsc-handler.d.ts +6 -0
- package/dist/server/app-rsc-handler.js +88 -10
- package/dist/server/app-rsc-render-mode.d.ts +3 -5
- package/dist/server/app-rsc-render-mode.js +5 -12
- package/dist/server/app-rsc-request-normalization.d.ts +3 -4
- package/dist/server/app-rsc-request-normalization.js +4 -5
- package/dist/server/app-server-action-execution.js +6 -5
- package/dist/server/app-ssr-entry.js +1 -1
- package/dist/server/cache-control.d.ts +3 -1
- package/dist/server/cache-control.js +13 -1
- package/dist/server/dev-module-runner.js +1 -1
- package/dist/server/dev-origin-check.d.ts +2 -2
- package/dist/server/dev-origin-check.js +2 -2
- package/dist/server/dev-server.d.ts +11 -1
- package/dist/server/dev-server.js +82 -24
- package/dist/server/headers.d.ts +6 -2
- package/dist/server/headers.js +11 -5
- package/dist/server/image-optimization.d.ts +12 -1
- package/dist/server/image-optimization.js +13 -1
- package/dist/server/isr-cache.d.ts +13 -4
- package/dist/server/isr-cache.js +8 -4
- package/dist/server/pages-data-route.d.ts +4 -2
- package/dist/server/pages-data-route.js +18 -4
- package/dist/server/pages-dev-module-url.d.ts +2 -1
- package/dist/server/pages-dev-module-url.js +6 -3
- package/dist/server/pages-node-compat.d.ts +12 -1
- package/dist/server/pages-node-compat.js +50 -1
- package/dist/server/pages-page-data.d.ts +9 -0
- package/dist/server/pages-page-data.js +18 -10
- package/dist/server/pages-page-handler.js +19 -7
- package/dist/server/pages-page-response.d.ts +1 -0
- package/dist/server/pages-page-response.js +5 -4
- package/dist/server/pages-request-pipeline.d.ts +8 -6
- package/dist/server/pages-request-pipeline.js +31 -7
- package/dist/server/pages-router-entry.js +1 -1
- package/dist/server/prerender-manifest.d.ts +15 -1
- package/dist/server/prerender-manifest.js +29 -1
- package/dist/server/prod-server.d.ts +10 -7
- package/dist/server/prod-server.js +43 -28
- package/dist/server/request-pipeline.js +1 -1
- package/dist/server/static-file-cache.js +1 -1
- package/dist/shims/cache-handler.js +8 -1
- package/dist/shims/cache.d.ts +1 -1
- package/dist/shims/cache.js +3 -0
- package/dist/shims/cdn-cache.d.ts +2 -7
- package/dist/shims/cdn-cache.js +2 -14
- package/dist/shims/fetch-cache.d.ts +3 -1
- package/dist/shims/fetch-cache.js +77 -52
- package/dist/shims/internal/app-prefetch-fetch-queue.d.ts +10 -0
- package/dist/shims/internal/app-prefetch-fetch-queue.js +61 -0
- package/dist/shims/internal/hybrid-client-route-owner.d.ts +2 -1
- package/dist/shims/internal/hybrid-client-route-owner.js +34 -1
- package/dist/shims/internal/pages-data-fetch-dedup.d.ts +2 -1
- package/dist/shims/internal/pages-data-fetch-dedup.js +13 -2
- package/dist/shims/internal/pages-data-target.d.ts +8 -3
- package/dist/shims/internal/pages-data-target.js +9 -4
- package/dist/shims/link.d.ts +1 -1
- package/dist/shims/link.js +179 -42
- package/dist/shims/navigation.d.ts +15 -3
- package/dist/shims/navigation.js +202 -39
- package/dist/shims/request-context.js +18 -0
- package/dist/shims/request-state-types.d.ts +2 -2
- package/dist/shims/router.js +31 -15
- package/dist/shims/unified-request-context.d.ts +1 -1
- package/dist/shims/unified-request-context.js +1 -0
- package/dist/typegen.js +1 -1
- package/dist/utils/middleware-request-headers.js +1 -1
- package/dist/utils/project.d.ts +1 -1
- package/dist/utils/protocol-headers.d.ts +7 -1
- package/dist/utils/protocol-headers.js +7 -1
- package/dist/utils/vite-version.d.ts +4 -12
- package/dist/utils/vite-version.js +39 -21
- package/package.json +31 -13
- package/dist/cloudflare/index.d.ts +0 -3
- package/dist/cloudflare/index.js +0 -3
- package/dist/packages/cloudflare/src/cache/cdn-adapter.runtime.js +0 -102
- package/dist/packages/cloudflare/src/cache/kv-data-adapter.runtime.d.ts +0 -126
- package/dist/packages/cloudflare/src/cache/kv-data-adapter.runtime.js +0 -435
- package/dist/packages/cloudflare/src/deploy-config.js +0 -150
- package/dist/packages/cloudflare/src/deploy-help.js +0 -55
- package/dist/packages/cloudflare/src/deploy.js +0 -276
- package/dist/packages/cloudflare/src/tpr.d.ts +0 -45
- package/dist/packages/cloudflare/src/tpr.js +0 -561
- package/dist/packages/cloudflare/src/utils/cache-control-metadata.js +0 -20
|
@@ -4,6 +4,7 @@ import { applyMiddlewareRequestHeaders, isExternalUrl, matchRedirect, matchRewri
|
|
|
4
4
|
import { applyConfigHeadersToHeaderRecord, cloneRequestWithUrl, normalizeTrailingSlash } from "./request-pipeline.js";
|
|
5
5
|
import { mergeRewriteQuery } from "../utils/query.js";
|
|
6
6
|
import { normalizeDefaultLocalePathname, stripI18nLocaleForApiRoute } from "./pages-i18n.js";
|
|
7
|
+
import { buildMiddlewarePrefetchSkipResponse } from "./pages-data-route.js";
|
|
7
8
|
import { mergeHeaders } from "./worker-utils.js";
|
|
8
9
|
//#region src/server/pages-request-pipeline.ts
|
|
9
10
|
async function fetchWorkerFilesystemRoute(request, requestPathname, phase, fetchAsset) {
|
|
@@ -149,6 +150,15 @@ async function runPagesRequest(request, deps) {
|
|
|
149
150
|
if (resolvedLocale && i18nConfig.locales.includes(resolvedLocale)) return matchedPathname === "/" ? `/${resolvedLocale}` : `/${resolvedLocale}${matchedPathname}`;
|
|
150
151
|
return matchResolvedPathname(matchedPathname);
|
|
151
152
|
};
|
|
153
|
+
const buildMiddlewarePrefetchSkipResult = (match) => {
|
|
154
|
+
if (!match) return null;
|
|
155
|
+
if (match.route.dataKind !== "server" || !isDataRequest || !deps.hasMiddleware || request.headers.get("x-middleware-prefetch") !== "1") return null;
|
|
156
|
+
return {
|
|
157
|
+
type: "response",
|
|
158
|
+
response: mergeHeaders(buildMiddlewarePrefetchSkipResponse(matchedPathnameForRoute(match.route.pattern)), middlewareHeaders, void 0),
|
|
159
|
+
defaultContentType: "application/json"
|
|
160
|
+
};
|
|
161
|
+
};
|
|
152
162
|
if (configHeaders.length) applyConfigHeadersToHeaderRecord(middlewareHeaders, {
|
|
153
163
|
configHeaders,
|
|
154
164
|
pathname: matchPathname,
|
|
@@ -178,14 +188,16 @@ async function runPagesRequest(request, deps) {
|
|
|
178
188
|
const beforeFilesResult = await serveFilesystemRoute(resolvedPathname, "beforeFiles");
|
|
179
189
|
if (beforeFilesResult) return beforeFilesResult;
|
|
180
190
|
}
|
|
181
|
-
|
|
191
|
+
const isOutsideBasePathUnclaimed = () => basePath && !hadBasePath && !configRewriteFired;
|
|
192
|
+
const outOfBasePathNotFound = () => ({
|
|
182
193
|
type: "response",
|
|
183
194
|
response: new Response("This page could not be found", {
|
|
184
195
|
status: 404,
|
|
185
196
|
headers: { "Content-Type": "text/html; charset=utf-8" }
|
|
186
197
|
})
|
|
187
|
-
};
|
|
198
|
+
});
|
|
188
199
|
const handleResolvedApiRoute = async () => {
|
|
200
|
+
if (isOutsideBasePathUnclaimed()) return null;
|
|
189
201
|
const apiLookupUrl = stripI18nLocaleForApiRoute(resolvedUrl, i18nConfig);
|
|
190
202
|
const apiLookupPathname = apiLookupUrl.split("?")[0];
|
|
191
203
|
if (!apiLookupPathname.startsWith("/api/") && apiLookupPathname !== "/api") return null;
|
|
@@ -212,7 +224,7 @@ async function runPagesRequest(request, deps) {
|
|
|
212
224
|
};
|
|
213
225
|
const apiResult = await handleResolvedApiRoute();
|
|
214
226
|
if (apiResult) return apiResult;
|
|
215
|
-
let pageMatch = deps.matchPageRoute ? deps.matchPageRoute(resolvedPathname, request) : null;
|
|
227
|
+
let pageMatch = !isOutsideBasePathUnclaimed() && deps.matchPageRoute ? deps.matchPageRoute(resolvedPathname, request) : null;
|
|
216
228
|
let resolvedPathnameChanged = false;
|
|
217
229
|
if (!pageMatch || pageMatch.route.isDynamic) for (const rewrite of configRewrites.afterFiles ?? []) {
|
|
218
230
|
const rewritten = matchRewrite(matchResolvedPathname(resolvedPathname), [rewrite], rewriteRequestContext(), basePathState);
|
|
@@ -223,6 +235,7 @@ async function runPagesRequest(request, deps) {
|
|
|
223
235
|
};
|
|
224
236
|
resolvedUrl = mergeRewriteQuery(resolvedUrl, rewritten);
|
|
225
237
|
resolvedPathname = pathnameForResolvedUrl(resolvedUrl);
|
|
238
|
+
configRewriteFired = true;
|
|
226
239
|
resolvedPathnameChanged = true;
|
|
227
240
|
const afterFilesFilesystemResult = await serveFilesystemRoute(resolvedPathname, "afterFiles");
|
|
228
241
|
if (afterFilesFilesystemResult) return afterFilesFilesystemResult;
|
|
@@ -239,7 +252,7 @@ async function runPagesRequest(request, deps) {
|
|
|
239
252
|
refreshDataRewriteHeader();
|
|
240
253
|
if (typeof deps.renderPage === "function") {
|
|
241
254
|
let renderPageMatch = pageMatch;
|
|
242
|
-
if ((isDataReq || isDataRequest) && !renderPageMatch && configRewrites.fallback?.length) for (const rewrite of configRewrites.fallback) {
|
|
255
|
+
if ((isOutsideBasePathUnclaimed() || isDataReq || isDataRequest) && !renderPageMatch && configRewrites.fallback?.length) for (const rewrite of configRewrites.fallback) {
|
|
243
256
|
const fallbackRewrite = matchRewrite(matchResolvedPathname(resolvedPathname), [rewrite], rewriteRequestContext(), basePathState);
|
|
244
257
|
if (!fallbackRewrite) continue;
|
|
245
258
|
if (isExternalUrl(fallbackRewrite)) return {
|
|
@@ -248,6 +261,7 @@ async function runPagesRequest(request, deps) {
|
|
|
248
261
|
};
|
|
249
262
|
resolvedUrl = mergeRewriteQuery(resolvedUrl, fallbackRewrite);
|
|
250
263
|
resolvedPathname = pathnameForResolvedUrl(resolvedUrl);
|
|
264
|
+
configRewriteFired = true;
|
|
251
265
|
const fallbackFilesystemResult = await serveFilesystemRoute(resolvedPathname, "fallback");
|
|
252
266
|
if (fallbackFilesystemResult) return fallbackFilesystemResult;
|
|
253
267
|
const fallbackApiResult = await handleResolvedApiRoute();
|
|
@@ -256,6 +270,9 @@ async function runPagesRequest(request, deps) {
|
|
|
256
270
|
refreshDataRewriteHeader();
|
|
257
271
|
if (renderPageMatch) break;
|
|
258
272
|
}
|
|
273
|
+
const prefetchSkipResult = buildMiddlewarePrefetchSkipResult(renderPageMatch);
|
|
274
|
+
if (prefetchSkipResult) return prefetchSkipResult;
|
|
275
|
+
if (isOutsideBasePathUnclaimed()) return outOfBasePathNotFound();
|
|
259
276
|
const shouldDeferErrorPageOnMiss = !isDataReq && !isDataRequest && !!deps.matchPageRoute && !renderPageMatch;
|
|
260
277
|
const initialRenderOptions = shouldDeferErrorPageOnMiss ? { renderErrorPageOnMiss: false } : isDataReq ? { isDataReq: true } : void 0;
|
|
261
278
|
const stagedHeaders = new Headers();
|
|
@@ -272,6 +289,7 @@ async function runPagesRequest(request, deps) {
|
|
|
272
289
|
};
|
|
273
290
|
resolvedUrl = mergeRewriteQuery(resolvedUrl, fallbackRewrite);
|
|
274
291
|
resolvedPathname = pathnameForResolvedUrl(resolvedUrl);
|
|
292
|
+
configRewriteFired = true;
|
|
275
293
|
const fallbackFilesystemResult = await serveFilesystemRoute(resolvedPathname, "fallback");
|
|
276
294
|
if (fallbackFilesystemResult) return fallbackFilesystemResult;
|
|
277
295
|
const fallbackApiResult = await handleResolvedApiRoute();
|
|
@@ -302,10 +320,11 @@ async function runPagesRequest(request, deps) {
|
|
|
302
320
|
return {
|
|
303
321
|
type: "response",
|
|
304
322
|
response: merged,
|
|
305
|
-
defaultContentType: "text/html"
|
|
323
|
+
defaultContentType: "text/html; charset=utf-8"
|
|
306
324
|
};
|
|
307
325
|
}
|
|
308
|
-
|
|
326
|
+
let devPageMatch = isOutsideBasePathUnclaimed() ? null : resolvedPathnameChanged ? deps.matchPageRoute ? deps.matchPageRoute(resolvedPathname, request) : null : pageMatch;
|
|
327
|
+
if (!devPageMatch && configRewrites.fallback?.length) for (const rewrite of configRewrites.fallback) {
|
|
309
328
|
const fallbackRewrite = matchRewrite(matchResolvedPathname(resolvedPathname), [rewrite], rewriteRequestContext(), basePathState);
|
|
310
329
|
if (!fallbackRewrite) continue;
|
|
311
330
|
if (isExternalUrl(fallbackRewrite)) return {
|
|
@@ -314,12 +333,17 @@ async function runPagesRequest(request, deps) {
|
|
|
314
333
|
};
|
|
315
334
|
resolvedUrl = mergeRewriteQuery(resolvedUrl, fallbackRewrite);
|
|
316
335
|
resolvedPathname = pathnameForResolvedUrl(resolvedUrl);
|
|
336
|
+
configRewriteFired = true;
|
|
317
337
|
const fallbackFilesystemResult = await serveFilesystemRoute(resolvedPathname, "fallback");
|
|
318
338
|
if (fallbackFilesystemResult) return fallbackFilesystemResult;
|
|
319
339
|
const fallbackApiResult = await handleResolvedApiRoute();
|
|
320
340
|
if (fallbackApiResult) return fallbackApiResult;
|
|
321
|
-
|
|
341
|
+
devPageMatch = deps.matchPageRoute?.(resolvedPathname, request) ?? null;
|
|
342
|
+
if (devPageMatch) break;
|
|
322
343
|
}
|
|
344
|
+
const prefetchSkipResult = buildMiddlewarePrefetchSkipResult(devPageMatch);
|
|
345
|
+
if (prefetchSkipResult) return prefetchSkipResult;
|
|
346
|
+
if (isOutsideBasePathUnclaimed()) return outOfBasePathNotFound();
|
|
323
347
|
refreshDataRewriteHeader();
|
|
324
348
|
return {
|
|
325
349
|
type: "render",
|
|
@@ -2,8 +2,8 @@ import { hasBasePath, stripBasePath } from "../utils/base-path.js";
|
|
|
2
2
|
import { notFoundStaticAssetResponse } from "./http-error-responses.js";
|
|
3
3
|
import { isOpenRedirectShaped } from "./open-redirect.js";
|
|
4
4
|
import { cloneRequestWithHeaders, cloneRequestWithUrl, filterInternalHeaders } from "./request-pipeline.js";
|
|
5
|
-
import { DEFAULT_DEVICE_SIZES, DEFAULT_IMAGE_SIZES, handleConfiguredImageOptimization, isImageOptimizationPath } from "./image-optimization.js";
|
|
6
5
|
import { assetPrefixPathname, isNextStaticPath } from "../utils/asset-prefix.js";
|
|
6
|
+
import { DEFAULT_DEVICE_SIZES, DEFAULT_IMAGE_SIZES, handleConfiguredImageOptimization, isImageOptimizationPath } from "./image-optimization.js";
|
|
7
7
|
import { finalizeMissingStaticAssetResponse } from "./worker-utils.js";
|
|
8
8
|
import { fetchWorkerFilesystemRoute, runPagesRequest, wrapMiddlewareWithBasePath } from "./pages-request-pipeline.js";
|
|
9
9
|
import { registerConfiguredCacheAdapters } from "virtual:vinext-cache-adapters";
|
|
@@ -15,6 +15,10 @@ type PrerenderManifest = {
|
|
|
15
15
|
routes?: PrerenderManifestRoute[];
|
|
16
16
|
pregeneratedConcretePaths?: Array<[string, string[]]>;
|
|
17
17
|
};
|
|
18
|
+
type PrerenderedPathSelectionOptions = {
|
|
19
|
+
includeFallbackShells?: boolean;
|
|
20
|
+
includeErrorDocuments?: boolean;
|
|
21
|
+
};
|
|
18
22
|
declare function readPrerenderManifest(manifestPath: string): PrerenderManifest | null;
|
|
19
23
|
declare function getRenderedAppRoutes(routes: PrerenderManifestRoute[]): PrerenderManifestRoute[];
|
|
20
24
|
/**
|
|
@@ -31,5 +35,15 @@ declare function isFallbackShellArtifactPath(pathname: string, route?: Prerender
|
|
|
31
35
|
* rendered App routes or all routes are fallback-shell artifacts.
|
|
32
36
|
*/
|
|
33
37
|
declare function buildPregeneratedConcretePathTable(manifest: PrerenderManifest): Array<[string, string[]]>;
|
|
38
|
+
/**
|
|
39
|
+
* Select concrete URL paths that were rendered by the prerender engine.
|
|
40
|
+
*
|
|
41
|
+
* This intentionally includes both App Router and Pages Router entries because
|
|
42
|
+
* deploy-time cache warmup should exercise the same URLs the prerender phase
|
|
43
|
+
* proved are statically renderable. PPR fallback-shell placeholder artifacts
|
|
44
|
+
* and known error documents are excluded by default so warmup does not request
|
|
45
|
+
* synthetic bracket paths or treat a healthy 404 response as a failed warmup.
|
|
46
|
+
*/
|
|
47
|
+
declare function getPrerenderedConcretePaths(manifest: PrerenderManifest, options?: PrerenderedPathSelectionOptions): string[];
|
|
34
48
|
//#endregion
|
|
35
|
-
export { PrerenderManifest, buildPregeneratedConcretePathTable, getRenderedAppRoutes, isFallbackShellArtifactPath, readPrerenderManifest };
|
|
49
|
+
export { PrerenderManifest, PrerenderManifestRoute, PrerenderedPathSelectionOptions, buildPregeneratedConcretePathTable, getPrerenderedConcretePaths, getRenderedAppRoutes, isFallbackShellArtifactPath, readPrerenderManifest };
|
|
@@ -22,6 +22,9 @@ function groupRoutesByPattern(routes) {
|
|
|
22
22
|
}
|
|
23
23
|
return byPattern;
|
|
24
24
|
}
|
|
25
|
+
function isErrorDocumentRoute(pathname, route) {
|
|
26
|
+
return pathname === "/404" || pathname === "/500" || pathname === "/_error" || route.route === "/404" || route.route === "/500" || route.route === "/_error";
|
|
27
|
+
}
|
|
25
28
|
/**
|
|
26
29
|
* Returns true when `pathname` contains bracket-delimited route params,
|
|
27
30
|
* indicating it is a fallback-shell placeholder (e.g. `/en/blog/[slug]`)
|
|
@@ -50,5 +53,30 @@ function buildPregeneratedConcretePathTable(manifest) {
|
|
|
50
53
|
});
|
|
51
54
|
return Array.from(groupRoutesByPattern(concreteRoutes).entries());
|
|
52
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* Select concrete URL paths that were rendered by the prerender engine.
|
|
58
|
+
*
|
|
59
|
+
* This intentionally includes both App Router and Pages Router entries because
|
|
60
|
+
* deploy-time cache warmup should exercise the same URLs the prerender phase
|
|
61
|
+
* proved are statically renderable. PPR fallback-shell placeholder artifacts
|
|
62
|
+
* and known error documents are excluded by default so warmup does not request
|
|
63
|
+
* synthetic bracket paths or treat a healthy 404 response as a failed warmup.
|
|
64
|
+
*/
|
|
65
|
+
function getPrerenderedConcretePaths(manifest, options) {
|
|
66
|
+
const routes = manifest.routes;
|
|
67
|
+
if (!routes?.length) return [];
|
|
68
|
+
const paths = [];
|
|
69
|
+
const seen = /* @__PURE__ */ new Set();
|
|
70
|
+
for (const route of routes) {
|
|
71
|
+
if (route.status !== "rendered") continue;
|
|
72
|
+
const pathname = route.path ?? route.route;
|
|
73
|
+
if (!options?.includeFallbackShells && isFallbackShellArtifactPath(pathname, route)) continue;
|
|
74
|
+
if (!options?.includeErrorDocuments && isErrorDocumentRoute(pathname, route)) continue;
|
|
75
|
+
if (seen.has(pathname)) continue;
|
|
76
|
+
seen.add(pathname);
|
|
77
|
+
paths.push(pathname);
|
|
78
|
+
}
|
|
79
|
+
return paths;
|
|
80
|
+
}
|
|
53
81
|
//#endregion
|
|
54
|
-
export { buildPregeneratedConcretePathTable, getRenderedAppRoutes, isFallbackShellArtifactPath, readPrerenderManifest };
|
|
82
|
+
export { buildPregeneratedConcretePathTable, getPrerenderedConcretePaths, getRenderedAppRoutes, isFallbackShellArtifactPath, readPrerenderManifest };
|
|
@@ -10,10 +10,9 @@ import { IncomingMessage, ServerResponse } from "node:http";
|
|
|
10
10
|
*
|
|
11
11
|
* The first import of a given path uses the plain file:// URL with NO query
|
|
12
12
|
* string. This is load-bearing: code-split builds emit lazy chunks that
|
|
13
|
-
* import the entry back by bare specifier (default Vite builds
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* entry chunk, which the chunks then import as e.g. "../../index.js").
|
|
13
|
+
* import the entry back by bare specifier (default Vite/Rolldown builds hoist
|
|
14
|
+
* modules shared between the entry's static graph and lazy route chunks into
|
|
15
|
+
* the entry chunk, which the chunks then import as e.g. "../../index.js").
|
|
17
16
|
* Node keys its ESM cache on the full URL including the query string, so if
|
|
18
17
|
* the server imported the entry as `index.js?t=<mtime>`, a chunk's bare
|
|
19
18
|
* back-import would evaluate the entire server bundle a second time and
|
|
@@ -41,20 +40,24 @@ import { IncomingMessage, ServerResponse } from "node:http";
|
|
|
41
40
|
* Exported for direct unit testing of the URL choice.
|
|
42
41
|
*/
|
|
43
42
|
declare function resolveServerEntryImportUrl(entryPath: string): string;
|
|
43
|
+
declare function rememberCurrentServerEntryImportMtime(entryPath: string): void;
|
|
44
44
|
declare function importServerEntryModule(entryPath: string): Promise<any>;
|
|
45
45
|
/** Convert a Node.js IncomingMessage into a ReadableStream for Web Request body. */
|
|
46
46
|
declare function readNodeStream(req: IncomingMessage): ReadableStream<Uint8Array>;
|
|
47
47
|
type ProdServerOptions = {
|
|
48
48
|
/** Port to listen on */port?: number; /** Host to bind to */
|
|
49
49
|
host?: string; /** Path to the build output directory */
|
|
50
|
-
outDir?: string; /**
|
|
50
|
+
outDir?: string; /** Explicit App Router RSC entry path. Defaults to `<outDir>/server/index.js`. */
|
|
51
|
+
rscEntryPath?: string; /** Explicit Pages Router server entry path. Defaults to `<outDir>/server/entry.js`. */
|
|
52
|
+
serverEntryPath?: string; /** Disable compression (default: false) */
|
|
51
53
|
noCompression?: boolean;
|
|
52
54
|
/**
|
|
53
55
|
* Narrow startup context for callers that need a more precise log line.
|
|
54
56
|
* Omitted for normal `vinext start` so the existing production-server output
|
|
55
57
|
* remains stable.
|
|
56
58
|
*/
|
|
57
|
-
purpose?: "prerender";
|
|
59
|
+
purpose?: "prerender"; /** Suppress the startup log for internal child-process servers. */
|
|
60
|
+
silent?: boolean;
|
|
58
61
|
};
|
|
59
62
|
/** Content types that benefit from compression. */
|
|
60
63
|
declare const COMPRESSIBLE_TYPES: Set<string>;
|
|
@@ -145,4 +148,4 @@ declare function resolveAppRouterPrerenderSeeder(entryModule: unknown): AppRoute
|
|
|
145
148
|
*/
|
|
146
149
|
declare function resolveAppRouterAssetPath(pathname: string, assetPathPrefix: string, assetPrefix: string): string | null;
|
|
147
150
|
//#endregion
|
|
148
|
-
export { COMPRESSIBLE_TYPES, COMPRESS_THRESHOLD, ProdServerOptions, importServerEntryModule, mergeResponseHeaders, mergeWebResponse, negotiateEncoding, nodeToWebRequest, readNodeStream, resolveAppRouterAssetPath, resolveAppRouterPrerenderSeeder, resolveRequestHost as resolveHost, resolveServerEntryImportUrl, sendCompressed, sendWebResponse, startProdServer, trustProxy, trustedHosts, tryServeStatic };
|
|
151
|
+
export { COMPRESSIBLE_TYPES, COMPRESS_THRESHOLD, ProdServerOptions, importServerEntryModule, mergeResponseHeaders, mergeWebResponse, negotiateEncoding, nodeToWebRequest, readNodeStream, rememberCurrentServerEntryImportMtime, resolveAppRouterAssetPath, resolveAppRouterPrerenderSeeder, resolveRequestHost as resolveHost, resolveServerEntryImportUrl, sendCompressed, sendWebResponse, startProdServer, trustProxy, trustedHosts, tryServeStatic };
|
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
import { normalizePathnameForRouteMatchStrict } from "../routing/utils.js";
|
|
2
2
|
import { hasBasePath, stripBasePath } from "../utils/base-path.js";
|
|
3
|
-
import { VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, VINEXT_PRERENDER_SECRET_HEADER } from "../utils/protocol-headers.js";
|
|
3
|
+
import { VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, VINEXT_PRERENDER_SECRET_HEADER, VINEXT_PRERENDER_SPECULATIVE_HEADER } from "../utils/protocol-headers.js";
|
|
4
4
|
import { VINEXT_STATIC_FILE_HEADER } from "./headers.js";
|
|
5
5
|
import { normalizePath } from "./normalize-path.js";
|
|
6
6
|
import { notFoundResponse } from "./http-error-responses.js";
|
|
7
7
|
import { isOpenRedirectShaped } from "./open-redirect.js";
|
|
8
8
|
import { filterInternalHeaders } from "./request-pipeline.js";
|
|
9
9
|
import { isUnknownRecord } from "../utils/record.js";
|
|
10
|
-
import { buildNextDataNotFoundResponse, isNextDataPathname, parseNextDataPathname } from "./pages-data-route.js";
|
|
10
|
+
import { buildNextDataNotFoundResponse, isNextDataPathname, normalizeNextDataPagePathname, parseNextDataPathname } from "./pages-data-route.js";
|
|
11
|
+
import { ASSET_PREFIX_URL_DIR, assetPrefixPathname, isAbsoluteAssetPrefix } from "../utils/asset-prefix.js";
|
|
12
|
+
import { setPagesClientAssets } from "./pages-client-assets.js";
|
|
11
13
|
import { resolveRequestHost, resolveRequestProtocol, trustProxy, trustedHosts } from "./proxy-trust.js";
|
|
12
14
|
import { DEFAULT_DEVICE_SIZES, DEFAULT_IMAGE_SIZES, isImageOptimizationPath, isSafeImageContentType, parseImageParams } from "./image-optimization.js";
|
|
13
15
|
import { installSocketErrorBackstop } from "./socket-error-backstop.js";
|
|
14
|
-
import { ASSET_PREFIX_URL_DIR, assetPrefixPathname, isAbsoluteAssetPrefix } from "../utils/asset-prefix.js";
|
|
15
16
|
import { CONTENT_TYPES, StaticFileCache, etagFromFilenameHash } from "./static-file-cache.js";
|
|
16
17
|
import { collectInlineCssManifest } from "../build/inline-css.js";
|
|
17
18
|
import { mergeHeaders } from "./worker-utils.js";
|
|
18
19
|
import { runPagesRequest, wrapMiddlewareWithBasePath } from "./pages-request-pipeline.js";
|
|
19
20
|
import { computeClientRuntimeMetadata } from "../utils/client-runtime-metadata.js";
|
|
20
21
|
import { readTrustedPrerenderRouteParamsFromHeaders, serializePrerenderRouteParamsHeader } from "./prerender-route-params.js";
|
|
21
|
-
import { setPagesClientAssets } from "./pages-client-assets.js";
|
|
22
22
|
import { readPrerenderSecret } from "../build/server-manifest.js";
|
|
23
23
|
import { seedMemoryCacheFromPrerender } from "./seed-cache.js";
|
|
24
24
|
import { negotiateEncoding, parseAcceptedEncodings, selectContentEncoding } from "./accept-encoding.js";
|
|
@@ -56,16 +56,27 @@ import { createServer } from "node:http";
|
|
|
56
56
|
* through a cache-busted URL instead.
|
|
57
57
|
*/
|
|
58
58
|
const bareServerEntryMtimes = /* @__PURE__ */ new Map();
|
|
59
|
+
function resolveCanonicalServerEntry(entryPath) {
|
|
60
|
+
let canonicalEntryPath;
|
|
61
|
+
try {
|
|
62
|
+
canonicalEntryPath = fs.realpathSync.native(entryPath);
|
|
63
|
+
} catch {
|
|
64
|
+
canonicalEntryPath = entryPath;
|
|
65
|
+
}
|
|
66
|
+
return {
|
|
67
|
+
href: pathToFileURL(canonicalEntryPath).href,
|
|
68
|
+
mtime: fs.statSync(canonicalEntryPath).mtimeMs
|
|
69
|
+
};
|
|
70
|
+
}
|
|
59
71
|
/**
|
|
60
72
|
* Import a built server entry module (App Router RSC entry or Pages Router
|
|
61
73
|
* server entry) by absolute file path.
|
|
62
74
|
*
|
|
63
75
|
* The first import of a given path uses the plain file:// URL with NO query
|
|
64
76
|
* string. This is load-bearing: code-split builds emit lazy chunks that
|
|
65
|
-
* import the entry back by bare specifier (default Vite builds
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
* entry chunk, which the chunks then import as e.g. "../../index.js").
|
|
77
|
+
* import the entry back by bare specifier (default Vite/Rolldown builds hoist
|
|
78
|
+
* modules shared between the entry's static graph and lazy route chunks into
|
|
79
|
+
* the entry chunk, which the chunks then import as e.g. "../../index.js").
|
|
69
80
|
* Node keys its ESM cache on the full URL including the query string, so if
|
|
70
81
|
* the server imported the entry as `index.js?t=<mtime>`, a chunk's bare
|
|
71
82
|
* back-import would evaluate the entire server bundle a second time and
|
|
@@ -93,14 +104,7 @@ const bareServerEntryMtimes = /* @__PURE__ */ new Map();
|
|
|
93
104
|
* Exported for direct unit testing of the URL choice.
|
|
94
105
|
*/
|
|
95
106
|
function resolveServerEntryImportUrl(entryPath) {
|
|
96
|
-
|
|
97
|
-
try {
|
|
98
|
-
canonicalEntryPath = fs.realpathSync.native(entryPath);
|
|
99
|
-
} catch {
|
|
100
|
-
canonicalEntryPath = entryPath;
|
|
101
|
-
}
|
|
102
|
-
const href = pathToFileURL(canonicalEntryPath).href;
|
|
103
|
-
const mtime = fs.statSync(canonicalEntryPath).mtimeMs;
|
|
107
|
+
const { href, mtime } = resolveCanonicalServerEntry(entryPath);
|
|
104
108
|
const bareMtime = bareServerEntryMtimes.get(href);
|
|
105
109
|
if (bareMtime === void 0 || bareMtime === mtime) {
|
|
106
110
|
bareServerEntryMtimes.set(href, mtime);
|
|
@@ -108,6 +112,10 @@ function resolveServerEntryImportUrl(entryPath) {
|
|
|
108
112
|
}
|
|
109
113
|
return `${href}?t=${mtime}`;
|
|
110
114
|
}
|
|
115
|
+
function rememberCurrentServerEntryImportMtime(entryPath) {
|
|
116
|
+
const { href, mtime } = resolveCanonicalServerEntry(entryPath);
|
|
117
|
+
bareServerEntryMtimes.set(href, mtime);
|
|
118
|
+
}
|
|
111
119
|
async function importServerEntryModule(entryPath) {
|
|
112
120
|
return import(resolveServerEntryImportUrl(entryPath));
|
|
113
121
|
}
|
|
@@ -557,9 +565,11 @@ function nodeToWebRequest(req, urlOverride, prerenderSecret) {
|
|
|
557
565
|
const url = new URL(urlOverride ?? req.url ?? "/", origin);
|
|
558
566
|
const rawHeaders = nodeHeadersToWebHeaders(req.headers);
|
|
559
567
|
const prerenderRouteParamsPayload = readTrustedPrerenderRouteParamsFromHeaders(rawHeaders, prerenderSecret);
|
|
568
|
+
const isTrustedSpeculativePrerender = process.env.VINEXT_PRERENDER === "1" && prerenderSecret !== void 0 && rawHeaders.get("x-vinext-prerender-secret") === prerenderSecret && rawHeaders.get("x-vinext-prerender-speculative") === "1";
|
|
560
569
|
const headers = filterInternalHeaders(rawHeaders);
|
|
561
570
|
const prerenderRouteParamsHeader = serializePrerenderRouteParamsHeader(prerenderRouteParamsPayload);
|
|
562
571
|
if (prerenderRouteParamsHeader !== null) headers.set(VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, prerenderRouteParamsHeader);
|
|
572
|
+
if (isTrustedSpeculativePrerender) headers.set(VINEXT_PRERENDER_SPECULATIVE_HEADER, "1");
|
|
563
573
|
const method = req.method ?? "GET";
|
|
564
574
|
const hasBody = method !== "GET" && method !== "HEAD";
|
|
565
575
|
const init = {
|
|
@@ -622,12 +632,12 @@ async function sendWebResponse(webResponse, req, res, compress) {
|
|
|
622
632
|
*/
|
|
623
633
|
async function startProdServer(options = {}) {
|
|
624
634
|
installSocketErrorBackstop();
|
|
625
|
-
const { port = process.env.PORT ? parseInt(process.env.PORT) : 3e3, host = "0.0.0.0", outDir = path.resolve("dist"), noCompression = false, purpose } = options;
|
|
635
|
+
const { port = process.env.PORT ? parseInt(process.env.PORT) : 3e3, host = "0.0.0.0", outDir = path.resolve("dist"), rscEntryPath: explicitRscEntryPath, serverEntryPath: explicitServerEntryPath, noCompression = false, purpose, silent = false } = options;
|
|
626
636
|
const compress = !noCompression;
|
|
627
637
|
const resolvedOutDir = path.resolve(outDir);
|
|
628
638
|
const clientDir = path.join(resolvedOutDir, "client");
|
|
629
|
-
const rscEntryPath = path.join(resolvedOutDir, "server", "index.js");
|
|
630
|
-
const serverEntryPath = path.join(resolvedOutDir, "server", "entry.js");
|
|
639
|
+
const rscEntryPath = explicitRscEntryPath ? path.resolve(explicitRscEntryPath) : path.join(resolvedOutDir, "server", "index.js");
|
|
640
|
+
const serverEntryPath = explicitServerEntryPath ? path.resolve(explicitServerEntryPath) : path.join(resolvedOutDir, "server", "entry.js");
|
|
631
641
|
const isAppRouter = fs.existsSync(rscEntryPath);
|
|
632
642
|
if (!isAppRouter && !fs.existsSync(serverEntryPath)) {
|
|
633
643
|
console.error(`[vinext] No build output found in ${outDir}`);
|
|
@@ -640,7 +650,8 @@ async function startProdServer(options = {}) {
|
|
|
640
650
|
clientDir,
|
|
641
651
|
rscEntryPath,
|
|
642
652
|
compress,
|
|
643
|
-
purpose
|
|
653
|
+
purpose,
|
|
654
|
+
silent
|
|
644
655
|
});
|
|
645
656
|
return startPagesRouterServer({
|
|
646
657
|
port,
|
|
@@ -648,7 +659,8 @@ async function startProdServer(options = {}) {
|
|
|
648
659
|
clientDir,
|
|
649
660
|
serverEntryPath,
|
|
650
661
|
compress,
|
|
651
|
-
purpose
|
|
662
|
+
purpose,
|
|
663
|
+
silent
|
|
652
664
|
});
|
|
653
665
|
}
|
|
654
666
|
function createNodeExecutionContext() {
|
|
@@ -776,7 +788,7 @@ function installPagesClientAssets(options) {
|
|
|
776
788
|
* 4. Stream the Web Response back (with optional compression)
|
|
777
789
|
*/
|
|
778
790
|
async function startAppRouterServer(options) {
|
|
779
|
-
const { port, host, clientDir, rscEntryPath, compress, purpose } = options;
|
|
791
|
+
const { port, host, clientDir, rscEntryPath, compress, purpose, silent } = options;
|
|
780
792
|
const prerenderSecret = readPrerenderSecret(path.dirname(rscEntryPath));
|
|
781
793
|
const rscModule = await importServerEntryModule(rscEntryPath);
|
|
782
794
|
const rscHandler = resolveAppRouterHandler(rscModule.default);
|
|
@@ -899,7 +911,8 @@ async function startAppRouterServer(options) {
|
|
|
899
911
|
await new Promise((resolve) => {
|
|
900
912
|
server.listen(port, host, () => {
|
|
901
913
|
const addr = server.address();
|
|
902
|
-
|
|
914
|
+
const actualPort = typeof addr === "object" && addr ? addr.port : port;
|
|
915
|
+
if (!silent) logProdServerStarted(host, actualPort, purpose);
|
|
903
916
|
resolve();
|
|
904
917
|
});
|
|
905
918
|
});
|
|
@@ -930,7 +943,7 @@ function readPagesServerEntryPageRoutes(value) {
|
|
|
930
943
|
* - vinextConfig — embedded next.config.js settings
|
|
931
944
|
*/
|
|
932
945
|
async function startPagesRouterServer(options) {
|
|
933
|
-
const { port, host, clientDir, serverEntryPath, compress, purpose } = options;
|
|
946
|
+
const { port, host, clientDir, serverEntryPath, compress, purpose, silent } = options;
|
|
934
947
|
const serverEntry = await importServerEntryModule(serverEntryPath);
|
|
935
948
|
const { renderPage, handleApiRoute: handleApi, runMiddleware, vinextConfig, buildId: pagesBuildId } = serverEntry;
|
|
936
949
|
const matchPageRoute = typeof serverEntry.matchPageRoute === "function" ? serverEntry.matchPageRoute : void 0;
|
|
@@ -1062,8 +1075,9 @@ async function startPagesRouterServer(options) {
|
|
|
1062
1075
|
}
|
|
1063
1076
|
isDataReq = true;
|
|
1064
1077
|
const qs = url.includes("?") ? url.slice(url.indexOf("?")) : "";
|
|
1065
|
-
|
|
1066
|
-
|
|
1078
|
+
const pagePathname = normalizeNextDataPagePathname(dataMatch.pagePathname, hasMiddleware && trailingSlash);
|
|
1079
|
+
url = pagePathname + qs;
|
|
1080
|
+
pathname = pagePathname;
|
|
1067
1081
|
}
|
|
1068
1082
|
const protocol = resolveRequestProtocol(req);
|
|
1069
1083
|
const hostHeader = resolveRequestHost(req, `${host}:${port}`);
|
|
@@ -1144,7 +1158,8 @@ async function startPagesRouterServer(options) {
|
|
|
1144
1158
|
await new Promise((resolve) => {
|
|
1145
1159
|
server.listen(port, host, () => {
|
|
1146
1160
|
const addr = server.address();
|
|
1147
|
-
|
|
1161
|
+
const actualPort = typeof addr === "object" && addr ? addr.port : port;
|
|
1162
|
+
if (!silent) logProdServerStarted(host, actualPort, purpose);
|
|
1148
1163
|
resolve();
|
|
1149
1164
|
});
|
|
1150
1165
|
});
|
|
@@ -1155,4 +1170,4 @@ async function startPagesRouterServer(options) {
|
|
|
1155
1170
|
};
|
|
1156
1171
|
}
|
|
1157
1172
|
//#endregion
|
|
1158
|
-
export { COMPRESSIBLE_TYPES, COMPRESS_THRESHOLD, importServerEntryModule, mergeResponseHeaders, mergeWebResponse, negotiateEncoding, nodeToWebRequest, readNodeStream, resolveAppRouterAssetPath, resolveAppRouterPrerenderSeeder, resolveRequestHost as resolveHost, resolveServerEntryImportUrl, sendCompressed, sendWebResponse, startProdServer, trustProxy, trustedHosts, tryServeStatic };
|
|
1173
|
+
export { COMPRESSIBLE_TYPES, COMPRESS_THRESHOLD, importServerEntryModule, mergeResponseHeaders, mergeWebResponse, negotiateEncoding, nodeToWebRequest, readNodeStream, rememberCurrentServerEntryImportMtime, resolveAppRouterAssetPath, resolveAppRouterPrerenderSeeder, resolveRequestHost as resolveHost, resolveServerEntryImportUrl, sendCompressed, sendWebResponse, startProdServer, trustProxy, trustedHosts, tryServeStatic };
|
|
@@ -338,7 +338,7 @@ function isOriginAllowed(origin, allowed) {
|
|
|
338
338
|
*/
|
|
339
339
|
function processMiddlewareHeaders(headers) {
|
|
340
340
|
const keysToDelete = [];
|
|
341
|
-
for (const key of headers.keys()) if (key.startsWith("x-middleware-")) keysToDelete.push(key);
|
|
341
|
+
for (const key of headers.keys()) if (key.startsWith("x-middleware-") && key !== "x-middleware-cache") keysToDelete.push(key);
|
|
342
342
|
for (const key of keysToDelete) headers.delete(key);
|
|
343
343
|
}
|
|
344
344
|
const STRIPPED_INTERNAL_HEADERS = new Set([...INTERNAL_HEADERS, ...VINEXT_INTERNAL_HEADERS]);
|
|
@@ -21,7 +21,7 @@ const CONTENT_TYPES = {
|
|
|
21
21
|
".js": "application/javascript",
|
|
22
22
|
".mjs": "application/javascript",
|
|
23
23
|
".css": "text/css",
|
|
24
|
-
".html": "text/html",
|
|
24
|
+
".html": "text/html; charset=utf-8",
|
|
25
25
|
".json": "application/json",
|
|
26
26
|
".txt": "text/plain; charset=utf-8",
|
|
27
27
|
".png": "image/png",
|
|
@@ -42,6 +42,10 @@ function readStringArrayField(ctx, field) {
|
|
|
42
42
|
if (!Array.isArray(value)) return [];
|
|
43
43
|
return value.filter((item) => typeof item === "string");
|
|
44
44
|
}
|
|
45
|
+
function readPositiveNumberField(ctx, field) {
|
|
46
|
+
const value = ctx?.[field];
|
|
47
|
+
return typeof value === "number" && value > 0 ? value : void 0;
|
|
48
|
+
}
|
|
45
49
|
var MemoryCacheHandler = class {
|
|
46
50
|
store = /* @__PURE__ */ new Map();
|
|
47
51
|
tagRevalidatedAt = /* @__PURE__ */ new Map();
|
|
@@ -89,7 +93,10 @@ var MemoryCacheHandler = class {
|
|
|
89
93
|
return null;
|
|
90
94
|
}
|
|
91
95
|
this.touchEntry(key, entry);
|
|
92
|
-
|
|
96
|
+
const now = Date.now();
|
|
97
|
+
const requestedRevalidate = readPositiveNumberField(ctx, "revalidate");
|
|
98
|
+
const requestedRevalidateAt = requestedRevalidate === void 0 ? null : entry.lastModified + requestedRevalidate * 1e3;
|
|
99
|
+
if (entry.revalidateAt !== null && now > entry.revalidateAt || requestedRevalidateAt !== null && now > requestedRevalidateAt) return {
|
|
93
100
|
lastModified: entry.lastModified,
|
|
94
101
|
value: entry.value,
|
|
95
102
|
cacheState: "stale",
|
package/dist/shims/cache.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ACTION_DID_REVALIDATE_DYNAMIC_ONLY, ACTION_DID_REVALIDATE_STATIC_AND_DYNAMIC, ActionRevalidationKind, CacheLifeConfig, CacheState, UnstableCacheObservation, UnstableCacheRevalidationMode, _consumeRequestScopedCacheLife, _initRequestScopedCacheState, _peekRequestScopedCacheLife, _peekUnstableCacheObservations, _registerCacheContextAccessor, _runWithCacheState, _setRequestScopedCacheLife, cacheLifeProfiles, getAndClearActionRevalidationKind, getRegisteredCacheContext, markActionRevalidation, recordUnstableCacheObservation, shouldServeStaleUnstableCacheEntry } from "./cache-request-state.js";
|
|
2
|
-
import { CacheControlMetadata, CacheHandler, CacheHandlerContext, CacheHandlerValue, CachedAppPageValue, CachedFetchValue, CachedImageValue, CachedPagesValue, CachedRedirectValue, CachedRouteValue, IncrementalCacheValue, MemoryCacheHandler, NoOpCacheHandler, configureMemoryCacheHandler, getCacheHandler, getDataCacheHandler, setCacheHandler, setDataCacheHandler } from "./cache-handler.js";
|
|
3
2
|
import { ExecutionContextLike, getRequestExecutionContext, runWithExecutionContext } from "./request-context.js";
|
|
3
|
+
import { CacheControlMetadata, CacheHandler, CacheHandlerContext, CacheHandlerValue, CachedAppPageValue, CachedFetchValue, CachedImageValue, CachedPagesValue, CachedRedirectValue, CachedRouteValue, IncrementalCacheValue, MemoryCacheHandler, NoOpCacheHandler, configureMemoryCacheHandler, getCacheHandler, getDataCacheHandler, setCacheHandler, setDataCacheHandler } from "./cache-handler.js";
|
|
4
4
|
|
|
5
5
|
//#region src/shims/cache.d.ts
|
|
6
6
|
/**
|
package/dist/shims/cache.js
CHANGED
|
@@ -8,6 +8,7 @@ import { makeHangingPromise } from "./internal/make-hanging-promise.js";
|
|
|
8
8
|
import { getHeadersAccessPhase, isDraftModeEnabled, markDynamicUsage } from "./headers.js";
|
|
9
9
|
import { ACTION_DID_REVALIDATE_DYNAMIC_ONLY, ACTION_DID_REVALIDATE_STATIC_AND_DYNAMIC, _consumeRequestScopedCacheLife, _initRequestScopedCacheState, _peekRequestScopedCacheLife, _peekUnstableCacheObservations, _registerCacheContextAccessor, _runWithCacheState, _setRequestScopedCacheLife, cacheLifeProfiles, getAndClearActionRevalidationKind, getRegisteredCacheContext, markActionRevalidation, recordUnstableCacheObservation, shouldServeStaleUnstableCacheEntry } from "./cache-request-state.js";
|
|
10
10
|
import { encodeCacheTag, encodeCacheTags } from "../utils/encode-cache-tag.js";
|
|
11
|
+
import { addCollectedRequestTags } from "./fetch-cache.js";
|
|
11
12
|
import { workUnitAsyncStorage } from "./internal/work-unit-async-storage.js";
|
|
12
13
|
//#region src/shims/cache.ts
|
|
13
14
|
/**
|
|
@@ -124,6 +125,7 @@ function updateTag(tag) {
|
|
|
124
125
|
* It's provided for API compatibility so apps importing it don't break.
|
|
125
126
|
*/
|
|
126
127
|
function unstable_noStore() {
|
|
128
|
+
if (isInsideUnstableCacheScope()) return;
|
|
127
129
|
markDynamicUsage();
|
|
128
130
|
}
|
|
129
131
|
/**
|
|
@@ -373,6 +375,7 @@ function unstable_cache(fn, keyParts, options) {
|
|
|
373
375
|
const revalidateSeconds = options?.revalidate;
|
|
374
376
|
const cachedFn = async (...args) => {
|
|
375
377
|
const cacheKey = `unstable_cache:${baseKey}:${JSON.stringify(args)}`;
|
|
378
|
+
addCollectedRequestTags(tags);
|
|
376
379
|
recordUnstableCacheObservation({
|
|
377
380
|
kind: "unstable_cache",
|
|
378
381
|
keyHash: fnv1a64(cacheKey),
|
|
@@ -112,13 +112,8 @@ declare class DefaultCdnCacheAdapter implements CdnCacheAdapter {
|
|
|
112
112
|
*/
|
|
113
113
|
declare function setCdnCacheAdapter(adapter: CdnCacheAdapter): void;
|
|
114
114
|
/**
|
|
115
|
-
* Get the active CDN cache adapter.
|
|
116
|
-
*
|
|
117
|
-
* {@link DefaultCdnCacheAdapter}.
|
|
118
|
-
*
|
|
119
|
-
* Auto-detection of the Cloudflare Workers Cache (`ctx.cache`) only runs when
|
|
120
|
-
* `VINEXT_CDN_CACHE_AUTO_DETECT === "1"`; otherwise the default adapter is used
|
|
121
|
-
* unless an adapter was set explicitly.
|
|
115
|
+
* Get the active CDN cache adapter. An explicitly configured adapter wins;
|
|
116
|
+
* otherwise the origin-managed {@link DefaultCdnCacheAdapter} is used.
|
|
122
117
|
*/
|
|
123
118
|
declare function getCdnCacheAdapter(): CdnCacheAdapter;
|
|
124
119
|
//#endregion
|
package/dist/shims/cdn-cache.js
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import { getRequestExecutionContext } from "./request-context.js";
|
|
2
1
|
import { getDataCacheHandler } from "./cache-handler.js";
|
|
3
|
-
import { CloudflareCdnCacheAdapter } from "../packages/cloudflare/src/cache/cdn-adapter.runtime.js";
|
|
4
2
|
//#region src/shims/cdn-cache.ts
|
|
5
3
|
/**
|
|
6
4
|
* CDN cache adapter — owns the *page-level ISR serving strategy*.
|
|
@@ -78,22 +76,12 @@ function setCdnCacheAdapter(adapter) {
|
|
|
78
76
|
_gCdn[_CDN_KEY] = adapter;
|
|
79
77
|
}
|
|
80
78
|
/**
|
|
81
|
-
* Get the active CDN cache adapter.
|
|
82
|
-
*
|
|
83
|
-
* {@link DefaultCdnCacheAdapter}.
|
|
84
|
-
*
|
|
85
|
-
* Auto-detection of the Cloudflare Workers Cache (`ctx.cache`) only runs when
|
|
86
|
-
* `VINEXT_CDN_CACHE_AUTO_DETECT === "1"`; otherwise the default adapter is used
|
|
87
|
-
* unless an adapter was set explicitly.
|
|
79
|
+
* Get the active CDN cache adapter. An explicitly configured adapter wins;
|
|
80
|
+
* otherwise the origin-managed {@link DefaultCdnCacheAdapter} is used.
|
|
88
81
|
*/
|
|
89
82
|
function getCdnCacheAdapter() {
|
|
90
83
|
const active = _gCdn[_CDN_KEY];
|
|
91
84
|
if (active) return active;
|
|
92
|
-
if (process.env.VINEXT_CDN_CACHE_AUTO_DETECT === "1" && getRequestExecutionContext()?.cache) {
|
|
93
|
-
const edge = new CloudflareCdnCacheAdapter();
|
|
94
|
-
_gCdn[_CDN_KEY] = edge;
|
|
95
|
-
return edge;
|
|
96
|
-
}
|
|
97
85
|
return _defaultAdapter ??= new DefaultCdnCacheAdapter();
|
|
98
86
|
}
|
|
99
87
|
//#endregion
|
|
@@ -42,6 +42,7 @@ type FetchCacheState = {
|
|
|
42
42
|
currentFetchCacheMode: FetchCacheMode | null;
|
|
43
43
|
currentForceDynamicFetchDefault: boolean;
|
|
44
44
|
dynamicFetchUrls: Set<string>;
|
|
45
|
+
refreshStaleFetchesInForeground: boolean;
|
|
45
46
|
isFetchDedupeActive: boolean;
|
|
46
47
|
currentFetchDedupeEntries: Map<string, FetchDedupeEntry[]>;
|
|
47
48
|
};
|
|
@@ -86,6 +87,7 @@ declare function setCurrentFetchSoftTags(tags: string[]): void;
|
|
|
86
87
|
declare function getCurrentFetchSoftTags(): string[];
|
|
87
88
|
declare function setCurrentFetchCacheMode(mode: FetchCacheMode | null): void;
|
|
88
89
|
declare function setCurrentForceDynamicFetchDefault(enabled: boolean): void;
|
|
90
|
+
declare function setRefreshStaleFetchesInForeground(enabled: boolean): void;
|
|
89
91
|
/**
|
|
90
92
|
* Install the patched fetch and reset per-request tag state.
|
|
91
93
|
* Returns a cleanup function that clears tags.
|
|
@@ -133,4 +135,4 @@ declare function ensureFetchPatch(): void;
|
|
|
133
135
|
*/
|
|
134
136
|
declare function getOriginalFetch(): typeof globalThis.fetch;
|
|
135
137
|
//#endregion
|
|
136
|
-
export { FetchCacheMode, FetchCacheState, _resetPendingRefetches, addCollectedRequestTags, consumeDynamicFetchObservations, ensureFetchPatch, getCollectedFetchTags, getCurrentFetchSoftTags, getOriginalFetch, peekCacheableFetchObservations, peekDynamicFetchObservations, runWithFetchCache, runWithFetchDedupe, setCurrentFetchCacheMode, setCurrentFetchSoftTags, setCurrentForceDynamicFetchDefault, withFetchCache };
|
|
138
|
+
export { FetchCacheMode, FetchCacheState, _resetPendingRefetches, addCollectedRequestTags, consumeDynamicFetchObservations, ensureFetchPatch, getCollectedFetchTags, getCurrentFetchSoftTags, getOriginalFetch, peekCacheableFetchObservations, peekDynamicFetchObservations, runWithFetchCache, runWithFetchDedupe, setCurrentFetchCacheMode, setCurrentFetchSoftTags, setCurrentForceDynamicFetchDefault, setRefreshStaleFetchesInForeground, withFetchCache };
|