UNPKG

next

Version:

The React Framework

928 lines • 91.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); 0 && (module.exports = { NoFallbackError: null, WrappedBuildError: null, default: null }); function _export(target, all) { for(var name in all)Object.defineProperty(target, name, { enumerable: true, get: all[name] }); } _export(exports, { NoFallbackError: function() { return NoFallbackError; }, WrappedBuildError: function() { return WrappedBuildError; }, default: function() { return Server; } }); const _utils = require("../shared/lib/utils"); const _url = require("url"); const _redirectstatus = require("../lib/redirect-status"); const _isedgeruntime = require("../lib/is-edge-runtime"); const _constants = require("../shared/lib/constants"); const _utils1 = require("../shared/lib/router/utils"); const _apiutils = require("./api-utils"); const _runtimeconfig = require("../shared/lib/runtime-config"); const _revalidateheaders = require("./send-payload/revalidate-headers"); const _utils2 = require("./utils"); const _isbot = require("../shared/lib/router/utils/is-bot"); const _renderresult = /*#__PURE__*/ _interop_require_default(require("./render-result")); const _removetrailingslash = require("../shared/lib/router/utils/remove-trailing-slash"); const _denormalizepagepath = require("../shared/lib/page-path/denormalize-page-path"); const _log = /*#__PURE__*/ _interop_require_wildcard(require("../build/output/log")); const _escapepathdelimiters = /*#__PURE__*/ _interop_require_default(require("../shared/lib/router/utils/escape-path-delimiters")); const _serverutils = require("./server-utils"); const _iserror = /*#__PURE__*/ _interop_require_wildcard(require("../lib/is-error")); const _requestmeta = require("./request-meta"); const _removepathprefix = require("../shared/lib/router/utils/remove-path-prefix"); const _apppaths = require("../shared/lib/router/utils/app-paths"); const _gethostname = require("../shared/lib/get-hostname"); const _parseurl = require("../shared/lib/router/utils/parse-url"); const _getnextpathnameinfo = require("../shared/lib/router/utils/get-next-pathname-info"); const _approuterheaders = require("../client/components/app-router-headers"); const _localeroutenormalizer = require("./future/normalizers/locale-route-normalizer"); const _defaultroutematchermanager = require("./future/route-matcher-managers/default-route-matcher-manager"); const _apppageroutematcherprovider = require("./future/route-matcher-providers/app-page-route-matcher-provider"); const _approuteroutematcherprovider = require("./future/route-matcher-providers/app-route-route-matcher-provider"); const _pagesapiroutematcherprovider = require("./future/route-matcher-providers/pages-api-route-matcher-provider"); const _pagesroutematcherprovider = require("./future/route-matcher-providers/pages-route-matcher-provider"); const _servermanifestloader = require("./future/route-matcher-providers/helpers/manifest-loaders/server-manifest-loader"); const _tracer = require("./lib/trace/tracer"); const _constants1 = require("./lib/trace/constants"); const _i18nprovider = require("./future/helpers/i18n-provider"); const _sendresponse = require("./send-response"); const _routekind = require("./future/route-kind"); const _responsehandlers = require("./future/route-modules/helpers/response-handlers"); const _utils3 = require("./web/utils"); const _constants2 = require("../lib/constants"); const _normalizelocalepath = require("../shared/lib/i18n/normalize-locale-path"); const _nextrequest = require("./web/spec-extension/adapters/next-request"); function _interop_require_default(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } function _interop_require_wildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for(var key in obj){ if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } class NoFallbackError extends Error { } class WrappedBuildError extends Error { constructor(innerError){ super(); this.innerError = innerError; } } class Server { constructor(options){ var _this_nextConfig_i18n, _this_nextConfig_experimental_amp, _this_nextConfig_i18n1; this.prepared = false; this.preparedPromise = null; this.customErrorNo404Warn = (0, _utils.execOnce)(()=>{ _log.warn(`You have added a custom /_error page without a custom /404 page. This prevents the 404 page from being auto statically optimized.\nSee here for info: https://nextjs.org/docs/messages/custom-error-no-custom-404`); }); const { dir ="." , quiet =false , conf , dev =false , minimalMode =false , customServer =true , hostname , port } = options; this.serverOptions = options; this.isRenderWorker = options._renderWorker; this.dir = process.env.NEXT_RUNTIME === "edge" ? dir : require("path").resolve(dir); this.quiet = quiet; this.loadEnvConfig({ dev }); // TODO: should conf be normalized to prevent missing // values from causing issues as this can be user provided this.nextConfig = conf; this.hostname = hostname; this.port = port; this.distDir = process.env.NEXT_RUNTIME === "edge" ? this.nextConfig.distDir : require("path").join(this.dir, this.nextConfig.distDir); this.publicDir = this.getPublicDir(); this.hasStaticDir = !minimalMode && this.getHasStaticDir(); this.i18nProvider = ((_this_nextConfig_i18n = this.nextConfig.i18n) == null ? void 0 : _this_nextConfig_i18n.locales) ? new _i18nprovider.I18NProvider(this.nextConfig.i18n) : undefined; // Configure the locale normalizer, it's used for routes inside `pages/`. this.localeNormalizer = this.i18nProvider ? new _localeroutenormalizer.LocaleRouteNormalizer(this.i18nProvider) : undefined; // Only serverRuntimeConfig needs the default // publicRuntimeConfig gets it's default in client/index.js const { serverRuntimeConfig ={} , publicRuntimeConfig , assetPrefix , generateEtags } = this.nextConfig; this.buildId = this.getBuildId(); this.minimalMode = minimalMode || !!process.env.NEXT_PRIVATE_MINIMAL_MODE; this.hasAppDir = !!this.nextConfig.experimental.appDir && this.getHasAppDir(dev); const serverComponents = this.hasAppDir; this.nextFontManifest = this.getNextFontManifest(); if (process.env.NEXT_RUNTIME !== "edge") { if (this.nextConfig.experimental.deploymentId) { process.env.NEXT_DEPLOYMENT_ID = this.nextConfig.experimental.deploymentId; } } this.renderOpts = { deploymentId: this.nextConfig.experimental.deploymentId, strictNextHead: !!this.nextConfig.experimental.strictNextHead, poweredByHeader: this.nextConfig.poweredByHeader, canonicalBase: this.nextConfig.amp.canonicalBase || "", buildId: this.buildId, generateEtags, previewProps: this.getPrerenderManifest().preview, customServer: customServer === true ? true : undefined, ampOptimizerConfig: (_this_nextConfig_experimental_amp = this.nextConfig.experimental.amp) == null ? void 0 : _this_nextConfig_experimental_amp.optimizer, basePath: this.nextConfig.basePath, images: this.nextConfig.images, optimizeFonts: this.nextConfig.optimizeFonts, fontManifest: this.nextConfig.optimizeFonts && !dev ? this.getFontManifest() : undefined, optimizeCss: this.nextConfig.experimental.optimizeCss, nextConfigOutput: this.nextConfig.output, nextScriptWorkers: this.nextConfig.experimental.nextScriptWorkers, disableOptimizedLoading: this.nextConfig.experimental.disableOptimizedLoading, domainLocales: (_this_nextConfig_i18n1 = this.nextConfig.i18n) == null ? void 0 : _this_nextConfig_i18n1.domains, distDir: this.distDir, serverComponents, crossOrigin: this.nextConfig.crossOrigin ? this.nextConfig.crossOrigin : undefined, largePageDataBytes: this.nextConfig.experimental.largePageDataBytes, // Only the `publicRuntimeConfig` key is exposed to the client side // It'll be rendered as part of __NEXT_DATA__ on the client side runtimeConfig: Object.keys(publicRuntimeConfig).length > 0 ? publicRuntimeConfig : undefined }; // Initialize next/config with the environment configuration (0, _runtimeconfig.setConfig)({ serverRuntimeConfig, publicRuntimeConfig }); this.pagesManifest = this.getPagesManifest(); this.appPathsManifest = this.getAppPathsManifest(); this.appPathRoutes = this.getAppPathRoutes(); // Configure the routes. const { matchers } = this.getRoutes(); this.matchers = matchers; // Start route compilation. We don't wait for the routes to finish loading // because we use the `waitTillReady` promise below in `handleRequest` to // wait. Also we can't `await` in the constructor. matchers.reload(); this.setAssetPrefix(assetPrefix); this.responseCache = this.getResponseCache({ dev }); } async normalizeNextData(_req, _res, _parsedUrl) { return { finished: false }; } async handleNextImageRequest(_req, _res, _parsedUrl) { return { finished: false }; } async handleCatchallRenderRequest(_req, _res, _parsedUrl) { return { finished: false }; } async handleCatchallMiddlewareRequest(_req, _res, _parsedUrl) { return { finished: false }; } getRoutes() { // Create a new manifest loader that get's the manifests from the server. const manifestLoader = new _servermanifestloader.ServerManifestLoader((name)=>{ switch(name){ case _constants.PAGES_MANIFEST: return this.getPagesManifest() ?? null; case _constants.APP_PATHS_MANIFEST: return this.getAppPathsManifest() ?? null; default: return null; } }); // Configure the matchers and handlers. const matchers = new _defaultroutematchermanager.DefaultRouteMatcherManager(); // Match pages under `pages/`. matchers.push(new _pagesroutematcherprovider.PagesRouteMatcherProvider(this.distDir, manifestLoader, this.i18nProvider)); // Match api routes under `pages/api/`. matchers.push(new _pagesapiroutematcherprovider.PagesAPIRouteMatcherProvider(this.distDir, manifestLoader, this.i18nProvider)); // If the app directory is enabled, then add the app matchers and handlers. if (this.hasAppDir) { // Match app pages under `app/`. matchers.push(new _apppageroutematcherprovider.AppPageRouteMatcherProvider(this.distDir, manifestLoader)); matchers.push(new _approuteroutematcherprovider.AppRouteRouteMatcherProvider(this.distDir, manifestLoader)); } return { matchers }; } logError(err) { if (this.quiet) return; console.error(err); } async handleRequest(req, res, parsedUrl) { await this.prepare(); const method = req.method.toUpperCase(); return (0, _tracer.getTracer)().trace(_constants1.BaseServerSpan.handleRequest, { spanName: `${method} ${req.url}`, kind: _tracer.SpanKind.SERVER, attributes: { "http.method": method, "http.target": req.url } }, async (span)=>this.handleRequestImpl(req, res, parsedUrl).finally(()=>{ if (!span) return; span.setAttributes({ "http.status_code": res.statusCode }); const rootSpanAttributes = (0, _tracer.getTracer)().getRootSpanAttributes(); // We were unable to get attributes, probably OTEL is not enabled if (!rootSpanAttributes) return; if (rootSpanAttributes.get("next.span_type") !== _constants1.BaseServerSpan.handleRequest) { console.warn(`Unexpected root span type '${rootSpanAttributes.get("next.span_type")}'. Please report this Next.js issue https://github.com/vercel/next.js`); return; } const route = rootSpanAttributes.get("next.route"); if (route) { const newName = `${method} ${route}`; span.setAttributes({ "next.route": route, "http.route": route, "next.span_name": newName }); span.updateName(newName); } })); } async handleRequestImpl(req, res, parsedUrl) { try { var _this_i18nProvider, _this_nextConfig_i18n; // Wait for the matchers to be ready. await this.matchers.waitTillReady(); // ensure cookies set in middleware are merged and // not overridden by API routes/getServerSideProps const _res = res.originalResponse || res; const origSetHeader = _res.setHeader.bind(_res); _res.setHeader = (name, val)=>{ if (name.toLowerCase() === "set-cookie") { const middlewareValue = (0, _requestmeta.getRequestMeta)(req, "_nextMiddlewareCookie"); if (!middlewareValue || !Array.isArray(val) || !val.every((item, idx)=>item === middlewareValue[idx])) { val = [ // TODO: (wyattjoh) find out why this is called multiple times resulting in duplicate cookies being added ...new Set([ ...middlewareValue || [], ...typeof val === "string" ? [ val ] : Array.isArray(val) ? val : [] ]) ]; } } return origSetHeader(name, val); }; const urlParts = (req.url || "").split("?"); const urlNoQuery = urlParts[0]; // this normalizes repeated slashes in the path e.g. hello//world -> // hello/world or backslashes to forward slashes, this does not // handle trailing slash as that is handled the same as a next.config.js // redirect if (urlNoQuery == null ? void 0 : urlNoQuery.match(/(\\|\/\/)/)) { const cleanUrl = (0, _utils.normalizeRepeatedSlashes)(req.url); res.redirect(cleanUrl, 308).body(cleanUrl).send(); return; } (0, _apiutils.setLazyProp)({ req: req }, "cookies", (0, _apiutils.getCookieParser)(req.headers)); // Parse url if parsedUrl not provided if (!parsedUrl || typeof parsedUrl !== "object") { parsedUrl = (0, _url.parse)(req.url, true); } // Parse the querystring ourselves if the user doesn't handle querystring parsing if (typeof parsedUrl.query === "string") { parsedUrl.query = Object.fromEntries(new URLSearchParams(parsedUrl.query)); } // in minimal mode we detect RSC revalidate if the .rsc // path is requested if (this.minimalMode) { if (req.url.endsWith(".rsc")) { parsedUrl.query.__nextDataReq = "1"; } else if (req.headers["x-now-route-matches"]) { for (const param of _approuterheaders.FLIGHT_PARAMETERS){ delete req.headers[param.toString().toLowerCase()]; } } } req.url = (0, _apppaths.normalizeRscPath)(req.url, this.hasAppDir); parsedUrl.pathname = (0, _apppaths.normalizeRscPath)(parsedUrl.pathname || "", this.hasAppDir); this.attachRequestMeta(req, parsedUrl); const domainLocale = (_this_i18nProvider = this.i18nProvider) == null ? void 0 : _this_i18nProvider.detectDomainLocale((0, _gethostname.getHostname)(parsedUrl, req.headers)); const defaultLocale = (domainLocale == null ? void 0 : domainLocale.defaultLocale) || ((_this_nextConfig_i18n = this.nextConfig.i18n) == null ? void 0 : _this_nextConfig_i18n.defaultLocale); parsedUrl.query.__nextDefaultLocale = defaultLocale; const url = (0, _parseurl.parseUrl)(req.url.replace(/^\/+/, "/")); const pathnameInfo = (0, _getnextpathnameinfo.getNextPathnameInfo)(url.pathname, { nextConfig: this.nextConfig, i18nProvider: this.i18nProvider }); url.pathname = pathnameInfo.pathname; if (pathnameInfo.basePath) { req.url = (0, _removepathprefix.removePathPrefix)(req.url, this.nextConfig.basePath); (0, _requestmeta.addRequestMeta)(req, "_nextHadBasePath", true); } const useMatchedPathHeader = this.minimalMode && typeof req.headers["x-matched-path"] === "string"; // TODO: merge handling with x-invoke-path if (useMatchedPathHeader) { try { var _this_i18nProvider1, _this_getRoutesManifest; if (this.hasAppDir) { // ensure /index path is normalized for prerender // in minimal mode if (req.url.match(/^\/index($|\?)/)) { req.url = req.url.replace(/^\/index/, "/"); } parsedUrl.pathname = parsedUrl.pathname === "/index" ? "/" : parsedUrl.pathname; } // x-matched-path is the source of truth, it tells what page // should be rendered because we don't process rewrites in minimalMode let matchedPath = (0, _apppaths.normalizeRscPath)(new URL(req.headers["x-matched-path"], "http://localhost").pathname, this.hasAppDir); let urlPathname = new URL(req.url, "http://localhost").pathname; // For ISR the URL is normalized to the prerenderPath so if // it's a data request the URL path will be the data URL, // basePath is already stripped by this point if (urlPathname.startsWith(`/_next/data/`)) { parsedUrl.query.__nextDataReq = "1"; } const normalizedUrlPath = this.stripNextDataPath(urlPathname); matchedPath = this.stripNextDataPath(matchedPath, false); // Perform locale detection and normalization. const localeAnalysisResult = (_this_i18nProvider1 = this.i18nProvider) == null ? void 0 : _this_i18nProvider1.analyze(matchedPath, { defaultLocale }); // The locale result will be defined even if the locale was not // detected for the request because it will be inferred from the // default locale. if (localeAnalysisResult) { parsedUrl.query.__nextLocale = localeAnalysisResult.detectedLocale; // If the detected locale was inferred from the default locale, we // need to modify the metadata on the request to indicate that. if (localeAnalysisResult.inferredFromDefault) { parsedUrl.query.__nextInferredLocaleFromDefault = "1"; } else { delete parsedUrl.query.__nextInferredLocaleFromDefault; } } // TODO: check if this is needed any more? matchedPath = (0, _denormalizepagepath.denormalizePagePath)(matchedPath); let srcPathname = matchedPath; const match = await this.matchers.match(matchedPath, { i18n: localeAnalysisResult }); // Update the source pathname to the matched page's pathname. if (match) srcPathname = match.definition.pathname; // The page is dynamic if the params are defined. const pageIsDynamic = typeof (match == null ? void 0 : match.params) !== "undefined"; // The rest of this function can't handle i18n properly, so ensure we // restore the pathname with the locale information stripped from it // now that we're done matching if we're using i18n. if (localeAnalysisResult) { matchedPath = localeAnalysisResult.pathname; } const utils = (0, _serverutils.getUtils)({ pageIsDynamic, page: srcPathname, i18n: this.nextConfig.i18n, basePath: this.nextConfig.basePath, rewrites: ((_this_getRoutesManifest = this.getRoutesManifest()) == null ? void 0 : _this_getRoutesManifest.rewrites) || { beforeFiles: [], afterFiles: [], fallback: [] }, caseSensitive: !!this.nextConfig.experimental.caseSensitiveRoutes }); // Ensure parsedUrl.pathname includes locale before processing // rewrites or they won't match correctly. if (defaultLocale && !pathnameInfo.locale) { parsedUrl.pathname = `/${defaultLocale}${parsedUrl.pathname}`; } const pathnameBeforeRewrite = parsedUrl.pathname; const rewriteParams = utils.handleRewrites(req, parsedUrl); const rewriteParamKeys = Object.keys(rewriteParams); const didRewrite = pathnameBeforeRewrite !== parsedUrl.pathname; if (didRewrite) { (0, _requestmeta.addRequestMeta)(req, "_nextRewroteUrl", parsedUrl.pathname); (0, _requestmeta.addRequestMeta)(req, "_nextDidRewrite", true); } const routeParamKeys = new Set(); for (const key of Object.keys(parsedUrl.query)){ const value = parsedUrl.query[key]; if (key !== _constants2.NEXT_QUERY_PARAM_PREFIX && key.startsWith(_constants2.NEXT_QUERY_PARAM_PREFIX)) { const normalizedKey = key.substring(_constants2.NEXT_QUERY_PARAM_PREFIX.length); parsedUrl.query[normalizedKey] = value; routeParamKeys.add(normalizedKey); delete parsedUrl.query[key]; } } // interpolate dynamic params and normalize URL if needed if (pageIsDynamic) { let params = {}; let paramsResult = utils.normalizeDynamicRouteParams(parsedUrl.query); // for prerendered ISR paths we attempt parsing the route // params from the URL directly as route-matches may not // contain the correct values due to the filesystem path // matching before the dynamic route has been matched if (!paramsResult.hasValidParams && pageIsDynamic && !(0, _utils1.isDynamicRoute)(normalizedUrlPath)) { let matcherParams = utils.dynamicRouteMatcher == null ? void 0 : utils.dynamicRouteMatcher(normalizedUrlPath); if (matcherParams) { utils.normalizeDynamicRouteParams(matcherParams); Object.assign(paramsResult.params, matcherParams); paramsResult.hasValidParams = true; } } if (paramsResult.hasValidParams) { params = paramsResult.params; } if (req.headers["x-now-route-matches"] && (0, _utils1.isDynamicRoute)(matchedPath) && !paramsResult.hasValidParams) { const opts = {}; const routeParams = utils.getParamsFromRouteMatches(req, opts, parsedUrl.query.__nextLocale || ""); // If this returns a locale, it means that the locale was detected // from the pathname. if (opts.locale) { parsedUrl.query.__nextLocale = opts.locale; // As the locale was parsed from the pathname, we should mark // that the locale was not inferred as the default. delete parsedUrl.query.__nextInferredLocaleFromDefault; } paramsResult = utils.normalizeDynamicRouteParams(routeParams, true); if (paramsResult.hasValidParams) { params = paramsResult.params; } } // handle the actual dynamic route name being requested if (pageIsDynamic && utils.defaultRouteMatches && normalizedUrlPath === srcPathname && !paramsResult.hasValidParams && !utils.normalizeDynamicRouteParams({ ...params }, true).hasValidParams) { params = utils.defaultRouteMatches; } if (params) { matchedPath = utils.interpolateDynamicPath(srcPathname, params); req.url = utils.interpolateDynamicPath(req.url, params); } } if (pageIsDynamic || didRewrite) { var _utils_defaultRouteRegex; utils.normalizeVercelUrl(req, true, [ ...rewriteParamKeys, ...Object.keys(((_utils_defaultRouteRegex = utils.defaultRouteRegex) == null ? void 0 : _utils_defaultRouteRegex.groups) || {}) ]); } for (const key of routeParamKeys){ delete parsedUrl.query[key]; } parsedUrl.pathname = matchedPath; url.pathname = parsedUrl.pathname; const normalizeResult = await this.normalizeNextData(req, res, parsedUrl); if (normalizeResult.finished) { return; } } catch (err) { if (err instanceof _utils.DecodeError || err instanceof _utils.NormalizeError) { res.statusCode = 400; return this.renderError(null, req, res, "/_error", {}); } throw err; } } if (// Edge runtime always has minimal mode enabled. process.env.NEXT_RUNTIME !== "edge" && !this.minimalMode && defaultLocale) { const { getLocaleRedirect } = require("../shared/lib/i18n/get-locale-redirect"); const redirect = getLocaleRedirect({ defaultLocale, domainLocale, headers: req.headers, nextConfig: this.nextConfig, pathLocale: pathnameInfo.locale, urlParsed: { ...url, pathname: pathnameInfo.locale ? `/${pathnameInfo.locale}${url.pathname}` : url.pathname } }); if (redirect) { return res.redirect(redirect, _constants.TEMPORARY_REDIRECT_STATUS).body(redirect).send(); } } (0, _requestmeta.addRequestMeta)(req, "__nextIsLocaleDomain", Boolean(domainLocale)); if (pathnameInfo.locale) { req.url = (0, _url.format)(url); (0, _requestmeta.addRequestMeta)(req, "__nextStrippedLocale", true); } // If we aren't in minimal mode or there is no locale in the query // string, add the locale to the query string. if (!this.minimalMode || !parsedUrl.query.__nextLocale) { // If the locale is in the pathname, add it to the query string. if (pathnameInfo.locale) { parsedUrl.query.__nextLocale = pathnameInfo.locale; } else if (defaultLocale) { parsedUrl.query.__nextLocale = defaultLocale; parsedUrl.query.__nextInferredLocaleFromDefault = "1"; } } // set incremental cache to request meta so it can // be passed down for edge functions and the fetch disk // cache can be leveraged locally if (!this.serverOptions.webServerConfig && !(0, _requestmeta.getRequestMeta)(req, "_nextIncrementalCache")) { let protocol = "https:"; try { const parsedFullUrl = new URL((0, _requestmeta.getRequestMeta)(req, "__NEXT_INIT_URL") || "/", "http://n"); protocol = parsedFullUrl.protocol; } catch (_) {} const incrementalCache = this.getIncrementalCache({ requestHeaders: Object.assign({}, req.headers), requestProtocol: protocol.substring(0, protocol.length - 1) }); (0, _requestmeta.addRequestMeta)(req, "_nextIncrementalCache", incrementalCache); globalThis.__incrementalCache = incrementalCache; } // when x-invoke-path is specified we can short short circuit resolving // we only honor this header if we are inside of a render worker to // prevent external users coercing the routing path const invokePath = req.headers["x-invoke-path"]; const useInvokePath = !useMatchedPathHeader && process.env.NEXT_RUNTIME !== "edge" && process.env.__NEXT_PRIVATE_RENDER_WORKER && invokePath; if (useInvokePath) { var _this_nextConfig_i18n1; if (req.headers["x-invoke-status"]) { const invokeQuery = req.headers["x-invoke-query"]; if (typeof invokeQuery === "string") { Object.assign(parsedUrl.query, JSON.parse(decodeURIComponent(invokeQuery))); } res.statusCode = Number(req.headers["x-invoke-status"]); let err = null; if (typeof req.headers["x-invoke-error"] === "string") { const invokeError = JSON.parse(req.headers["x-invoke-error"] || "{}"); err = new Error(invokeError.message); } return this.renderError(err, req, res, "/_error", parsedUrl.query); } const parsedMatchedPath = new URL(invokePath || "/", "http://n"); const invokePathnameInfo = (0, _getnextpathnameinfo.getNextPathnameInfo)(parsedMatchedPath.pathname, { nextConfig: this.nextConfig, parseData: false }); if (invokePathnameInfo.locale) { parsedUrl.query.__nextLocale = invokePathnameInfo.locale; } if (parsedUrl.pathname !== parsedMatchedPath.pathname) { parsedUrl.pathname = parsedMatchedPath.pathname; (0, _requestmeta.addRequestMeta)(req, "_nextRewroteUrl", invokePathnameInfo.pathname); (0, _requestmeta.addRequestMeta)(req, "_nextDidRewrite", true); } const normalizeResult = (0, _normalizelocalepath.normalizeLocalePath)((0, _removepathprefix.removePathPrefix)(parsedUrl.pathname, this.nextConfig.basePath || ""), ((_this_nextConfig_i18n1 = this.nextConfig.i18n) == null ? void 0 : _this_nextConfig_i18n1.locales) || []); if (normalizeResult.detectedLocale) { parsedUrl.query.__nextLocale = normalizeResult.detectedLocale; } parsedUrl.pathname = normalizeResult.pathname; for (const key of Object.keys(parsedUrl.query)){ if (!key.startsWith("__next") && !key.startsWith("_next")) { delete parsedUrl.query[key]; } } const invokeQuery = req.headers["x-invoke-query"]; if (typeof invokeQuery === "string") { Object.assign(parsedUrl.query, JSON.parse(decodeURIComponent(invokeQuery))); } if (parsedUrl.pathname.startsWith("/_next/image")) { const imageResult = await this.handleNextImageRequest(req, res, parsedUrl); if (imageResult.finished) { return; } } const nextDataResult = await this.normalizeNextData(req, res, parsedUrl); if (nextDataResult.finished) { return; } await this.handleCatchallRenderRequest(req, res, parsedUrl); return; } if (process.env.NEXT_RUNTIME !== "edge" && process.env.__NEXT_PRIVATE_RENDER_WORKER && req.headers["x-middleware-invoke"]) { const nextDataResult = await this.normalizeNextData(req, res, parsedUrl); if (nextDataResult.finished) { return; } const result = await this.handleCatchallMiddlewareRequest(req, res, parsedUrl); if (!result.finished) { res.setHeader("x-middleware-next", "1"); res.body(""); res.send(); } return; } // ensure we strip the basePath when not using an invoke header if (!(useMatchedPathHeader || useInvokePath) && pathnameInfo.basePath) { parsedUrl.pathname = (0, _removepathprefix.removePathPrefix)(parsedUrl.pathname, pathnameInfo.basePath); } res.statusCode = 200; return await this.run(req, res, parsedUrl); } catch (err) { if (err && typeof err === "object" && err.code === "ERR_INVALID_URL" || err instanceof _utils.DecodeError || err instanceof _utils.NormalizeError) { res.statusCode = 400; return this.renderError(null, req, res, "/_error", {}); } if (this.minimalMode || this.renderOpts.dev) { throw err; } this.logError((0, _iserror.getProperError)(err)); res.statusCode = 500; res.body("Internal Server Error").send(); } } getRequestHandler() { return this.handleRequest.bind(this); } async handleUpgrade(_req, _socket, _head) {} setAssetPrefix(prefix) { this.renderOpts.assetPrefix = prefix ? prefix.replace(/\/$/, "") : ""; } /** * Runs async initialization of server. * It is idempotent, won't fire underlying initialization more than once. */ async prepare() { if (this.prepared) return; if (this.preparedPromise === null) { this.preparedPromise = this.prepareImpl().then(()=>{ this.prepared = true; this.preparedPromise = null; }); } return this.preparedPromise; } async prepareImpl() {} // Backwards compatibility async close() {} getAppPathRoutes() { const appPathRoutes = {}; Object.keys(this.appPathsManifest || {}).forEach((entry)=>{ const normalizedPath = (0, _apppaths.normalizeAppPath)(entry); if (!appPathRoutes[normalizedPath]) { appPathRoutes[normalizedPath] = []; } appPathRoutes[normalizedPath].push(entry); }); return appPathRoutes; } async run(req, res, parsedUrl) { return (0, _tracer.getTracer)().trace(_constants1.BaseServerSpan.run, async ()=>this.runImpl(req, res, parsedUrl)); } async runImpl(req, res, parsedUrl) { await this.handleCatchallRenderRequest(req, res, parsedUrl); } async pipe(fn, partialContext) { return (0, _tracer.getTracer)().trace(_constants1.BaseServerSpan.pipe, async ()=>this.pipeImpl(fn, partialContext)); } async pipeImpl(fn, partialContext) { const isBotRequest = (0, _isbot.isBot)(partialContext.req.headers["user-agent"] || ""); const ctx = { ...partialContext, renderOpts: { ...this.renderOpts, supportsDynamicHTML: !isBotRequest, isBot: !!isBotRequest } }; const payload = await fn(ctx); if (payload === null) { return; } const { req , res } = ctx; const { body , type , revalidateOptions } = payload; if (!res.sent) { const { generateEtags , poweredByHeader , dev } = this.renderOpts; if (dev) { // In dev, we should not cache pages for any reason. res.setHeader("Cache-Control", "no-store, must-revalidate"); } return this.sendRenderResult(req, res, { result: body, type, generateEtags, poweredByHeader, options: revalidateOptions }); } } async getStaticHTML(fn, partialContext) { const ctx = { ...partialContext, renderOpts: { ...this.renderOpts, supportsDynamicHTML: false } }; const payload = await fn(ctx); if (payload === null) { return null; } return payload.body.toUnchunkedString(); } async render(req, res, pathname, query = {}, parsedUrl, internalRender = false) { return (0, _tracer.getTracer)().trace(_constants1.BaseServerSpan.render, async ()=>this.renderImpl(req, res, pathname, query, parsedUrl, internalRender)); } async renderImpl(req, res, pathname, query = {}, parsedUrl, internalRender = false) { var _req_url; if (!pathname.startsWith("/")) { console.warn(`Cannot render page with path "${pathname}", did you mean "/${pathname}"?. See more info here: https://nextjs.org/docs/messages/render-no-starting-slash`); } if (this.renderOpts.customServer && pathname === "/index" && !await this.hasPage("/index")) { // maintain backwards compatibility for custom server // (see custom-server integration tests) pathname = "/"; } // we allow custom servers to call render for all URLs // so check if we need to serve a static _next file or not. // we don't modify the URL for _next/data request but still // call render so we special case this to prevent an infinite loop if (!internalRender && !this.minimalMode && !query.__nextDataReq && (((_req_url = req.url) == null ? void 0 : _req_url.match(/^\/_next\//)) || this.hasStaticDir && req.url.match(/^\/static\//))) { return this.handleRequest(req, res, parsedUrl); } if ((0, _utils2.isBlockedPage)(pathname)) { return this.render404(req, res, parsedUrl); } return this.pipe((ctx)=>this.renderToResponse(ctx), { req, res, pathname, query }); } async getStaticPaths({ pathname }) { var _this_getPrerenderManifest_dynamicRoutes_pathname; // `staticPaths` is intentionally set to `undefined` as it should've // been caught when checking disk data. const staticPaths = undefined; // Read whether or not fallback should exist from the manifest. const fallbackField = (_this_getPrerenderManifest_dynamicRoutes_pathname = this.getPrerenderManifest().dynamicRoutes[pathname]) == null ? void 0 : _this_getPrerenderManifest_dynamicRoutes_pathname.fallback; return { staticPaths, fallbackMode: typeof fallbackField === "string" ? "static" : fallbackField === null ? "blocking" : fallbackField }; } async renderToResponseWithComponents(requestContext, findComponentsResult) { return (0, _tracer.getTracer)().trace(_constants1.BaseServerSpan.renderToResponseWithComponents, async ()=>this.renderToResponseWithComponentsImpl(requestContext, findComponentsResult)); } async renderToResponseWithComponentsImpl({ req , res , pathname , renderOpts: opts }, { components , query }) { var _components_Component, _this_nextConfig_i18n, _this_nextConfig_i18n1; const is404Page = pathname === "/404"; const is500Page = pathname === "/500"; const isAppPath = components.isAppPath; const hasServerProps = !!components.getServerSideProps; let hasStaticPaths = !!components.getStaticPaths; const hasGetInitialProps = !!((_components_Component = components.Component) == null ? void 0 : _components_Component.getInitialProps); let isSSG = !!components.getStaticProps; // Compute the iSSG cache key. We use the rewroteUrl since // pages with fallback: false are allowed to be rewritten to // and we need to look up the path by the rewritten path let urlPathname = (0, _url.parse)(req.url || "").pathname || "/"; let resolvedUrlPathname = (0, _requestmeta.getRequestMeta)(req, "_nextRewroteUrl") || urlPathname; let staticPaths; let fallbackMode; if (isAppPath) { const pathsResult = await this.getStaticPaths({ pathname, originalAppPath: components.pathname, requestHeaders: req.headers }); staticPaths = pathsResult.staticPaths; fallbackMode = pathsResult.fallbackMode; const hasFallback = typeof fallbackMode !== "undefined"; if (hasFallback) { hasStaticPaths = true; } if (hasFallback || (staticPaths == null ? void 0 : staticPaths.includes(resolvedUrlPathname)) || // this signals revalidation in deploy environments // TODO: make this more generic req.headers["x-now-route-matches"]) { isSSG = true; } else if (!this.renderOpts.dev) { const manifest = this.getPrerenderManifest(); isSSG = isSSG || !!manifest.routes[pathname === "/index" ? "/" : pathname]; } } // Toggle whether or not this is a Data request let isDataReq = !!(query.__nextDataReq || req.headers["x-nextjs-data"] && this.serverOptions.webServerConfig) && (isSSG || hasServerProps); // when we are handling a middleware prefetch and it doesn't // resolve to a static data route we bail early to avoid // unexpected SSR invocations if (!isSSG && req.headers["x-middleware-prefetch"] && !(is404Page || pathname === "/_error")) { res.setHeader("x-middleware-skip", "1"); res.body("{}").send(); return null; } delete query.__nextDataReq; // normalize req.url for SSG paths as it is not exposed // to getStaticProps and the asPath should not expose /_next/data if (isSSG && this.minimalMode && req.headers["x-matched-path"] && req.url.startsWith("/_next/data")) { req.url = this.stripNextDataPath(req.url); } if (!!req.headers["x-nextjs-data"] && (!res.statusCode || res.statusCode === 200)) { res.setHeader("x-nextjs-matched-path", `${query.__nextLocale ? `/${query.__nextLocale}` : ""}${pathname}`); } // Don't delete headers[RSC] yet, it still needs to be used in renderToHTML later const isFlightRequest = Boolean(req.headers[_approuterheaders.RSC.toLowerCase()]); // For pages we need to ensure the correct Vary header is set too, to avoid // caching issues when navigating between pages and app if (!isAppPath && isFlightRequest) { res.setHeader("vary", _approuterheaders.RSC_VARY_HEADER); } // we need to ensure the status code if /404 is visited directly if (is404Page && !isDataReq && !isFlightRequest) { res.statusCode = 404; } // ensure correct status is set when visiting a status page // directly e.g. /500 if (_constants.STATIC_STATUS_PAGES.includes(pathname)) { res.statusCode = parseInt(pathname.slice(1), 10); } // static pages can only respond to GET/HEAD // requests so ensure we respond with 405 for // invalid requests if (!is404Page && !is500Page && pathname !== "/_error" && req.method !== "HEAD" && req.method !== "GET" && (typeof components.Component === "string" || isSSG)) { res.statusCode = 405; res.setHeader("Allow", [ "GET", "HEAD" ]); await this.renderError(null, req, res, pathname); return null; } // handle static page if (typeof components.Component === "string") { return { type: "html", // TODO: Static pages should be serialized as RenderResult body: _renderresult.default.fromStatic(components.Component) }; } if (!query.amp) { delete query.amp; } if (opts.supportsDynamicHTML === true) { var _components_Document; const isBotRequest = (0, _isbot.isBot)(req.headers["user-agent"] || ""); const isSupportedDocument = typeof ((_components_Document = components.Document) == null ? void 0 : _components_Document.getInitialProps) !== "function" || // The built-in `Document` component also supports dynamic HTML for concurrent mode. _constants.NEXT_BUILTIN_DOCUMENT in components.Document; // Disable dynamic HTML in cases that we know it won't be generated, // so that we can continue generating a cache key when possible. // TODO-APP: should the first render for a dynamic app path // be static so we can collect revalidate and populate the // cache if there are no dynamic data requirements opts.supportsDynamicHTML = !isSSG && !isBotRequest && !query.amp && isSupportedDocument; opts.isBot = isBotRequest; } // In development, we always want to generate dynamic HTML. if (!isDataReq && isAppPath && opts.dev && opts.supportsDynamicHTML === false) { opts.supportsDynamicHTML = true; } const defaultLocale = isSSG ? (_this_nextConfig_i18n = this.nextConfig.i18n) == null ? void 0 : _this_nextConfig_i18n.defaultLocale : query.__nextDefaultLocale; const locale = query.__nextLocale; const locales = (_this_nextConfig_i18n1 = this.nextConfig.i18n) == null ? void 0 : _this_nextConfig_i18n1.locales; let previewData; let isPreviewMode = false; if (hasServerProps || isSSG) { // For the edge runtime, we don't support preview mode in SSG. if (process.env.NEXT_RUNTIME !== "edge") { const { tryGetPreviewData } = require("./api-utils/node"); previewData = tr