vinext 1.4.2 → 1.4.3
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 +4 -1
- package/dist/build/prerender-paths.d.ts +11 -1
- package/dist/build/prerender-paths.js +36 -10
- package/dist/cli.js +1 -1
- package/dist/client/dev-error-overlay.d.ts +2 -1
- package/dist/client/dev-error-overlay.js +8 -3
- package/dist/config/config-matchers.js +6 -2
- package/dist/config/next-config.d.ts +9 -0
- package/dist/entries/app-rsc-entry.js +24 -12
- package/dist/entries/pages-server-entry.js +17 -4
- package/dist/index.js +22 -25
- package/dist/init-platform.js +2 -2
- package/dist/init.js +42 -5
- package/dist/server/api-handler.js +7 -3
- package/dist/server/app-browser-entry.js +16 -37
- package/dist/server/app-browser-error.d.ts +1 -1
- package/dist/server/app-browser-error.js +13 -4
- package/dist/server/app-browser-supplemental-refresh.d.ts +13 -0
- package/dist/server/app-browser-supplemental-refresh.js +36 -4
- package/dist/server/app-middleware.d.ts +2 -0
- package/dist/server/app-middleware.js +1 -0
- package/dist/server/app-page-boundary-render.js +2 -0
- package/dist/server/app-page-dispatch.d.ts +1 -0
- package/dist/server/app-page-dispatch.js +5 -1
- package/dist/server/app-page-element-builder.d.ts +2 -0
- package/dist/server/app-page-element-builder.js +2 -1
- package/dist/server/app-page-route-wiring.d.ts +7 -5
- package/dist/server/app-page-route-wiring.js +3 -1
- package/dist/server/app-pages-bridge.d.ts +6 -0
- package/dist/server/app-pages-bridge.js +8 -3
- package/dist/server/app-request-stage-independent-entry.js +2 -3
- package/dist/server/app-router-entry.js +2 -3
- package/dist/server/app-rsc-cache-busting.d.ts +8 -0
- package/dist/server/app-rsc-cache-busting.js +13 -1
- package/dist/server/app-rsc-combined-handler.js +2 -1
- package/dist/server/app-rsc-handler.d.ts +4 -0
- package/dist/server/app-rsc-handler.js +39 -22
- package/dist/server/app-rsc-response-finalizer.d.ts +15 -3
- package/dist/server/app-rsc-response-finalizer.js +51 -7
- package/dist/server/app-ssr-entry.js +8 -0
- package/dist/server/config-headers.d.ts +7 -0
- package/dist/server/config-headers.js +20 -1
- package/dist/server/image-animation.d.ts +9 -0
- package/dist/server/image-animation.js +118 -0
- package/dist/server/image-optimization.d.ts +25 -5
- package/dist/server/image-optimization.js +305 -21
- package/dist/server/image-source-reader.d.ts +34 -0
- package/dist/server/image-source-reader.js +126 -0
- package/dist/server/metadata-route-response.d.ts +6 -1
- package/dist/server/metadata-route-response.js +22 -4
- package/dist/server/middleware-matcher.d.ts +1 -0
- package/dist/server/middleware-matcher.js +1 -1
- package/dist/server/middleware-runtime.d.ts +5 -0
- package/dist/server/middleware-runtime.js +48 -15
- package/dist/server/middleware.d.ts +1 -1
- package/dist/server/middleware.js +2 -1
- package/dist/server/nitro-request-target-plugin.d.ts +17 -0
- package/dist/server/nitro-request-target-plugin.js +22 -0
- package/dist/server/open-redirect.d.ts +2 -1
- package/dist/server/open-redirect.js +2 -1
- package/dist/server/pages-client-assets.d.ts +1 -0
- package/dist/server/pages-i18n.d.ts +7 -0
- package/dist/server/pages-i18n.js +14 -1
- package/dist/server/pages-node-compat.js +7 -3
- package/dist/server/pages-page-handler.js +4 -4
- package/dist/server/pages-request-pipeline.d.ts +9 -0
- package/dist/server/pages-request-pipeline.js +29 -10
- package/dist/server/pages-request-stage-entry.js +7 -2
- package/dist/server/pages-router-entry.js +3 -0
- package/dist/server/prod-server.d.ts +1 -1
- package/dist/server/prod-server.js +48 -54
- package/dist/server/srvx-middleware-origin.d.ts +13 -0
- package/dist/server/srvx-middleware-origin.js +19 -0
- package/dist/server/srvx-request-target.d.ts +12 -0
- package/dist/server/srvx-request-target.js +20 -0
- package/dist/server/static-file-cache.js +10 -22
- package/dist/server/worker-image-optimization.d.ts +8 -1
- package/dist/server/worker-image-optimization.js +25 -6
- package/dist/server/worker-revalidation-context.js +1 -0
- package/dist/shims/error-boundary.d.ts +6 -2
- package/dist/shims/error-boundary.js +4 -2
- package/dist/shims/request-context.d.ts +3 -1
- package/dist/shims/url-utils.js +6 -1
- package/dist/utils/client-entry-manifest.d.ts +5 -0
- package/dist/utils/client-entry-manifest.js +1 -0
- package/dist/utils/client-runtime-metadata.d.ts +5 -0
- package/dist/utils/client-runtime-metadata.js +33 -7
- package/package.json +1 -1
|
@@ -7,7 +7,7 @@ import { notFoundResponse } from "./http-error-responses.js";
|
|
|
7
7
|
import { getRepeatedSlashRedirect, isOpenRedirectShaped, sendRepeatedSlashRedirect } from "./open-redirect.js";
|
|
8
8
|
import { readStaticFileSignal } from "./static-file-signal.js";
|
|
9
9
|
import { canonicalizeRequestPathname, filterInternalHeaders } from "./request-pipeline.js";
|
|
10
|
-
import { ASSET_PREFIX_URL_DIR, assetPrefixPathname, isAbsoluteAssetPrefix
|
|
10
|
+
import { ASSET_PREFIX_URL_DIR, assetPrefixPathname, isAbsoluteAssetPrefix } from "../utils/asset-prefix.js";
|
|
11
11
|
import { StaticFileCache, contentTypeForPath, etagFromFilenameHash, staticFileCacheControl } from "./static-file-cache.js";
|
|
12
12
|
import { normalizePathnameForRouteMatchStrict } from "../routing/utils.js";
|
|
13
13
|
import { resolveRequestHost, resolveRequestProtocol, resolveTrustedForwardedHost, trustProxy, trustedHosts } from "./proxy-trust.js";
|
|
@@ -19,13 +19,13 @@ import { parseHttpDate } from "./http-date.js";
|
|
|
19
19
|
import { evaluateStaticPreconditions } from "./http-conditional.js";
|
|
20
20
|
import { setPagesClientAssets } from "./pages-client-assets.js";
|
|
21
21
|
import { signalFromNodeResponse } from "./node-response-signal.js";
|
|
22
|
-
import { DEFAULT_DEVICE_SIZES, DEFAULT_IMAGE_SIZES, IMAGE_RESPONSE_CACHE_STATE,
|
|
22
|
+
import { DEFAULT_DEVICE_SIZES, DEFAULT_IMAGE_SIZES, IMAGE_RESPONSE_CACHE_STATE, IMAGE_TYPE_NOT_ALLOWED_MESSAGE, INVALID_IMAGE_SOURCE_MESSAGE, isImageOptimizationPath, isImageSourceBodyStatus, isSafeImageContentType, parseImageParams, readImageOptimizationSignal, sourceImageCacheControl } from "./image-optimization.js";
|
|
23
23
|
import { installSocketErrorBackstop } from "./socket-error-backstop.js";
|
|
24
24
|
import { collectInlineCssManifest } from "../build/inline-css.js";
|
|
25
25
|
import { readTrustedRevalidationHostname } from "./revalidation-host.js";
|
|
26
26
|
import { traceFrameworkRequest } from "./request-tracing.js";
|
|
27
27
|
import { mergeHeaders } from "./worker-utils.js";
|
|
28
|
-
import { runPagesRequest, wrapMiddlewareWithBasePath } from "./pages-request-pipeline.js";
|
|
28
|
+
import { isServableFilesystemRoute, runPagesRequest, wrapMiddlewareWithBasePath } from "./pages-request-pipeline.js";
|
|
29
29
|
import { computeClientRuntimeMetadata } from "../utils/client-runtime-metadata.js";
|
|
30
30
|
import { readPrerenderSecret, readServerCompress } from "../build/server-manifest.js";
|
|
31
31
|
import { readTrustedPrerenderRouteParamsFromHeaders, serializePrerenderRouteParamsHeader } from "./prerender-route-params.js";
|
|
@@ -339,6 +339,7 @@ function installClientBuildManifestGlobals(clientDir, assetBase, assetPrefix, cr
|
|
|
339
339
|
});
|
|
340
340
|
setPagesClientAssets({
|
|
341
341
|
appBootstrapPreinitModules: metadata.appBootstrapPreinitModules,
|
|
342
|
+
appBootstrapPreloadModules: metadata.appBootstrapPreloadModules,
|
|
342
343
|
appRouteManifestModule: metadata.appRouteManifestModule,
|
|
343
344
|
lazyChunks: metadata.lazyChunks,
|
|
344
345
|
dynamicPreloads: metadata.dynamicPreloads,
|
|
@@ -444,7 +445,7 @@ function sendCompressed(req, res, body, contentType, statusCode, extraHeaders =
|
|
|
444
445
|
if (encoding !== "identity") {
|
|
445
446
|
writeHead(mergeVaryHeader({
|
|
446
447
|
...headersWithoutBodyHeaders,
|
|
447
|
-
"Content-Type": contentType,
|
|
448
|
+
...contentType !== void 0 && { "Content-Type": contentType },
|
|
448
449
|
"Content-Encoding": encoding
|
|
449
450
|
}, "Accept-Encoding"));
|
|
450
451
|
const compressor = createCompressor(encoding);
|
|
@@ -453,7 +454,7 @@ function sendCompressed(req, res, body, contentType, statusCode, extraHeaders =
|
|
|
453
454
|
} else {
|
|
454
455
|
const identityHeaders = {
|
|
455
456
|
...headersWithoutBodyHeaders,
|
|
456
|
-
"Content-Type": contentType,
|
|
457
|
+
...contentType !== void 0 && { "Content-Type": contentType },
|
|
457
458
|
"Content-Length": String(buf.length)
|
|
458
459
|
};
|
|
459
460
|
writeHead(varyByEncoding ? mergeVaryHeader(identityHeaders, "Accept-Encoding") : identityHeaders);
|
|
@@ -477,20 +478,18 @@ function staticCacheLookupPath(pathname) {
|
|
|
477
478
|
}
|
|
478
479
|
/**
|
|
479
480
|
* Give a Node image passthrough Next.js's image Cache-Control instead of the
|
|
480
|
-
* public-file default: a
|
|
481
|
-
*
|
|
481
|
+
* public-file default: a build asset keeps its immutable default, and a public
|
|
482
|
+
* file, whatever its path, is revalidated after `images.minimumCacheTTL`, or
|
|
483
|
+
* after the max-age a direct request for it gets (from middleware or
|
|
484
|
+
* `headers()`, else the public-file default) when that is longer.
|
|
482
485
|
*/
|
|
483
|
-
function withImageSourceCacheControl(headers,
|
|
484
|
-
if (
|
|
486
|
+
function withImageSourceCacheControl(headers, publicFileCacheControl, config) {
|
|
487
|
+
if (publicFileCacheControl === void 0) return headers;
|
|
485
488
|
return {
|
|
486
489
|
...headers,
|
|
487
|
-
"Cache-Control":
|
|
490
|
+
"Cache-Control": sourceImageCacheControl(false, publicFileCacheControl ?? staticFileCacheControl(false), config)
|
|
488
491
|
};
|
|
489
492
|
}
|
|
490
|
-
/** Pathnames the Pages Router serves from routes rather than the client directory. */
|
|
491
|
-
function isPagesFilesystemExcludedPath(pathname, phase) {
|
|
492
|
-
return pathname === "/" || pathname === "/api" || pathname.startsWith("/api/") || phase === "direct" && pathname.startsWith(`/_next/static/`);
|
|
493
|
-
}
|
|
494
493
|
/**
|
|
495
494
|
* Try to serve a static file from the client build directory.
|
|
496
495
|
*
|
|
@@ -783,32 +782,14 @@ function pipeStaticFileRange(filePath, range, res) {
|
|
|
783
782
|
}
|
|
784
783
|
});
|
|
785
784
|
}
|
|
786
|
-
/**
|
|
787
|
-
* Resolve the actual file to serve, trying extension-less HTML fallbacks.
|
|
788
|
-
* Returns the resolved path + size + mtime, or null if not found.
|
|
789
|
-
*/
|
|
785
|
+
/** Returns the file's path + size + mtime, or null if it is not a regular file. */
|
|
790
786
|
async function resolveStaticFile(staticFile) {
|
|
791
787
|
const stat = await statIfFile(staticFile);
|
|
792
|
-
|
|
788
|
+
return stat ? {
|
|
793
789
|
path: staticFile,
|
|
794
790
|
size: stat.size,
|
|
795
791
|
mtimeMs: stat.mtimeMs
|
|
796
|
-
};
|
|
797
|
-
const htmlFallback = staticFile + ".html";
|
|
798
|
-
const htmlStat = await statIfFile(htmlFallback);
|
|
799
|
-
if (htmlStat) return {
|
|
800
|
-
path: htmlFallback,
|
|
801
|
-
size: htmlStat.size,
|
|
802
|
-
mtimeMs: htmlStat.mtimeMs
|
|
803
|
-
};
|
|
804
|
-
const indexFallback = path.join(staticFile, "index.html");
|
|
805
|
-
const indexStat = await statIfFile(indexFallback);
|
|
806
|
-
if (indexStat) return {
|
|
807
|
-
path: indexFallback,
|
|
808
|
-
size: indexStat.size,
|
|
809
|
-
mtimeMs: indexStat.mtimeMs
|
|
810
|
-
};
|
|
811
|
-
return null;
|
|
792
|
+
} : null;
|
|
812
793
|
}
|
|
813
794
|
async function statIfFile(filePath) {
|
|
814
795
|
try {
|
|
@@ -1129,6 +1110,7 @@ function installPagesClientAssets(options) {
|
|
|
1129
1110
|
setPagesClientAssets({
|
|
1130
1111
|
clientEntry: metadata.clientEntryFile,
|
|
1131
1112
|
appBootstrapPreinitModules: metadata.appBootstrapPreinitModules,
|
|
1113
|
+
appBootstrapPreloadModules: metadata.appBootstrapPreloadModules,
|
|
1132
1114
|
appRouteManifestModule: metadata.appRouteManifestModule,
|
|
1133
1115
|
ssrManifest: Object.keys(ssrManifest).length > 0 ? ssrManifest : void 0,
|
|
1134
1116
|
sharedChunks: metadata.sharedChunks,
|
|
@@ -1214,7 +1196,7 @@ async function startAppRouterServer(options) {
|
|
|
1214
1196
|
return;
|
|
1215
1197
|
}
|
|
1216
1198
|
const sendUnsafeContentType = () => {
|
|
1217
|
-
sendBadRequest(
|
|
1199
|
+
sendBadRequest(IMAGE_TYPE_NOT_ALLOWED_MESSAGE);
|
|
1218
1200
|
};
|
|
1219
1201
|
const isSafeSourcePath = (sourcePath) => isSafeImageContentType(contentTypeForPath(sourcePath), imageConfig?.dangerouslyAllowSVG);
|
|
1220
1202
|
const imageSecurityHeaders = {
|
|
@@ -1222,7 +1204,7 @@ async function startAppRouterServer(options) {
|
|
|
1222
1204
|
"X-Content-Type-Options": "nosniff",
|
|
1223
1205
|
"Content-Disposition": imageConfig?.contentDispositionType === "attachment" ? "attachment" : "inline"
|
|
1224
1206
|
};
|
|
1225
|
-
const serveSource = (sourcePath) => tryServeStatic(req, res, clientDir, sourcePath, false, staticCache, withImageSourceCacheControl(imageSecurityHeaders,
|
|
1207
|
+
const serveSource = (sourcePath, publicFileCacheControl) => tryServeStatic(req, res, clientDir, sourcePath, false, staticCache, withImageSourceCacheControl(imageSecurityHeaders, publicFileCacheControl, imageConfig), void 0, IMAGE_REPRESENTATION_HEADERS);
|
|
1226
1208
|
const sourceUrl = new URL(params.imageUrl, request.url);
|
|
1227
1209
|
let sourcePathname = null;
|
|
1228
1210
|
try {
|
|
@@ -1248,7 +1230,7 @@ async function startAppRouterServer(options) {
|
|
|
1248
1230
|
sendUnsafeContentType();
|
|
1249
1231
|
return;
|
|
1250
1232
|
}
|
|
1251
|
-
if (await serveSource(sourcePath)) return;
|
|
1233
|
+
if (await serveSource(sourcePath, sourceResponse.headers.get("Cache-Control"))) return;
|
|
1252
1234
|
}
|
|
1253
1235
|
}
|
|
1254
1236
|
sendBadRequest(INVALID_IMAGE_SOURCE_MESSAGE);
|
|
@@ -1440,6 +1422,8 @@ async function createPagesRouterRequestHandler(options) {
|
|
|
1440
1422
|
const ensureInstrumentation = typeof serverEntry.__ensureInstrumentation === "function" ? () => serverEntry.__ensureInstrumentation() : () => void 0;
|
|
1441
1423
|
const hasMiddleware = serverEntry.hasMiddleware === true;
|
|
1442
1424
|
const pageRoutes = readPagesServerEntryPageRoutes(serverEntry.pageRoutes);
|
|
1425
|
+
const publicFiles = serverEntry.publicFiles instanceof Set ? serverEntry.publicFiles : /* @__PURE__ */ new Set();
|
|
1426
|
+
const clientBuildFiles = serverEntry.clientBuildFiles instanceof Set ? serverEntry.clientBuildFiles : /* @__PURE__ */ new Set();
|
|
1443
1427
|
const prerenderSecret = readPrerenderSecret(path.dirname(serverEntryPath));
|
|
1444
1428
|
const compress = compressOption && readServerCompress(serverDir);
|
|
1445
1429
|
const basePath = vinextConfig?.basePath ?? "";
|
|
@@ -1464,7 +1448,9 @@ async function createPagesRouterRequestHandler(options) {
|
|
|
1464
1448
|
qualities: vinextConfig.images.qualities,
|
|
1465
1449
|
localPatterns: vinextConfig.images.localPatterns,
|
|
1466
1450
|
contentDispositionType: vinextConfig.images.contentDispositionType,
|
|
1467
|
-
contentSecurityPolicy: vinextConfig.images.contentSecurityPolicy
|
|
1451
|
+
contentSecurityPolicy: vinextConfig.images.contentSecurityPolicy,
|
|
1452
|
+
minimumCacheTTL: vinextConfig.images.minimumCacheTTL,
|
|
1453
|
+
maximumResponseBody: vinextConfig.images.maximumResponseBody
|
|
1468
1454
|
} : void 0;
|
|
1469
1455
|
const ssrManifest = installPagesClientAssets({
|
|
1470
1456
|
clientDir,
|
|
@@ -1554,6 +1540,10 @@ async function createPagesRouterRequestHandler(options) {
|
|
|
1554
1540
|
if (pagesAssetLookup) {
|
|
1555
1541
|
if (await tryServeStatic(req, res, clientDir, pagesAssetLookup, compress, staticCache)) return;
|
|
1556
1542
|
}
|
|
1543
|
+
if (req.method === "GET" || req.method === "HEAD") {
|
|
1544
|
+
const clientBuildFile = resolveClientBuildFile(rawPagesPathname, basePath, clientBuildFiles);
|
|
1545
|
+
if (clientBuildFile && await tryServeStatic(req, res, clientDir, clientBuildFile, compress, staticCache)) return;
|
|
1546
|
+
}
|
|
1557
1547
|
try {
|
|
1558
1548
|
const hadBasePath = !basePath || hasBasePath(requestPathname, basePath);
|
|
1559
1549
|
const restoreLocaleRootTrailingSlash = shouldRestoreLocaleRootTrailingSlash(requestPathname, {
|
|
@@ -1624,6 +1614,7 @@ async function createPagesRouterRequestHandler(options) {
|
|
|
1624
1614
|
const middlewareRequest = isDataReq && skipProxyUrlNormalize ? new Request(new URL(originalRenderUrl, webRequest.url), webRequest) : void 0;
|
|
1625
1615
|
const middlewareCtx = createNodeExecutionContext(void 0, { middlewareOrigin: resolveNodeMiddlewareOrigin(req, webRequest.url, host, port) });
|
|
1626
1616
|
const runNodeMiddleware = typeof runMiddleware === "function" ? (request, _ctx, opts) => runMiddleware(request, middlewareCtx, opts) : null;
|
|
1617
|
+
/** The public file a source resolves to, with the Cache-Control staged for it. */
|
|
1627
1618
|
const resolvePublicImageSource = async (sourceUrl) => {
|
|
1628
1619
|
const rawSourcePathname = sourceUrl.pathname;
|
|
1629
1620
|
if (getRepeatedSlashRedirect(rawSourcePathname) || isOpenRedirectShaped(rawSourcePathname)) return null;
|
|
@@ -1631,7 +1622,7 @@ async function createPagesRouterRequestHandler(options) {
|
|
|
1631
1622
|
const sourceHadBasePath = !basePath || hasBasePath(sourceRequestPathname, basePath);
|
|
1632
1623
|
const strippedSourcePathname = stripBasePath(sourceRequestPathname, basePath);
|
|
1633
1624
|
if (isNextDataPathname(strippedSourcePathname)) return null;
|
|
1634
|
-
let
|
|
1625
|
+
let source = null;
|
|
1635
1626
|
const result = await runPagesRequest(new Request(new URL(strippedSourcePathname + sourceUrl.search, sourceUrl), { signal: webRequest.signal }), {
|
|
1636
1627
|
...deps,
|
|
1637
1628
|
hadBasePath: sourceHadBasePath,
|
|
@@ -1649,17 +1640,20 @@ async function createPagesRouterRequestHandler(options) {
|
|
|
1649
1640
|
runMiddleware: runNodeMiddleware ? wrapMiddlewareWithBasePath(runNodeMiddleware, basePath, sourceHadBasePath) : null,
|
|
1650
1641
|
renderPage: null,
|
|
1651
1642
|
handleApi: null,
|
|
1652
|
-
serveFilesystemRoute: async (candidate,
|
|
1653
|
-
if (
|
|
1643
|
+
serveFilesystemRoute: async (candidate, stagedHeaders, phase, _resolvedUrl, sourceRequest, candidateHasBasePath) => {
|
|
1644
|
+
if (isImageOptimizationPath(candidate) || !isServableFilesystemRoute(candidate, phase, sourceRequest.method, publicFiles, basePath, pagesAssetPathPrefix, candidateHasBasePath)) return false;
|
|
1654
1645
|
const lookupPath = staticCacheLookupPath(candidate);
|
|
1655
1646
|
if (lookupPath === null || lookupPath === "/" || !staticCache.lookup(lookupPath)) return false;
|
|
1656
|
-
|
|
1647
|
+
source = {
|
|
1648
|
+
path: candidate,
|
|
1649
|
+
cacheControl: readHeaderCaseInsensitive(stagedHeaders, "cache-control") ?? null
|
|
1650
|
+
};
|
|
1657
1651
|
return new Response(null);
|
|
1658
1652
|
}
|
|
1659
1653
|
});
|
|
1660
1654
|
if (result.type !== "response") return null;
|
|
1661
1655
|
cancelResponseBody(result.response);
|
|
1662
|
-
return isImageSourceBodyStatus(result.response.status) ?
|
|
1656
|
+
return isImageSourceBodyStatus(result.response.status) ? source : null;
|
|
1663
1657
|
};
|
|
1664
1658
|
const deps = {
|
|
1665
1659
|
assetPrefix,
|
|
@@ -1693,7 +1687,7 @@ async function createPagesRouterRequestHandler(options) {
|
|
|
1693
1687
|
return handleApi(request, apiUrl, createNodeExecutionContext(), resolveTrustedNodeRevalidateOrigin(req, host, port), "node", stagedHeaders);
|
|
1694
1688
|
} : null,
|
|
1695
1689
|
handleApiAppliesStagedHeaders: true,
|
|
1696
|
-
serveFilesystemRoute: async (requestPathname, stagedHeaders, phase, resolvedUrl,
|
|
1690
|
+
serveFilesystemRoute: async (requestPathname, stagedHeaders, phase, resolvedUrl, filesystemRequest, resolvedPathnameHasBasePath) => {
|
|
1697
1691
|
if (resolvedPathnameHasBasePath && isImageOptimizationPath(requestPathname)) {
|
|
1698
1692
|
for (const [name, value] of Object.entries(stagedHeaders)) res.setHeader(name, value);
|
|
1699
1693
|
const parsedUrl = new URL(resolvedUrl, "http://localhost");
|
|
@@ -1705,7 +1699,7 @@ async function createPagesRouterRequestHandler(options) {
|
|
|
1705
1699
|
}
|
|
1706
1700
|
const sendUnsafeContentType = () => {
|
|
1707
1701
|
res.writeHead(400);
|
|
1708
|
-
res.end(
|
|
1702
|
+
res.end(IMAGE_TYPE_NOT_ALLOWED_MESSAGE);
|
|
1709
1703
|
};
|
|
1710
1704
|
const isSafeSourcePath = (sourcePath) => isSafeImageContentType(contentTypeForPath(sourcePath), pagesImageConfig?.dangerouslyAllowSVG);
|
|
1711
1705
|
const imageSecurityHeaders = {
|
|
@@ -1713,7 +1707,7 @@ async function createPagesRouterRequestHandler(options) {
|
|
|
1713
1707
|
"X-Content-Type-Options": "nosniff",
|
|
1714
1708
|
"Content-Disposition": pagesImageConfig?.contentDispositionType === "attachment" ? "attachment" : "inline"
|
|
1715
1709
|
};
|
|
1716
|
-
const serveSource = (sourcePath) => tryServeStatic(req, res, clientDir, sourcePath, false, staticCache, withImageSourceCacheControl(imageSecurityHeaders,
|
|
1710
|
+
const serveSource = (sourcePath, publicFileCacheControl) => tryServeStatic(req, res, clientDir, sourcePath, false, staticCache, withImageSourceCacheControl(imageSecurityHeaders, publicFileCacheControl, pagesImageConfig), void 0, IMAGE_REPRESENTATION_HEADERS);
|
|
1717
1711
|
const sourceUrl = new URL(params.imageUrl, webRequest.url);
|
|
1718
1712
|
let sourcePathname = null;
|
|
1719
1713
|
try {
|
|
@@ -1723,22 +1717,22 @@ async function createPagesRouterRequestHandler(options) {
|
|
|
1723
1717
|
sendUnsafeContentType();
|
|
1724
1718
|
return true;
|
|
1725
1719
|
}
|
|
1726
|
-
const assetLookupPath = sourcePathname !== null ? resolveAppRouterAssetPath(sourcePathname, pagesAssetPathPrefix, assetPrefix) : null;
|
|
1720
|
+
const assetLookupPath = sourcePathname !== null ? resolveAppRouterAssetPath(sourcePathname, pagesAssetPathPrefix, assetPrefix) ?? resolveClientBuildFile(sourceUrl.pathname, basePath, clientBuildFiles) : null;
|
|
1727
1721
|
if (assetLookupPath && await serveSource(assetLookupPath)) return true;
|
|
1728
|
-
const
|
|
1722
|
+
const source = sourcePathname !== null ? await resolvePublicImageSource(sourceUrl) : null;
|
|
1729
1723
|
if (webRequest.signal.aborted) return true;
|
|
1730
|
-
if (
|
|
1731
|
-
if (!isSafeSourcePath(staticCacheLookupPath(
|
|
1724
|
+
if (source !== null) {
|
|
1725
|
+
if (!isSafeSourcePath(staticCacheLookupPath(source.path) ?? source.path)) {
|
|
1732
1726
|
sendUnsafeContentType();
|
|
1733
1727
|
return true;
|
|
1734
1728
|
}
|
|
1735
|
-
if (await serveSource(
|
|
1729
|
+
if (await serveSource(source.path, source.cacheControl)) return true;
|
|
1736
1730
|
}
|
|
1737
1731
|
res.writeHead(400);
|
|
1738
1732
|
res.end(INVALID_IMAGE_SOURCE_MESSAGE);
|
|
1739
1733
|
return true;
|
|
1740
1734
|
}
|
|
1741
|
-
if (!
|
|
1735
|
+
if (!isServableFilesystemRoute(requestPathname, phase, filesystemRequest.method, publicFiles, basePath, pagesAssetPathPrefix, resolvedPathnameHasBasePath)) return false;
|
|
1742
1736
|
return tryServeStatic(req, res, clientDir, requestPathname, compress, staticCache, stagedHeaders);
|
|
1743
1737
|
}
|
|
1744
1738
|
};
|
|
@@ -1758,7 +1752,7 @@ async function createPagesRouterRequestHandler(options) {
|
|
|
1758
1752
|
}
|
|
1759
1753
|
const responseBody = await bufferResponseBodyUntilAborted(response, webRequest.signal);
|
|
1760
1754
|
if (!responseBody) return;
|
|
1761
|
-
const ct = response.headers.get("content-type") ?? result.defaultContentType;
|
|
1755
|
+
const ct = response.headers.get("content-type") ?? (responseBody.length > 0 ? result.defaultContentType : void 0);
|
|
1762
1756
|
const responseHeaders = {};
|
|
1763
1757
|
response.headers.forEach((v, k) => {
|
|
1764
1758
|
if (k === "set-cookie") return;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
//#region src/server/srvx-middleware-origin.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* The server-owned origin middleware runs against when srvx serves the
|
|
4
|
+
* request on Node. srvx builds `request.url` from the client's Host and
|
|
5
|
+
* `X-Forwarded-Proto` headers, which must not decide whether a rewrite to
|
|
6
|
+
* another host is external, so like the Node production server (and Next.js,
|
|
7
|
+
* which uses its hostname and port) this uses the socket's TLS state and
|
|
8
|
+
* local port, or a trusted `X-Forwarded-Proto` and `X-Forwarded-Host`.
|
|
9
|
+
* Undefined on other hosts, and on Vercel, which routes by hostname like
|
|
10
|
+
* Workers; they keep the request URL's origin.
|
|
11
|
+
*/
|
|
12
|
+
export declare function resolveSrvxMiddlewareOrigin(request: Request): string | undefined;
|
|
13
|
+
//#endregion
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { resolveRequestProtocol, resolveTrustedForwardedHost } from "./proxy-trust.js";
|
|
2
|
+
//#region src/server/srvx-middleware-origin.ts
|
|
3
|
+
/**
|
|
4
|
+
* The server-owned origin middleware runs against when srvx serves the
|
|
5
|
+
* request on Node. srvx builds `request.url` from the client's Host and
|
|
6
|
+
* `X-Forwarded-Proto` headers, which must not decide whether a rewrite to
|
|
7
|
+
* another host is external, so like the Node production server (and Next.js,
|
|
8
|
+
* which uses its hostname and port) this uses the socket's TLS state and
|
|
9
|
+
* local port, or a trusted `X-Forwarded-Proto` and `X-Forwarded-Host`.
|
|
10
|
+
* Undefined on other hosts, and on Vercel, which routes by hostname like
|
|
11
|
+
* Workers; they keep the request URL's origin.
|
|
12
|
+
*/
|
|
13
|
+
function resolveSrvxMiddlewareOrigin(request) {
|
|
14
|
+
const socket = request.runtime?.node?.req?.socket;
|
|
15
|
+
if (!socket || process.env.VERCEL) return void 0;
|
|
16
|
+
return `${socket.encrypted === true || resolveRequestProtocol(request.headers) === "https" ? "https:" : "http:"}//${resolveTrustedForwardedHost(request.headers) ?? (socket.localPort === void 0 ? "localhost" : `localhost:${socket.localPort}`)}`;
|
|
17
|
+
}
|
|
18
|
+
//#endregion
|
|
19
|
+
export { resolveSrvxMiddlewareOrigin };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
//#region src/server/srvx-request-target.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Whether srvx, serving the request on Node, got a request target that is not
|
|
4
|
+
* origin-form (`GET https://other.example/x`). srvx then takes the origin of
|
|
5
|
+
* `request.url` from the target, as RFC 9112 §3.2.2 asks of an origin server.
|
|
6
|
+
* `vinext start` 404s every such target (see `getRepeatedSlashRedirect`) and
|
|
7
|
+
* Next.js never routes one, so the request-based entries Nitro calls refuse
|
|
8
|
+
* them too. The raw target is the one `nitroRequestTargetPlugin` saved before
|
|
9
|
+
* h3 could rewrite `req.url`.
|
|
10
|
+
*/
|
|
11
|
+
export declare function isSrvxAbsoluteFormTarget(request: Request): boolean;
|
|
12
|
+
//#endregion
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { NITRO_REQUEST_TARGET_KEY } from "./nitro-request-target-plugin.js";
|
|
2
|
+
//#region src/server/srvx-request-target.ts
|
|
3
|
+
/**
|
|
4
|
+
* Whether srvx, serving the request on Node, got a request target that is not
|
|
5
|
+
* origin-form (`GET https://other.example/x`). srvx then takes the origin of
|
|
6
|
+
* `request.url` from the target, as RFC 9112 §3.2.2 asks of an origin server.
|
|
7
|
+
* `vinext start` 404s every such target (see `getRepeatedSlashRedirect`) and
|
|
8
|
+
* Next.js never routes one, so the request-based entries Nitro calls refuse
|
|
9
|
+
* them too. The raw target is the one `nitroRequestTargetPlugin` saved before
|
|
10
|
+
* h3 could rewrite `req.url`.
|
|
11
|
+
*/
|
|
12
|
+
function isSrvxAbsoluteFormTarget(request) {
|
|
13
|
+
const req = request.runtime?.node?.req;
|
|
14
|
+
if (!req) return false;
|
|
15
|
+
const saved = Reflect.get(req, Symbol.for(NITRO_REQUEST_TARGET_KEY));
|
|
16
|
+
const target = typeof saved === "string" ? saved : req.url;
|
|
17
|
+
return !!target && !target.startsWith("/");
|
|
18
|
+
}
|
|
19
|
+
//#endregion
|
|
20
|
+
export { isSrvxAbsoluteFormTarget };
|
|
@@ -29,9 +29,11 @@ const CONTENT_TYPES = {
|
|
|
29
29
|
".css": "text/css; charset=utf-8",
|
|
30
30
|
".html": "text/html; charset=utf-8",
|
|
31
31
|
".ico": "image/x-icon",
|
|
32
|
+
".icns": "image/x-icns",
|
|
32
33
|
".jpeg": "image/jpeg",
|
|
33
34
|
".jpg": "image/jpeg",
|
|
34
35
|
".json": "application/json; charset=utf-8",
|
|
36
|
+
".jxl": "image/jxl",
|
|
35
37
|
".map": "application/json; charset=utf-8",
|
|
36
38
|
".mp3": "audio/mpeg",
|
|
37
39
|
".mp4": "video/mp4",
|
|
@@ -148,30 +150,16 @@ var StaticFileCache = class StaticFileCache {
|
|
|
148
150
|
}
|
|
149
151
|
const pathname = "/" + relativePath;
|
|
150
152
|
entries.set(pathname, entry);
|
|
151
|
-
if (ext === ".html") {
|
|
152
|
-
if (relativePath.endsWith("/index.html")) {
|
|
153
|
-
const dirPath = "/" + relativePath.slice(0, -11);
|
|
154
|
-
if (dirPath !== "/") entries.set(dirPath, entry);
|
|
155
|
-
} else {
|
|
156
|
-
const withoutExt = "/" + relativePath.slice(0, -ext.length);
|
|
157
|
-
entries.set(withoutExt, entry);
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
153
|
}
|
|
161
154
|
const toBuffer = [];
|
|
162
|
-
const
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
entry.zst
|
|
171
|
-
]) {
|
|
172
|
-
if (!variant || variant.size > BUFFER_THRESHOLD) continue;
|
|
173
|
-
toBuffer.push(variant);
|
|
174
|
-
}
|
|
155
|
+
for (const entry of entries.values()) for (const variant of [
|
|
156
|
+
entry.original,
|
|
157
|
+
entry.br,
|
|
158
|
+
entry.gz,
|
|
159
|
+
entry.zst
|
|
160
|
+
]) {
|
|
161
|
+
if (!variant || variant.size > BUFFER_THRESHOLD) continue;
|
|
162
|
+
toBuffer.push(variant);
|
|
175
163
|
}
|
|
176
164
|
for (let i = 0; i < toBuffer.length; i += 64) await Promise.all(toBuffer.slice(i, i + 64).map(async (v) => {
|
|
177
165
|
v.buffer = await fsp.readFile(v.path);
|
|
@@ -19,10 +19,17 @@ export type WorkerImageSourceOptions = {
|
|
|
19
19
|
* served only when the application pipeline selects a public file for it.
|
|
20
20
|
*/
|
|
21
21
|
export declare function fetchWorkerImageSource(imageUrl: string, request: Request, options: WorkerImageSourceOptions): Promise<Response>;
|
|
22
|
+
/**
|
|
23
|
+
* Give a public-file source the Cache-Control middleware or `headers()` staged
|
|
24
|
+
* for it, which a direct request receives in place of the asset default and
|
|
25
|
+
* Next.js' internal source fetch reads.
|
|
26
|
+
*/
|
|
27
|
+
export declare function withStagedCacheControl(asset: Response, cacheControl: string | null): Response;
|
|
22
28
|
/**
|
|
23
29
|
* Return the public file a framework static-file signal selects, provided the
|
|
24
30
|
* pipeline answered with a body-bearing 2xx outcome. The file is fetched with middleware's
|
|
25
|
-
* request header overrides, as resolveStaticAssetSignal fetches it
|
|
31
|
+
* request header overrides, as resolveStaticAssetSignal fetches it, and keeps
|
|
32
|
+
* the Cache-Control the pipeline staged.
|
|
26
33
|
*/
|
|
27
34
|
export declare function fetchSignalledPublicFile(response: Response, sourceRequest: Request, assets: VinextAssetFetcher): Promise<Response> | null;
|
|
28
35
|
/**
|
|
@@ -2,7 +2,7 @@ import { VINEXT_PRERENDER_SPECIAL_ERROR_HEADER } from "./headers.js";
|
|
|
2
2
|
import { readStaticFileSignal, readStaticFileSignalRequestHeaders } from "./static-file-signal.js";
|
|
3
3
|
import { attachRequestCfMetadata, cloneRequestWithUrl } from "./request-pipeline.js";
|
|
4
4
|
import { isNextStaticPath } from "../utils/asset-prefix.js";
|
|
5
|
-
import { handleConfiguredImageOptimization, invalidImageSourceResponse, isImageSourceBodyStatus } from "./image-optimization.js";
|
|
5
|
+
import { handleConfiguredImageOptimization, invalidImageSourceResponse, isImageSourceBodyStatus, markImageBuildAsset } from "./image-optimization.js";
|
|
6
6
|
import { createStaticAssetRequest, mergeHeaders } from "./worker-utils.js";
|
|
7
7
|
import { resolveClientBuildFile } from "./client-build-files.js";
|
|
8
8
|
//#region src/server/worker-image-optimization.ts
|
|
@@ -53,28 +53,47 @@ async function fetchWorkerImageSource(imageUrl, request, options) {
|
|
|
53
53
|
} catch {
|
|
54
54
|
return invalidImageSourceResponse();
|
|
55
55
|
}
|
|
56
|
-
const
|
|
57
|
-
if (
|
|
56
|
+
const nextStaticFile = resolveNextStaticFile(decodedPathname, options);
|
|
57
|
+
if (nextStaticFile !== null) return markImageBuildAsset(await options.assets.fetch(new Request(new URL(nextStaticFile, request.url))));
|
|
58
|
+
const clientBuildFile = resolveClientBuildFile(sourceUrl.pathname, options.basePath, options.clientBuildFiles);
|
|
59
|
+
if (clientBuildFile !== null) return options.assets.fetch(new Request(new URL(clientBuildFile, request.url)));
|
|
58
60
|
const sourceResponse = await options.fetchPublicFile(attachRequestCfMetadata(new Request(sourceUrl, { signal: request.signal }), request));
|
|
59
61
|
if (sourceResponse && isImageSourceBodyStatus(sourceResponse.status)) return sourceResponse;
|
|
60
62
|
if (sourceResponse) cancelResponseBody(sourceResponse);
|
|
61
63
|
return invalidImageSourceResponse();
|
|
62
64
|
}
|
|
63
65
|
/**
|
|
66
|
+
* Give a public-file source the Cache-Control middleware or `headers()` staged
|
|
67
|
+
* for it, which a direct request receives in place of the asset default and
|
|
68
|
+
* Next.js' internal source fetch reads.
|
|
69
|
+
*/
|
|
70
|
+
function withStagedCacheControl(asset, cacheControl) {
|
|
71
|
+
if (cacheControl === null) return asset;
|
|
72
|
+
const headers = new Headers(asset.headers);
|
|
73
|
+
headers.set("Cache-Control", cacheControl);
|
|
74
|
+
return new Response(asset.body, {
|
|
75
|
+
status: asset.status,
|
|
76
|
+
statusText: asset.statusText,
|
|
77
|
+
headers
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
64
81
|
* Return the public file a framework static-file signal selects, provided the
|
|
65
82
|
* pipeline answered with a body-bearing 2xx outcome. The file is fetched with middleware's
|
|
66
|
-
* request header overrides, as resolveStaticAssetSignal fetches it
|
|
83
|
+
* request header overrides, as resolveStaticAssetSignal fetches it, and keeps
|
|
84
|
+
* the Cache-Control the pipeline staged.
|
|
67
85
|
*/
|
|
68
86
|
function fetchSignalledPublicFile(response, sourceRequest, assets) {
|
|
69
87
|
const signal = isImageSourceBodyStatus(response.status) ? readStaticFileSignal(response) : null;
|
|
70
88
|
const requestHeaders = readStaticFileSignalRequestHeaders(response);
|
|
89
|
+
const stagedCacheControl = response.headers.get("Cache-Control");
|
|
71
90
|
cancelResponseBody(response);
|
|
72
91
|
if (signal === null) return null;
|
|
73
92
|
let assetPath = signal;
|
|
74
93
|
try {
|
|
75
94
|
assetPath = decodeURIComponent(signal);
|
|
76
95
|
} catch {}
|
|
77
|
-
return Promise.resolve(assets.fetch(createStaticAssetRequest(assetPath, sourceRequest, requestHeaders)));
|
|
96
|
+
return Promise.resolve(assets.fetch(createStaticAssetRequest(assetPath, sourceRequest, requestHeaders))).then((asset) => withStagedCacheControl(asset, stagedCacheControl));
|
|
78
97
|
}
|
|
79
98
|
/**
|
|
80
99
|
* Serve the App Router image endpoint once the request handler admitted it
|
|
@@ -111,4 +130,4 @@ async function serveWorkerImageOptimization(request, signal, allowedWidths, imag
|
|
|
111
130
|
});
|
|
112
131
|
}
|
|
113
132
|
//#endregion
|
|
114
|
-
export { fetchSignalledPublicFile, fetchWorkerImageSource, serveWorkerImageOptimization };
|
|
133
|
+
export { fetchSignalledPublicFile, fetchWorkerImageSource, serveWorkerImageOptimization, withStagedCacheControl };
|
|
@@ -12,6 +12,7 @@ function deriveExecutionContext(base, dispatchPagesRevalidate, isInternalPagesRe
|
|
|
12
12
|
...base?.cache === void 0 ? {} : { cache: base.cache },
|
|
13
13
|
...base?.assets === void 0 ? {} : { assets: base.assets },
|
|
14
14
|
...base?.trustedRevalidateOrigin === void 0 ? {} : { trustedRevalidateOrigin: base.trustedRevalidateOrigin },
|
|
15
|
+
...base?.middlewareOrigin === void 0 ? {} : { middlewareOrigin: base.middlewareOrigin },
|
|
15
16
|
dispatchPagesRevalidate,
|
|
16
17
|
isInternalPagesRevalidation
|
|
17
18
|
};
|
|
@@ -6,6 +6,8 @@ export type ErrorBoundaryProps = {
|
|
|
6
6
|
reset: () => void;
|
|
7
7
|
}>;
|
|
8
8
|
children: React.ReactNode;
|
|
9
|
+
/** Whether this boundary renders global-error, at the root of the page. */
|
|
10
|
+
isGlobalErrorBoundary?: boolean;
|
|
9
11
|
resetKey?: string | null;
|
|
10
12
|
};
|
|
11
13
|
export type SerializedBoundaryError = {
|
|
@@ -38,6 +40,7 @@ type RedirectBoundaryProps = {
|
|
|
38
40
|
type ErrorBoundaryInnerProps = {
|
|
39
41
|
isImplicitRootErrorBoundary?: boolean;
|
|
40
42
|
pathname: string | null;
|
|
43
|
+
wrapsGlobalErrorBoundary?: boolean;
|
|
41
44
|
} & ErrorBoundaryProps;
|
|
42
45
|
export type ErrorBoundaryState = {
|
|
43
46
|
error: CapturedError | null;
|
|
@@ -66,13 +69,14 @@ export declare class ErrorBoundaryInner extends React.Component<ErrorBoundaryInn
|
|
|
66
69
|
reset: () => void;
|
|
67
70
|
render(): string | number | bigint | boolean | React.JSX.Element | Iterable<React.ReactNode> | Promise<string | number | bigint | boolean | Iterable<React.ReactNode> | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | React.ReactPortal | null | undefined> | null | undefined;
|
|
68
71
|
}
|
|
69
|
-
export declare function ErrorBoundary({ fallback, children, resetKey }: ErrorBoundaryProps): React.JSX.Element;
|
|
70
|
-
export declare function GlobalErrorBoundary({ fallback, children }: {
|
|
72
|
+
export declare function ErrorBoundary({ fallback, children, isGlobalErrorBoundary, resetKey }: ErrorBoundaryProps): React.JSX.Element;
|
|
73
|
+
export declare function GlobalErrorBoundary({ fallback, children, wrapsGlobalErrorBoundary }: {
|
|
71
74
|
fallback: React.ComponentType<{
|
|
72
75
|
error: unknown;
|
|
73
76
|
reset: () => void;
|
|
74
77
|
}>;
|
|
75
78
|
children: React.ReactNode;
|
|
79
|
+
wrapsGlobalErrorBoundary?: boolean;
|
|
76
80
|
}): React.JSX.Element;
|
|
77
81
|
type NotFoundBoundaryProps = {
|
|
78
82
|
fallback: React.ReactNode;
|
|
@@ -174,21 +174,23 @@ var ErrorBoundaryInner = class extends React.Component {
|
|
|
174
174
|
return this.props.children;
|
|
175
175
|
}
|
|
176
176
|
};
|
|
177
|
-
function ErrorBoundary({ fallback, children, resetKey }) {
|
|
177
|
+
function ErrorBoundary({ fallback, children, isGlobalErrorBoundary, resetKey }) {
|
|
178
178
|
const pathname = useErrorBoundaryPathname();
|
|
179
179
|
return /* @__PURE__ */ jsx(ErrorBoundaryInner, {
|
|
180
180
|
pathname,
|
|
181
181
|
resetKey,
|
|
182
182
|
fallback,
|
|
183
|
+
isGlobalErrorBoundary,
|
|
183
184
|
children
|
|
184
185
|
});
|
|
185
186
|
}
|
|
186
|
-
function GlobalErrorBoundary({ fallback, children }) {
|
|
187
|
+
function GlobalErrorBoundary({ fallback, children, wrapsGlobalErrorBoundary }) {
|
|
187
188
|
const pathname = useErrorBoundaryPathname();
|
|
188
189
|
return /* @__PURE__ */ jsx(ErrorBoundaryInner, {
|
|
189
190
|
pathname,
|
|
190
191
|
fallback,
|
|
191
192
|
isImplicitRootErrorBoundary: fallback === DefaultGlobalError,
|
|
193
|
+
wrapsGlobalErrorBoundary,
|
|
192
194
|
children
|
|
193
195
|
});
|
|
194
196
|
}
|
|
@@ -46,7 +46,9 @@ export type ExecutionContextLike = {
|
|
|
46
46
|
requestSignal?: AbortSignal;
|
|
47
47
|
/**
|
|
48
48
|
* Server-owned origin middleware runs against. Set by the Node production
|
|
49
|
-
* server
|
|
49
|
+
* server and by entries that srvx serves on Node (Nitro's Node presets,
|
|
50
|
+
* @vitejs/plugin-rsc's dev and preview handlers), whose request URL comes
|
|
51
|
+
* from the client's Host header. Without it
|
|
50
52
|
* middleware uses the request URL's origin, which Workers can trust because
|
|
51
53
|
* the platform routes by hostname.
|
|
52
54
|
*/
|
package/dist/shims/url-utils.js
CHANGED
|
@@ -115,12 +115,17 @@ function normalizePathTrailingSlash(path, trailingSlash) {
|
|
|
115
115
|
if (!path.startsWith("/") || path.startsWith("//") || process.env.__VINEXT_MANUAL_TRAILING_SLASH === "true") return path;
|
|
116
116
|
const { pathname, query, hash } = parsePath(path);
|
|
117
117
|
if (trailingSlash) {
|
|
118
|
-
if (
|
|
118
|
+
if (hasFileLikeLastSegment(pathname)) return `${removeRouteTrailingSlash(pathname)}${query}${hash}`;
|
|
119
119
|
if (pathname.endsWith("/")) return `${pathname}${query}${hash}`;
|
|
120
120
|
return `${pathname}/${query}${hash}`;
|
|
121
121
|
}
|
|
122
122
|
return `${removeRouteTrailingSlash(pathname)}${query}${hash}`;
|
|
123
123
|
}
|
|
124
|
+
function hasFileLikeLastSegment(pathname) {
|
|
125
|
+
const end = pathname.endsWith("/") ? pathname.length - 1 : pathname.length;
|
|
126
|
+
const dot = pathname.indexOf(".", pathname.lastIndexOf("/", end - 1) + 1);
|
|
127
|
+
return dot !== -1 && dot < end - 1;
|
|
128
|
+
}
|
|
124
129
|
/**
|
|
125
130
|
* Prepend basePath to a local path for browser URLs / fetches.
|
|
126
131
|
*/
|
|
@@ -11,6 +11,11 @@ export declare const VINEXT_CLIENT_REFERENCE_SOURCES = ".vite/vinext-client-refe
|
|
|
11
11
|
export type ClientEntryManifest = {
|
|
12
12
|
pagesClientEntry?: string;
|
|
13
13
|
appBrowserEntry?: string;
|
|
14
|
+
/**
|
|
15
|
+
* The chunk holding the App Router browser runtime, whose evaluation starts
|
|
16
|
+
* hydration. A user chunk group can move it out of `appBrowserEntry`.
|
|
17
|
+
*/
|
|
18
|
+
appBrowserRuntime?: string;
|
|
14
19
|
/** The App Router RouteManifest chunk, which the browser entry imports as it boots. */
|
|
15
20
|
appRouteManifest?: string;
|
|
16
21
|
/** What each App Router client reference loads in the browser. */
|
|
@@ -20,6 +20,7 @@ function readClientEntryManifest(clientDir) {
|
|
|
20
20
|
const manifest = {};
|
|
21
21
|
if (typeof value.pagesClientEntry === "string") manifest.pagesClientEntry = value.pagesClientEntry;
|
|
22
22
|
if (typeof value.appBrowserEntry === "string") manifest.appBrowserEntry = value.appBrowserEntry;
|
|
23
|
+
if (typeof value.appBrowserRuntime === "string") manifest.appBrowserRuntime = value.appBrowserRuntime;
|
|
23
24
|
if (typeof value.appRouteManifest === "string") manifest.appRouteManifest = value.appRouteManifest;
|
|
24
25
|
if (isUnknownRecord(value.appClientReferenceChunks)) manifest.appClientReferenceChunks = value.appClientReferenceChunks;
|
|
25
26
|
return manifest.pagesClientEntry || manifest.appBrowserEntry ? manifest : void 0;
|
|
@@ -3,6 +3,7 @@ import { ClientReferenceChunks } from "./client-reference-chunks.js";
|
|
|
3
3
|
type ClientRuntimeMetadata = {
|
|
4
4
|
clientEntryFile?: string;
|
|
5
5
|
appBootstrapPreinitModules?: string[];
|
|
6
|
+
appBootstrapPreloadModules?: string[];
|
|
6
7
|
appRouteManifestModule?: string;
|
|
7
8
|
appClientReferenceChunks?: ClientReferenceChunks;
|
|
8
9
|
cssGraph?: Record<string, {
|
|
@@ -17,6 +18,10 @@ type ClientRuntimeMetadata = {
|
|
|
17
18
|
* Read the client build manifest and compute runtime metadata used by
|
|
18
19
|
* Cloudflare worker entry injection and Node production server startup.
|
|
19
20
|
*
|
|
21
|
+
* - `appBootstrapPreinitModules` — chunks the App Router browser entry imports,
|
|
22
|
+
* preinitialized during SSR.
|
|
23
|
+
* - `appBootstrapPreloadModules` — those of them that would start hydration if
|
|
24
|
+
* evaluated ahead of the shell, preloaded during SSR instead.
|
|
20
25
|
* - `appRouteManifestModule` — the App Router RouteManifest chunk, which the
|
|
21
26
|
* browser entry imports as it boots, preloaded during SSR.
|
|
22
27
|
* - `appClientReferenceChunks` — what each App Router client reference loads
|