"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "default", { enumerable: true, get: function() { return NextNodeServer; } }); 0 && __export(require("./base-server")); require("./node-environment"); require("./require-hook"); require("./node-polyfill-fetch"); require("./node-polyfill-form"); require("./node-polyfill-web-streams"); require("./node-polyfill-crypto"); const _utils = require("../shared/lib/utils"); const _routematcher = require("../shared/lib/router/utils/route-matcher"); const _fs = /*#__PURE__*/ _interop_require_default(require("fs")); const _path = require("path"); const _http = require("http"); const _requestmeta = require("./request-meta"); const _constants = require("../shared/lib/constants"); const _recursivereaddirsync = require("./lib/recursive-readdir-sync"); const _findpagesdir = require("../lib/find-pages-dir"); const _url = require("url"); const _pathmatch = require("../shared/lib/router/utils/path-match"); const _serverrouteutils = require("./server-route-utils"); const _getroutefromassetpath = /*#__PURE__*/ _interop_require_default(require("../shared/lib/router/utils/get-route-from-asset-path")); const _node = require("./base-http/node"); const _sendpayload = require("./send-payload"); const _servestatic = require("./serve-static"); const _node1 = require("./api-utils/node"); const _parseurl = require("../shared/lib/router/utils/parse-url"); const _log = /*#__PURE__*/ _interop_require_wildcard(require("../build/output/log")); const _baseserver = /*#__PURE__*/ _interop_require_wildcard(_export_star(require("./base-server"), exports)); const _require = require("./require"); const _denormalizepagepath = require("../shared/lib/page-path/denormalize-page-path"); const _normalizepagepath = require("../shared/lib/page-path/normalize-page-path"); const _loadcomponents = require("./load-components"); const _iserror = /*#__PURE__*/ _interop_require_wildcard(require("../lib/is-error")); const _utils1 = require("./web/utils"); const _relativizeurl = require("../shared/lib/router/utils/relativize-url"); const _preparedestination = require("../shared/lib/router/utils/prepare-destination"); const _middlewareroutematcher = require("../shared/lib/router/utils/middleware-route-matcher"); const _env = require("@next/env"); const _querystring = require("../shared/lib/router/utils/querystring"); const _removetrailingslash = require("../shared/lib/router/utils/remove-trailing-slash"); const _getnextpathnameinfo = require("../shared/lib/router/utils/get-next-pathname-info"); const _bodystreams = require("./body-streams"); const _apiutils = require("./api-utils"); const _responsecache = /*#__PURE__*/ _interop_require_default(require("./response-cache")); const _incrementalcache = require("./lib/incremental-cache"); const _apppaths = require("../shared/lib/router/utils/app-paths"); const _config = require("./config"); const _routekind = require("./future/route-kind"); const _constants1 = require("../lib/constants"); const _tracer = require("./lib/trace/tracer"); const _constants2 = require("./lib/trace/constants"); const _nodefsmethods = require("./lib/node-fs-methods"); const _routeregex = require("../shared/lib/router/utils/route-regex"); const _removepathprefix = require("../shared/lib/router/utils/remove-path-prefix"); const _addpathprefix = require("../shared/lib/router/utils/add-path-prefix"); const _pathhasprefix = require("../shared/lib/router/utils/path-has-prefix"); const _invokerequest = require("./lib/server-ipc/invoke-request"); const _utils2 = require("./lib/server-ipc/utils"); const _mockrequest = require("./lib/mock-request"); const _chalk = /*#__PURE__*/ _interop_require_default(require("next/dist/compiled/chalk")); const _approuterheaders = require("../client/components/app-router-headers"); const _nextrequest = require("./web/spec-extension/adapters/next-request"); function _export_star(from, to) { Object.keys(from).forEach(function(k) { if (k !== "default" && !Object.prototype.hasOwnProperty.call(to, k)) { Object.defineProperty(to, k, { enumerable: true, get: function() { return from[k]; } }); } }); return from; } 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; } const MiddlewareMatcherCache = new WeakMap(); function getMiddlewareMatcher(info) { const stored = MiddlewareMatcherCache.get(info); if (stored) { return stored; } if (!Array.isArray(info.matchers)) { throw new Error(`Invariant: invalid matchers for middleware ${JSON.stringify(info)}`); } const matcher = (0, _middlewareroutematcher.getMiddlewareRouteMatcher)(info.matchers); MiddlewareMatcherCache.set(info, matcher); return matcher; } /** * Hardcoded every possible error status code that could be thrown by "serveStatic" method * This is done by searching "this.error" inside "send" module's source code: * https://github.com/pillarjs/send/blob/master/index.js * https://github.com/pillarjs/send/blob/develop/index.js */ const POSSIBLE_ERROR_CODE_FROM_SERVE_STATIC = new Set([ // send module will throw 500 when header is already sent or fs.stat error happens // https://github.com/pillarjs/send/blob/53f0ab476145670a9bdd3dc722ab2fdc8d358fc6/index.js#L392 // Note: we will use Next.js built-in 500 page to handle 500 errors // 500, // send module will throw 404 when file is missing // https://github.com/pillarjs/send/blob/53f0ab476145670a9bdd3dc722ab2fdc8d358fc6/index.js#L421 // Note: we will use Next.js built-in 404 page to handle 404 errors // 404, // send module will throw 403 when redirecting to a directory without enabling directory listing // https://github.com/pillarjs/send/blob/53f0ab476145670a9bdd3dc722ab2fdc8d358fc6/index.js#L484 // Note: Next.js throws a different error (without status code) for directory listing // 403, // send module will throw 400 when fails to normalize the path // https://github.com/pillarjs/send/blob/53f0ab476145670a9bdd3dc722ab2fdc8d358fc6/index.js#L520 400, // send module will throw 412 with conditional GET request // https://github.com/pillarjs/send/blob/53f0ab476145670a9bdd3dc722ab2fdc8d358fc6/index.js#L632 412, // send module will throw 416 when range is not satisfiable // https://github.com/pillarjs/send/blob/53f0ab476145670a9bdd3dc722ab2fdc8d358fc6/index.js#L669 416 ]); class NextNodeServer extends _baseserver.default { constructor(options){ // Initialize super class super(options); this._validFilesystemPathSet = null; /** * This sets environment variable to be used at the time of SSR by head.tsx. * Using this from process.env allows targeting SSR by calling * `process.env.__NEXT_OPTIMIZE_CSS`. */ if (this.renderOpts.optimizeFonts) { process.env.__NEXT_OPTIMIZE_FONTS = JSON.stringify(this.renderOpts.optimizeFonts); } if (this.renderOpts.optimizeCss) { process.env.__NEXT_OPTIMIZE_CSS = JSON.stringify(true); } if (this.renderOpts.nextScriptWorkers) { process.env.__NEXT_SCRIPT_WORKERS = JSON.stringify(true); } if (this.nextConfig.compress) { this.compression = require("next/dist/compiled/compression")(); } if (this.nextConfig.experimental.deploymentId) { process.env.NEXT_DEPLOYMENT_ID = this.nextConfig.experimental.deploymentId; } if (!this.minimalMode) { this.imageResponseCache = new _responsecache.default(this.minimalMode); } const { appDocumentPreloading } = this.nextConfig.experimental; const isDefaultEnabled = typeof appDocumentPreloading === "undefined"; if (!options.dev && (appDocumentPreloading === true || !(this.minimalMode && isDefaultEnabled))) { // pre-warm _document and _app as these will be // needed for most requests (0, _loadcomponents.loadComponents)({ distDir: this.distDir, pathname: "/_document", hasServerComponents: false, isAppPath: false }).catch(()=>{}); (0, _loadcomponents.loadComponents)({ distDir: this.distDir, pathname: "/_app", hasServerComponents: false, isAppPath: false }).catch(()=>{}); } if (this.isRouterWorker) { this.renderWorkers = {}; this.renderWorkerOpts = { port: this.port || 0, dir: this.dir, workerType: "render", hostname: this.hostname, minimalMode: this.minimalMode, dev: !!options.dev, isNodeDebugging: !!options.isNodeDebugging }; const { createWorker , createIpcServer } = require("./lib/server-ipc"); this.renderWorkersPromises = new Promise(async (resolveWorkers)=>{ try { this.renderWorkers = {}; const { ipcPort , ipcValidationKey } = await createIpcServer(this); if (this.hasAppDir) { this.renderWorkers.app = await createWorker(ipcPort, ipcValidationKey, options.isNodeDebugging, "app", this.nextConfig.experimental.serverActions); } this.renderWorkers.pages = await createWorker(ipcPort, ipcValidationKey, options.isNodeDebugging, "pages"); this.renderWorkers.middleware = this.renderWorkers.pages || this.renderWorkers.app; resolveWorkers(); } catch (err) { _log.error(`Invariant failed to initialize render workers`); console.error(err); process.exit(1); } }); global._nextDeleteCache = (filePath)=>{ try { var _this_renderWorkers, _this_renderWorkers_pages, _this_renderWorkers1, _this_renderWorkers_app; (_this_renderWorkers = this.renderWorkers) == null ? void 0 : (_this_renderWorkers_pages = _this_renderWorkers.pages) == null ? void 0 : _this_renderWorkers_pages.deleteCache(filePath); (_this_renderWorkers1 = this.renderWorkers) == null ? void 0 : (_this_renderWorkers_app = _this_renderWorkers1.app) == null ? void 0 : _this_renderWorkers_app.deleteCache(filePath); } catch (err) { console.error(err); } }; global._nextDeleteAppClientCache = ()=>{ try { var _this_renderWorkers, _this_renderWorkers_pages, _this_renderWorkers1, _this_renderWorkers_app; (_this_renderWorkers = this.renderWorkers) == null ? void 0 : (_this_renderWorkers_pages = _this_renderWorkers.pages) == null ? void 0 : _this_renderWorkers_pages.deleteAppClientCache(); (_this_renderWorkers1 = this.renderWorkers) == null ? void 0 : (_this_renderWorkers_app = _this_renderWorkers1.app) == null ? void 0 : _this_renderWorkers_app.deleteAppClientCache(); } catch (err) { console.error(err); } }; global._nextClearModuleContext = (targetPath)=>{ try { var _this_renderWorkers, _this_renderWorkers_pages, _this_renderWorkers1, _this_renderWorkers_app; (_this_renderWorkers = this.renderWorkers) == null ? void 0 : (_this_renderWorkers_pages = _this_renderWorkers.pages) == null ? void 0 : _this_renderWorkers_pages.clearModuleContext(targetPath); (_this_renderWorkers1 = this.renderWorkers) == null ? void 0 : (_this_renderWorkers_app = _this_renderWorkers1.app) == null ? void 0 : _this_renderWorkers_app.clearModuleContext(targetPath); } catch (err) { console.error(err); } }; } // ensure options are set when loadConfig isn't called (0, _config.setHttpClientAndAgentOptions)(this.nextConfig); } async prepareImpl() { await super.prepareImpl(); if (!this.serverOptions.dev && this.nextConfig.experimental.instrumentationHook) { try { const instrumentationHook = await require((0, _path.resolve)(this.serverOptions.dir || ".", this.serverOptions.conf.distDir, "server", _constants1.INSTRUMENTATION_HOOK_FILENAME)); await (instrumentationHook.register == null ? void 0 : instrumentationHook.register()); } catch (err) { if (err.code !== "MODULE_NOT_FOUND") { err.message = `An error occurred while loading instrumentation hook: ${err.message}`; throw err; } } } } loadEnvConfig({ dev , forceReload , silent }) { (0, _env.loadEnvConfig)(this.dir, dev, silent ? { info: ()=>{}, error: ()=>{} } : _log, forceReload); } getIncrementalCache({ requestHeaders , requestProtocol }) { const dev = !!this.renderOpts.dev; let CacheHandler; const { incrementalCacheHandlerPath } = this.nextConfig.experimental; if (incrementalCacheHandlerPath) { CacheHandler = require((0, _path.isAbsolute)(incrementalCacheHandlerPath) ? incrementalCacheHandlerPath : (0, _path.join)(this.distDir, incrementalCacheHandlerPath)); CacheHandler = CacheHandler.default || CacheHandler; } // incremental-cache is request specific with a shared // although can have shared caches in module scope // per-cache handler return new _incrementalcache.IncrementalCache({ fs: this.getCacheFilesystem(), dev, requestHeaders, requestProtocol, appDir: this.hasAppDir, allowedRevalidateHeaderKeys: this.nextConfig.experimental.allowedRevalidateHeaderKeys, minimalMode: this.minimalMode, serverDistDir: this.serverDistDir, fetchCache: this.nextConfig.experimental.appDir, fetchCacheKeyPrefix: this.nextConfig.experimental.fetchCacheKeyPrefix, maxMemoryCacheSize: this.nextConfig.experimental.isrMemoryCacheSize, flushToDisk: !this.minimalMode && this.nextConfig.experimental.isrFlushToDisk, getPrerenderManifest: ()=>this.getPrerenderManifest(), CurCacheHandler: CacheHandler }); } getResponseCache() { return new _responsecache.default(this.minimalMode); } getPublicDir() { return (0, _path.join)(this.dir, _constants.CLIENT_PUBLIC_FILES_PATH); } getHasStaticDir() { return _fs.default.existsSync((0, _path.join)(this.dir, "static")); } getPagesManifest() { return require((0, _path.join)(this.serverDistDir, _constants.PAGES_MANIFEST)); } getAppPathsManifest() { if (!this.hasAppDir) return undefined; const appPathsManifestPath = (0, _path.join)(this.serverDistDir, _constants.APP_PATHS_MANIFEST); return require(appPathsManifestPath); } async hasPage(pathname) { var _this_nextConfig_i18n; return !!(0, _require.getMaybePagePath)(pathname, this.distDir, (_this_nextConfig_i18n = this.nextConfig.i18n) == null ? void 0 : _this_nextConfig_i18n.locales, this.hasAppDir); } getBuildId() { const buildIdFile = (0, _path.join)(this.distDir, _constants.BUILD_ID_FILE); try { return _fs.default.readFileSync(buildIdFile, "utf8").trim(); } catch (err) { if (!_fs.default.existsSync(buildIdFile)) { throw new Error(`Could not find a production build in the '${this.distDir}' directory. Try building your app with 'next build' before starting the production server. https://nextjs.org/docs/messages/production-start-no-build-id`); } throw err; } } getCustomRoutes() { const customRoutes = this.getRoutesManifest(); let rewrites; // rewrites can be stored as an array when an array is // returned in next.config.js so massage them into // the expected object format if (Array.isArray(customRoutes.rewrites)) { rewrites = { beforeFiles: [], afterFiles: customRoutes.rewrites, fallback: [] }; } else { rewrites = customRoutes.rewrites; } return Object.assign(customRoutes, { rewrites }); } generateImageRoutes() { return [ { match: (0, _pathmatch.getPathMatch)("/_next/image"), type: "route", name: "_next/image catchall", fn: async (req, res, _params, parsedUrl)=>{ if (this.minimalMode || this.nextConfig.output === "export") { res.statusCode = 400; res.body("Bad Request").send(); return { finished: true }; } const { ImageOptimizerCache } = require("./image-optimizer"); const imageOptimizerCache = new ImageOptimizerCache({ distDir: this.distDir, nextConfig: this.nextConfig }); const { getHash , sendResponse , ImageError } = require("./image-optimizer"); if (!this.imageResponseCache) { throw new Error("invariant image optimizer cache was not initialized"); } const imagesConfig = this.nextConfig.images; if (imagesConfig.loader !== "default" || imagesConfig.unoptimized) { await this.render404(req, res); return { finished: true }; } const paramsResult = ImageOptimizerCache.validateParams(req.originalRequest, parsedUrl.query, this.nextConfig, !!this.renderOpts.dev); if ("errorMessage" in paramsResult) { res.statusCode = 400; res.body(paramsResult.errorMessage).send(); return { finished: true }; } const cacheKey = ImageOptimizerCache.getCacheKey(paramsResult); try { var _cacheEntry_value; const cacheEntry = await this.imageResponseCache.get(cacheKey, async ()=>{ const { buffer , contentType , maxAge } = await this.imageOptimizer(req, res, paramsResult); const etag = getHash([ buffer ]); return { value: { kind: "IMAGE", buffer, etag, extension: (0, _servestatic.getExtension)(contentType) }, revalidate: maxAge }; }, { incrementalCache: imageOptimizerCache }); if ((cacheEntry == null ? void 0 : (_cacheEntry_value = cacheEntry.value) == null ? void 0 : _cacheEntry_value.kind) !== "IMAGE") { throw new Error("invariant did not get entry from image response cache"); } sendResponse(req.originalRequest, res.originalResponse, paramsResult.href, cacheEntry.value.extension, cacheEntry.value.buffer, paramsResult.isStatic, cacheEntry.isMiss ? "MISS" : cacheEntry.isStale ? "STALE" : "HIT", imagesConfig, cacheEntry.revalidate || 0, Boolean(this.renderOpts.dev)); } catch (err) { if (err instanceof ImageError) { res.statusCode = err.statusCode; res.body(err.message).send(); return { finished: true }; } throw err; } return { finished: true }; } } ]; } getHasAppDir(dev) { return Boolean((0, _findpagesdir.findDir)(dev ? this.dir : this.serverDistDir, "app")); } generateStaticRoutes() { return this.hasStaticDir ? [ { // It's very important to keep this route's param optional. // (but it should support as many params as needed, separated by '/') // Otherwise this will lead to a pretty simple DOS attack. // See more: https://github.com/vercel/next.js/issues/2617 match: (0, _pathmatch.getPathMatch)("/static/:path*"), name: "static catchall", fn: async (req, res, params, parsedUrl)=>{ const p = (0, _path.join)(this.dir, "static", ...params.path); await this.serveStatic(req, res, p, parsedUrl); return { finished: true }; } } ] : []; } setImmutableAssetCacheControl(res, _pathSegments) { res.setHeader("Cache-Control", "public, max-age=31536000, immutable"); } generateFsStaticRoutes() { return [ { match: (0, _pathmatch.getPathMatch)("/_next/static/:path*"), type: "route", name: "_next/static catchall", fn: async (req, res, params, parsedUrl)=>{ // make sure to 404 for /_next/static itself if (!params.path) { await this.render404(req, res, parsedUrl); return { finished: true }; } if (params.path[0] === _constants.CLIENT_STATIC_FILES_RUNTIME || params.path[0] === "chunks" || params.path[0] === "css" || params.path[0] === "image" || params.path[0] === "media" || params.path[0] === this.buildId || params.path[0] === "pages" || params.path[1] === "pages") { this.setImmutableAssetCacheControl(res, params.path); } const p = (0, _path.join)(this.distDir, _constants.CLIENT_STATIC_FILES_PATH, ...params.path || []); await this.serveStatic(req, res, p, parsedUrl); return { finished: true }; } } ]; } generatePublicRoutes() { if (!_fs.default.existsSync(this.publicDir)) return []; const publicFiles = new Set((0, _recursivereaddirsync.recursiveReadDirSync)(this.publicDir).map((p)=>encodeURI(p.replace(/\\/g, "/")))); return [ { match: (0, _pathmatch.getPathMatch)("/:path*"), matchesBasePath: true, name: "public folder catchall", fn: async (req, res, params, parsedUrl)=>{ const pathParts = params.path || []; const { basePath } = this.nextConfig; // if basePath is defined require it be present if (basePath) { const basePathParts = basePath.split("/"); // remove first empty value basePathParts.shift(); if (!basePathParts.every((part, idx)=>{ return part === pathParts[idx]; })) { return { finished: false }; } pathParts.splice(0, basePathParts.length); } let path = `/${pathParts.join("/")}`; if (!publicFiles.has(path)) { // In `next-dev-server.ts`, we ensure encoded paths match // decoded paths on the filesystem. So we need do the // opposite here: make sure decoded paths match encoded. path = encodeURI(path); } if (publicFiles.has(path)) { await this.serveStatic(req, res, (0, _path.join)(this.publicDir, ...pathParts), parsedUrl); return { finished: true }; } return { finished: false }; } } ]; } getFilesystemPaths() { if (this._validFilesystemPathSet) { return this._validFilesystemPathSet; } const pathUserFilesStatic = (0, _path.join)(this.dir, "static"); let userFilesStatic = []; if (this.hasStaticDir && _fs.default.existsSync(pathUserFilesStatic)) { userFilesStatic = (0, _recursivereaddirsync.recursiveReadDirSync)(pathUserFilesStatic).map((f)=>(0, _path.join)(".", "static", f)); } let userFilesPublic = []; if (this.publicDir && _fs.default.existsSync(this.publicDir)) { userFilesPublic = (0, _recursivereaddirsync.recursiveReadDirSync)(this.publicDir).map((f)=>(0, _path.join)(".", "public", f)); } let nextFilesStatic = []; nextFilesStatic = !this.minimalMode && _fs.default.existsSync((0, _path.join)(this.distDir, "static")) ? (0, _recursivereaddirsync.recursiveReadDirSync)((0, _path.join)(this.distDir, "static")).map((f)=>(0, _path.join)(".", (0, _path.relative)(this.dir, this.distDir), "static", f)) : []; return this._validFilesystemPathSet = new Set([ ...nextFilesStatic, ...userFilesPublic, ...userFilesStatic ]); } sendRenderResult(req, res, options) { return (0, _sendpayload.sendRenderResult)({ req: req.originalRequest, res: res.originalResponse, ...options }); } sendStatic(req, res, path) { return (0, _servestatic.serveStatic)(req.originalRequest, res.originalResponse, path); } handleCompression(req, res) { if (this.compression) { this.compression(req.originalRequest, res.originalResponse, ()=>{}); } } async handleUpgrade(req, socket, head) { try { const parsedUrl = (0, _url.parse)(req.url, true); this.attachRequestMeta(req, parsedUrl, true); await this.router.execute(req, socket, parsedUrl, head); } catch (err) { console.error(err); socket.end("Internal Server Error"); } } async proxyRequest(req, res, parsedUrl, upgradeHead) { const { query } = parsedUrl; delete parsedUrl.query; parsedUrl.search = (0, _serverrouteutils.stringifyQuery)(req, query); const target = (0, _url.format)(parsedUrl); const HttpProxy = require("next/dist/compiled/http-proxy"); const proxy = new HttpProxy({ target, changeOrigin: true, ignorePath: true, xfwd: true, ws: true, // we limit proxy requests to 30s by default, in development // we don't time out WebSocket requests to allow proxying proxyTimeout: upgradeHead && this.renderOpts.dev ? undefined : this.nextConfig.experimental.proxyTimeout || 30000 }); await new Promise((proxyResolve, proxyReject)=>{ let finished = false; proxy.on("error", (err)=>{ console.error(`Failed to proxy ${target}`, err); if (!finished) { finished = true; proxyReject(err); } }); // if upgrade head is present treat as WebSocket request if (upgradeHead) { proxy.on("proxyReqWs", (proxyReq)=>{ proxyReq.on("close", ()=>{ if (!finished) { finished = true; proxyResolve(true); } }); }); proxy.ws(req, res, upgradeHead); proxyResolve(true); } else { var _getRequestMeta; proxy.on("proxyReq", (proxyReq)=>{ proxyReq.on("close", ()=>{ if (!finished) { finished = true; proxyResolve(true); } }); }); proxy.web(req.originalRequest, res.originalResponse, { buffer: (_getRequestMeta = (0, _requestmeta.getRequestMeta)(req, "__NEXT_CLONABLE_BODY")) == null ? void 0 : _getRequestMeta.cloneBodyStream() }); } }); return { finished: true }; } async runApi(req, res, query, params, page, builtPagePath) { const edgeFunctionsPages = this.getEdgeFunctionsPages(); for (const edgeFunctionsPage of edgeFunctionsPages){ if (edgeFunctionsPage === page) { const handledAsEdgeFunction = await this.runEdgeFunction({ req, res, query, params, page, appPaths: null }); if (handledAsEdgeFunction) { return true; } } } const pageModule = await require(builtPagePath); query = { ...query, ...params }; delete query.__nextLocale; delete query.__nextDefaultLocale; delete query.__nextInferredLocaleFromDefault; await (0, _node1.apiResolver)(req.originalRequest, res.originalResponse, query, pageModule, { ...this.renderOpts.previewProps, revalidate: this.revalidate.bind(this), // internal config so is not typed trustHostHeader: this.nextConfig.experimental.trustHostHeader, allowedRevalidateHeaderKeys: this.nextConfig.experimental.allowedRevalidateHeaderKeys, hostname: this.hostname }, this.minimalMode, this.renderOpts.dev, page); return true; } async renderHTML(req, res, pathname, query, renderOpts) { return (0, _tracer.getTracer)().trace(_constants2.NextNodeServerSpan.renderHTML, async ()=>this.renderHTMLImpl(req, res, pathname, query, renderOpts)); } async renderHTMLImpl(req, res, pathname, query, renderOpts) { // Due to the way we pass data by mutating `renderOpts`, we can't extend the // object here but only updating its `clientReferenceManifest` field. // https://github.com/vercel/next.js/blob/df7cbd904c3bd85f399d1ce90680c0ecf92d2752/packages/next/server/render.tsx#L947-L952 renderOpts.clientReferenceManifest = this.clientReferenceManifest; renderOpts.nextFontManifest = this.nextFontManifest; if (this.hasAppDir && renderOpts.isAppPath) { const { renderToHTMLOrFlight: appRenderToHTMLOrFlight } = require("./app-render/app-render"); return appRenderToHTMLOrFlight(req.originalRequest, res.originalResponse, pathname, query, renderOpts); } throw new Error("Invariant: render should have used routeModule"); } streamResponseChunk(res, chunk) { res.write(chunk); // When both compression and streaming are enabled, we need to explicitly // flush the response to avoid it being buffered by gzip. if (this.compression && "flush" in res) { res.flush(); } } async imageOptimizer(req, res, paramsResult) { const { imageOptimizer } = require("./image-optimizer"); return imageOptimizer(req.originalRequest, res.originalResponse, paramsResult, this.nextConfig, this.renderOpts.dev, (newReq, newRes, newParsedUrl)=>this.getRequestHandler()(new _node.NodeNextRequest(newReq), new _node.NodeNextResponse(newRes), newParsedUrl)); } getPagePath(pathname, locales) { return (0, _require.getPagePath)(pathname, this.distDir, locales, this.hasAppDir); } async renderPageComponent(ctx, bubbleNoFallback) { const edgeFunctionsPages = this.getEdgeFunctionsPages() || []; if (edgeFunctionsPages.length) { const appPaths = this.getOriginalAppPaths(ctx.pathname); const isAppPath = Array.isArray(appPaths); let page = ctx.pathname; if (isAppPath) { // When it's an array, we need to pass all parallel routes to the loader. page = appPaths[0]; } for (const edgeFunctionsPage of edgeFunctionsPages){ if (edgeFunctionsPage === page) { await this.runEdgeFunction({ req: ctx.req, res: ctx.res, query: ctx.query, params: ctx.renderOpts.params, page, appPaths }); return null; } } } return super.renderPageComponent(ctx, bubbleNoFallback); } async findPageComponents({ pathname , query , params , isAppPath }) { let route = pathname; if (isAppPath) { // When in App we get page instead of route route = pathname.replace(/\/[^/]*$/, ""); } return (0, _tracer.getTracer)().trace(_constants2.NextNodeServerSpan.findPageComponents, { spanName: `resolving page into components`, attributes: { "next.route": route } }, ()=>this.findPageComponentsImpl({ pathname, query, params, isAppPath })); } async findPageComponentsImpl({ pathname , query , params , isAppPath }) { const paths = [ pathname ]; if (query.amp) { // try serving a static AMP version first paths.unshift((isAppPath ? (0, _apppaths.normalizeAppPath)(pathname) : (0, _normalizepagepath.normalizePagePath)(pathname)) + ".amp"); } if (query.__nextLocale) { paths.unshift(...paths.map((path)=>`/${query.__nextLocale}${path === "/" ? "" : path}`)); } for (const pagePath of paths){ try { const components = await (0, _loadcomponents.loadComponents)({ distDir: this.distDir, pathname: pagePath, hasServerComponents: !!this.renderOpts.serverComponents, isAppPath }); if (query.__nextLocale && typeof components.Component === "string" && !pagePath.startsWith(`/${query.__nextLocale}`)) { continue; } return { components, query: { ...components.getStaticProps ? { amp: query.amp, __nextDataReq: query.__nextDataReq, __nextLocale: query.__nextLocale, __nextDefaultLocale: query.__nextDefaultLocale } : query, // For appDir params is excluded. ...(isAppPath ? {} : params) || {} } }; } catch (err) { // we should only not throw if we failed to find the page // in the pages-manifest if (!(err instanceof _utils.PageNotFoundError)) { throw err; } } } return null; } getFontManifest() { return (0, _require.requireFontManifest)(this.distDir); } getServerComponentManifest() { if (!this.hasAppDir) return undefined; return require((0, _path.join)(this.distDir, "server", _constants.CLIENT_REFERENCE_MANIFEST + ".json")); } getNextFontManifest() { return require((0, _path.join)(this.distDir, "server", `${_constants.NEXT_FONT_MANIFEST}.json`)); } async getFallback(page) { page = (0, _normalizepagepath.normalizePagePath)(page); const cacheFs = this.getCacheFilesystem(); const html = await cacheFs.readFile((0, _path.join)(this.serverDistDir, "pages", `${page}.html`)); return html.toString("utf8"); } generateRoutes(dev) { const publicRoutes = this.generatePublicRoutes(); const imageRoutes = this.generateImageRoutes(); const staticFilesRoutes = this.generateStaticRoutes(); if (!dev) { const routesManifest = this.getRoutesManifest(); this.dynamicRoutes = routesManifest.dynamicRoutes.map((r)=>{ const regex = (0, _routeregex.getRouteRegex)(r.page); const match = (0, _routematcher.getRouteMatcher)(regex); return { match, page: r.page, regex: regex.re }; }); } const fsRoutes = [ ...this.generateFsStaticRoutes(), { match: (0, _pathmatch.getPathMatch)("/_next/data/:path*"), type: "route", name: "_next/data catchall", check: true, fn: async (req, res, params, _parsedUrl)=>{ const isNextDataNormalizing = (0, _requestmeta.getRequestMeta)(req, "_nextDataNormalizing"); // Make sure to 404 for /_next/data/ itself and // we also want to 404 if the buildId isn't correct if (!params.path || params.path[0] !== this.buildId) { if (isNextDataNormalizing) { return { finished: false }; } await this.render404(req, res, _parsedUrl); return { finished: true }; } // remove buildId from URL params.path.shift(); const lastParam = params.path[params.path.length - 1]; // show 404 if it doesn't end with .json if (typeof lastParam !== "string" || !lastParam.endsWith(".json")) { await this.render404(req, res, _parsedUrl); return { finished: true }; } // re-create page's pathname let pathname = `/${params.path.join("/")}`; pathname = (0, _getroutefromassetpath.default)(pathname, ".json"); // ensure trailing slash is normalized per config if (this.router.hasMiddleware) { if (this.nextConfig.trailingSlash && !pathname.endsWith("/")) { pathname += "/"; } if (!this.nextConfig.trailingSlash && pathname.length > 1 && pathname.endsWith("/")) { pathname = pathname.substring(0, pathname.length - 1); } } if (this.i18nProvider) { var _req_headers_host; // Remove the port from the hostname if present. const hostname = (_req_headers_host = req == null ? void 0 : req.headers.host) == null ? void 0 : _req_headers_host.split(":")[0].toLowerCase(); const domainLocale = this.i18nProvider.detectDomainLocale(hostname); const defaultLocale = (domainLocale == null ? void 0 : domainLocale.defaultLocale) ?? this.i18nProvider.config.defaultLocale; const localePathResult = this.i18nProvider.analyze(pathname); // If the locale is detected from the path, we need to remove it // from the pathname. if (localePathResult.detectedLocale) { pathname = localePathResult.pathname; } // Update the query with the detected locale and default locale. _parsedUrl.query.__nextLocale = localePathResult.detectedLocale; _parsedUrl.query.__nextDefaultLocale = defaultLocale; // If the locale is not detected from the path, we need to mark that // it was not inferred from default. if (!_parsedUrl.query.__nextLocale) { delete _parsedUrl.query.__nextInferredLocaleFromDefault; } // If no locale was detected and we don't have middleware, we need // to render a 404 page. // NOTE: (wyattjoh) we may need to change this for app/ if (!localePathResult.detectedLocale && !this.router.hasMiddleware) { _parsedUrl.query.__nextLocale = defaultLocale; await this.render404(req, res, _parsedUrl); return { finished: true }; } } return { pathname, query: { ..._parsedUrl.query, __nextDataReq: "1" }, finished: false }; } }, ...imageRoutes, { match: (0, _pathmatch.getPathMatch)("/_next/:path*"), type: "route", name: "_next catchall", // This path is needed because `render()` does a check for `/_next` and the calls the routing again fn: async (req, res, _params, parsedUrl)=>{ await this.render404(req, res, parsedUrl); return { finished: true }; } }, ...publicRoutes, ...staticFilesRoutes ]; const caseSensitiveRoutes = !!this.nextConfig.experimental.caseSensitiveRoutes; const restrictedRedirectPaths = this.nextConfig.basePath ? [ `${this.nextConfig.basePath}/_next` ] : [ "/_next" ]; // Headers come very first const headers = this.minimalMode || this.isRenderWorker ? [] : this.customRoutes.headers.map((rule)=>(0, _serverrouteutils.createHeaderRoute)({ rule, restrictedRedirectPaths, caseSensitive: caseSensitiveRoutes })); const redirects = this.minimalMode || this.isRenderWorker ? [] : this.customRoutes.redirects.map((rule)=>(0, _serverrouteutils.createRedirectRoute)({ rule, restrictedRedirectPaths, caseSensitive: caseSensitiveRoutes })); const rewrites = this.generateRewrites({ restrictedRedirectPaths }); const catchAllMiddleware = this.generateCatchAllMiddlewareRoute(); const catchAllRoute = { match: (0, _pathmatch.getPathMatch)("/:path*"), type: "route", matchesLocale: true, name: "Catchall render", fn: async (req, res, _params, parsedUrl)=>{ var _this_i18nProvider; let { pathname , query } = parsedUrl; if (!pathname) { throw new Error("pathname is undefined"); } const bubbleNoFallback = Boolean(query._nextBubbleNoFallback); // next.js core assumes page path without trailing slash pathname = (0, _removetrailingslash.removeTrailingSlash)(pathname); const options = { i18n: (_this_i18nProvider = this.i18nProvider) == null ? void 0 : _this_i18nProvider.fromQuery(pathname, query) }; const match = await this.matchers.match(pathname, options); if (this.isRouterWorker) { var _this_appPathRoutes, _this_renderWorkers; let page = pathname; let matchedExistingRoute = false; if (!await this.hasPage(page)) { for (const route of this.dynamicRoutes || []){ if (route.match(pathname)) { page = route.page; matchedExistingRoute = true; break; } } } else { matchedExistingRoute = true; } let renderKind = ((_this_appPathRoutes = this.appPathRoutes) == null ? void 0 : _this_appPathRoutes[page]) || // Possible that it's a dynamic app route or behind routing rules // such as i18n. In that case, we need to check the route kind directly. (match == null ? void 0 : match.definition.kind) === _routekind.RouteKind.APP_PAGE ? "app" : "pages"; // Handle app dir's /not-found feature: for 404 pages, they should be // routed to the app renderer. if (!matchedExistingRoute && this.appPathRoutes) { if (this.appPathRoutes[this.renderOpts.dev ? "/not-found" : "/_not-found"]) { renderKind = "app"; } } if (this.renderWorkersPromises) { await this.renderWorkersPromises; this.renderWorkersPromises = undefined; } const renderWorker = (_this_renderWorkers = this.renderWorkers) == null ? void 0 : _this_renderWorkers[renderKind]; if (renderWorker) { var _this_localeNormalizer, _getRequestMeta; const initUrl = (0, _requestmeta.getRequestMeta)(req, "__NEXT_INIT_URL"); const { port , hostname } = await renderWorker.initialize(this.renderWorkerOpts); const renderUrl = new URL(initUrl); renderUrl.hostname = hostname; renderUrl.port = port + ""; let invokePathname = pathname; const normalizedInvokePathname = (_this_localeNormalizer = this.localeNormalizer) == null ? void 0 : _this_localeNormalizer.normalize(pathname); if (normalizedInvokePathname == null ? void 0 : normalizedInvokePathname.startsWith("/api")) { invokePathname = normalizedInvokePathname; } else if (query.__nextLocale && !(0, _pathhasprefix.pathHasPrefix)(invokePathname, `/${query.__nextLocale}`)) { invokePathname = `/${query.__nextLocale}${invokePathname === "/" ? "" : invokePathname}`; } if (query.__nextDataReq) { invokePathname = `/_next/data/${this.buildId}${invokePathname}.json`; } invokePathname = (0, _addpathprefix.addPathPrefix)(invokePathname, this.nextConfig.basePath); const keptQuery = {}; for (const key of Object.keys(query)){ if (key.startsWith("__next") || key.startsWith("_next")) { continue; } keptQuery[key] = query[key]; } if (query._nextBubbleNoFallback) { keptQuery._nextBubbleNoFallback = "1"; } const invokeQuery = JSON.stringify(keptQuery); const invokeHeaders = { "cache-control": "", ...req.headers, "x-middleware-invoke": "", "x-invoke-path": invokePathname, "x-invoke-query": encodeURIComponent(invokeQuery) }; req.didInvokePath = true; const invokeRes = await (0, _invokerequest.invokeRequest)(renderUrl.toString(), { headers: invokeHeaders, method: req.method }, (_getRequestMeta = (0, _requestmeta.getRequestMeta)(req, "__NEXT_CLONABLE_BODY")) == null ? void 0 : _getRequestMeta.cloneBodyStream()); // if this is an upgrade request just pipe body back if (!res.setHeader) { invokeRes.pipe(res); return { finished: true }; } const noFallback = invokeRes.headers["x-no-fallback"]; if (noFallback) { if (bubbleNoFallback) { return { finished: false }; } else { await this.render404(req, res, parsedUrl); return { finished: true }; } } for (const [key, value] of Object.entries((0, _utils2.filterReqHeaders)({ ...invokeRes.headers }))){ if (value !== undefined) { if (key === "set-cookie") { const curValue = res.getHeader(key); const newValue = []; for (const cookie of (0, _utils1.splitCookiesString)(curValue || "")){ newValue.push(cookie); } for (const val of Array.isArray(value) ? value : value ? [ value ] : []){ newValue.push(val); } res.setHeader(key, newValue); } else { res.setHeader(key, value); } } } res.statusCode = invokeRes.statusCode; res.statusMessage = invokeRes.statusMessage; const { originalResponse } = res; for await (const chunk of invokeRes){ if (originalResponse.closed) break; this.streamResponseChunk(originalResponse, chunk); } res.send(); return { finished: true }; } } // Try to handle the given route with the configured handlers. if (match) { // Add the match to the request so we don't have to re-run the matcher // for the same request. (0, _requestmeta.addRequestMeta)(req, "_nextMatch", match); // TODO-APP: move this to a route handler const edgeFunctionsPages = this.getEdgeFunctionsPages(); for (const edgeFunctionsPage of edgeFunctionsPages){ if (edgeFunctionsPage === match.definition.page) { if (this.nextConfig.output === "export") { await this.render404(req, res, parsedUrl); return { finished: true }; } delete query._nextBubbleNoFallback; delete query[_approuterheaders.NEXT_RSC_UNION_QUERY]; const handledAsEdgeFunction = await this.runEdgeFunction({ req, res, query, params: match.params, page: match.definition.page, match, appPaths: null }); if (handledAsEdgeFunction) { return { finished: true }; } } } let handled = false; // If the route was detected as being a Pages API route, then handle // it. // TODO: move this behavior into a route handler. if (match.definition.kind === _routekind.RouteKind.PAGES_API) { if (this.nextConfig.output === "export") { await this.render404(req, res, parsedUrl); return { finished: true }; } delete query._nextBubbleNoFallback; handled = await this.handleApiRequest(req, res, query, // TODO: see if we can add a runtime check for this match); if (handled) return { finished: true }; } // else if (match.definition.kind === RouteKind.METADATA_ROUTE) { // handled = await this.handlers.handle(match, req, res) // if (handled) return { finished: true } // } } try { await this.render(req, res, pathname, query, parsedUrl, true); return { finished: true }; } catch (err) { if (err instanceof _baseserver.NoFallbackError && bubbleNoFallback) { if (this.isRenderWorker) { res.setHeader("x-no-fallback", "1"); res.send(); return { finished: true }; } return { finished: false }; } throw err; } } }; const { useFileSystemPublicRoutes } = this.nextConfig; if (useFileSystemPublicRoutes) { this.appPathRoutes = this.getAppPathRoutes(); } return { headers, fsRoutes, rewrites, redirects, catchAllRoute, catchAllMiddleware, useFileSystemPublicRoutes, matchers: this.matchers, nextConfig: this.nextConfig, i18nProvider: this.i18nProvider }; } /** * Resolves `API` request, in development builds on demand * @param req http request * @param res http response * @param pathname path of request */ async handleApiRequest(req, res, query, match) { const { definition: { pathname , filename } , params } = match; return this.runApi(req, res, query, params, pathname, filename); } getCacheFilesystem() { return _nodefsmethods.nodeFs; } normalizeReq(req) { return req instanceof _http.IncomingMessage ? new _node.NodeNextRequest(req) : req; } normalizeRes(res) { return res instanceof _http.ServerResponse ? new _node.NodeNextResponse(res) : res; } getRequestHandler() { // This is just optimization to fire prepare as soon as possible // It will be properly awaited later void this.prepare(); const handler = super.getRequestHandler(); return async (req, res, parsedUrl)=>{ const normalizedReq = this.normalizeReq(req); const normalizedRes = this.normalizeRes(res); if (this.renderOpts.dev) { const _req = req; const _res = res; const origReq = "originalRequest" in _req ? _req.originalRequest : _req; const origRes = "originalResponse" in _res ? _res.originalResponse : _res; const reqStart = Date.now(); const reqCallback = ()=>{ // if we already logged in a render worker // don't log again in the router worker. // we also don't log for middleware alone if (normalizedReq.didInvokePath || origReq.headers["x-middleware-invoke"]) { return; } const reqEnd = Date.now(); const fetchMetrics = normalizedReq.fetchMetrics || []; const reqDuration = reqEnd - reqStart; const getDurationStr = (duration)=>{ let durationStr = duration.toString(); if (duration < 500) { durationStr = _chalk.default.green(duration + "ms"); } else if (duration < 2000) { durationStr = _chalk.default.yellow(duration + "ms"); } else { durationStr = _chalk.default.red(duration + "ms"); } return durationStr; }; if (Array.isArray(fetchMetrics) && fetchMetrics.length) { process.stdout.write("\n"); process.stdout.write(`- ${_chalk.default.grey("┌")} ${_chalk.default.cyan(req.method || "GET")} ${req.url} ${res.statusCode} in ${getDurationStr(reqDuration)}\n`); const calcNestedLevel = (prevMetrics, start)=>{ let nestedLevel = 0; for(let i = 0; i < prevMetrics.length; i++){ const metric = prevMetrics[i]; const prevMetric = prevMetrics[i - 1]; if (metric.end <= start && !(prevMetric && prevMetric.start < metric.end)) { nestedLevel += 1; } } if (nestedLevel === 0) return ""; return ` ${nestedLevel} level${nestedLevel === 1 ? "" : "s"} `; }; for(let i = 0; i < fetchMetrics.length; i++){ const metric = fetchMetrics[i]; const lastItem = i === fetchMetrics.length - 1; let cacheStatus = metric.cacheStatus; const duration = metric.end - metric.start; if (cacheStatus === "hit") { cacheStatus = _chalk.default.green("HIT"); } else { cacheStatus = _chalk.default.yellow("MISS"); } let url = metric.url; if (url.length > 48) { const parsed = new URL(url); const truncatedHost = parsed.host.length > 16 ? parsed.host.substring(0, 16) + ".." : parsed.host; const truncatedPath = parsed.pathname.length > 24 ? parsed.pathname.substring(0, 24) + ".." : parsed.pathname; const truncatedSearch = parsed.search.length > 16 ? parsed.search.substring(0, 16) + ".." : parsed.search; url = parsed.protocol + "//" + truncatedHost + truncatedPath + truncatedSearch; } process.stdout.write(` ${_chalk.default.grey("│")}\n`); process.stdout.write(` ${_chalk.default.grey(`${lastItem ? "└" : "├"}──${calcNestedLevel(fetchMetrics.slice(0, i), metric.start)}──`)} ${_chalk.default.cyan(metric.method)} ${url} ${metric.status} in ${getDurationStr(duration)} (cache: ${cacheStatus})\n`); } process.stdout.write("\n"); } else if (this.nextConfig.experimental.logging === "verbose") { process.stdout.write(`- ${_chalk.default.cyan(req.method || "GET")} ${req.url} ${res.statusCode} in ${getDurationStr(reqDuration)}\n`); } origRes.off("close", reqCallback); }; origRes.on("close", reqCallback); } return handler(normalizedReq, normalizedRes, parsedUrl); }; } async revalidate({ urlPath , revalidateHeaders , opts }) { const mocked = (0, _mockrequest.createRequestResponseMocks)({ url: urlPath, headers: revalidateHeaders }); const handler = this.getRequestHandler(); await handler(new _node.NodeNextRequest(mocked.req), new _node.NodeNextResponse(mocked.res)); await mocked.res.hasStreamed; if (mocked.res.getHeader("x-nextjs-cache") !== "REVALIDATED" && !(mocked.res.statusCode === 404 && opts.unstable_onlyGenerated)) { throw new Error(`Invalid response ${mocked.res.statusCode}`); } return {}; } async render(req, res, pathname, query, parsedUrl, internal = false) { return super.render(this.normalizeReq(req), this.normalizeRes(res), pathname, query, parsedUrl, internal); } async renderToHTML(req, res, pathname, query) { return super.renderToHTML(this.normalizeReq(req), this.normalizeRes(res), pathname, query); } async renderError(err, req, res, pathname, query, setHeaders) { return super.renderError(err, this.normalizeReq(req), this.normalizeRes(res), pathname, query, setHeaders); } async renderErrorToHTML(err, req, res, pathname, query) { return super.renderErrorToHTML(err, this.normalizeReq(req), this.normalizeRes(res), pathname, query); } async render404(req, res, parsedUrl, setHeaders) { return super.render404(this.normalizeReq(req), this.normalizeRes(res), parsedUrl, setHeaders); } async serveStatic(req, res, path, parsedUrl) { if (!this.isServableUrl(path)) { return this.render404(req, res, parsedUrl); } if (!(req.method === "GET" || req.method === "HEAD")) { res.statusCode = 405; res.setHeader("Allow", [ "GET", "HEAD" ]); return this.renderError(null, req, res, path); } try { await this.sendStatic(req, res, path); } catch (error) { if (!(0, _iserror.default)(error)) throw error; const err = error; if (err.code === "ENOENT" || err.statusCode === 404) { this.render404(req, res, parsedUrl); } else if (typeof err.statusCode === "number" && POSSIBLE_ERROR_CODE_FROM_SERVE_STATIC.has(err.statusCode)) { res.statusCode = err.statusCode; return this.renderError(err, req, res, path); } else if (err.expose === false) { res.statusCode = 400; return this.renderError(null, req, res, path); } else { throw err; } } } getStaticRoutes() { return this.hasStaticDir ? [ { // It's very important to keep this route's param optional. // (but it should support as many params as needed, separated by '/') // Otherwise this will lead to a pretty simple DOS attack. // See more: https://github.com/vercel/next.js/issues/2617 match: (0, _pathmatch.getPathMatch)("/static/:path*"), name: "static catchall", fn: async (req, res, params, parsedUrl)=>{ const p = (0, _path.join)(this.dir, "static", ...params.path); await this.serveStatic(req, res, p, parsedUrl); return { finished: true }; } } ] : []; } isServableUrl(untrustedFileUrl) { // This method mimics what the version of `send` we use does: // 1. decodeURIComponent: // https://github.com/pillarjs/send/blob/0.17.1/index.js#L989 // https://github.com/pillarjs/send/blob/0.17.1/index.js#L518-L522 // 2. resolve: // https://github.com/pillarjs/send/blob/de073ed3237ade9ff71c61673a34474b30e5d45b/index.js#L561 let decodedUntrustedFilePath; try { // (1) Decode the URL so we have the proper file name decodedUntrustedFilePath = decodeURIComponent(untrustedFileUrl); } catch { return false; } // (2) Resolve "up paths" to determine real request const untrustedFilePath = (0, _path.resolve)(decodedUntrustedFilePath); // don't allow null bytes anywhere in the file path if (untrustedFilePath.indexOf("\x00") !== -1) { return false; } // Check if .next/static, static and public are in the path. // If not the path is not available. if ((untrustedFilePath.startsWith((0, _path.join)(this.distDir, "static") + _path.sep) || untrustedFilePath.startsWith((0, _path.join)(this.dir, "static") + _path.sep) || untrustedFilePath.startsWith((0, _path.join)(this.dir, "public") + _path.sep)) === false) { return false; } // Check against the real filesystem paths const filesystemUrls = this.getFilesystemPaths(); const resolved = (0, _path.relative)(this.dir, untrustedFilePath); return filesystemUrls.has(resolved); } generateRewrites({ restrictedRedirectPaths }) { let beforeFiles = []; let afterFiles = []; let fallback = []; if (!this.minimalMode && !this.isRenderWorker) { const buildRewrite = (rewrite, check = true)=>{ const rewriteRoute = (0, _serverrouteutils.getCustomRoute)({ type: "rewrite", rule: rewrite, restrictedRedirectPaths, caseSensitive: !!this.nextConfig.experimental.caseSensitiveRoutes }); return { ...rewriteRoute, check, type: rewriteRoute.type, name: `Rewrite route ${rewriteRoute.source}`, match: rewriteRoute.match, matchesBasePath: true, matchesLocale: true, matchesLocaleAPIRoutes: true, matchesTrailingSlash: true, fn: async (req, res, params, parsedUrl, upgradeHead)=>{ const { newUrl , parsedDestination } = (0, _preparedestination.prepareDestination)({ appendParamsToQuery: true, destination: rewriteRoute.destination, params: params, query: parsedUrl.query }); // external rewrite, proxy it if (parsedDestination.protocol) { return this.proxyRequest(req, res, parsedDestination, upgradeHead); } (0, _requestmeta.addRequestMeta)(req, "_nextRewroteUrl", newUrl); (0, _requestmeta.addRequestMeta)(req, "_nextDidRewrite", newUrl !== req.url); // Analyze the destination url to update the locale in the query if // it is enabled. if (this.i18nProvider) { // Base path should be stripped before we analyze the destination // url for locales if it is enabled. let pathname = newUrl; if (this.nextConfig.basePath) { pathname = (0, _removepathprefix.removePathPrefix)(pathname, this.nextConfig.basePath); } // Assume the default locale from the query. We do this to ensure // that if the rewrite is specified without a locale we can // fallback to the correct locale. The domain didn't change, so // we can use the same default as before. const defaultLocale = parsedUrl.query.__nextDefaultLocale; // Analyze the pathname to see if it detects a locale. const { detectedLocale , inferredFromDefault } = this.i18nProvider.analyze(pathname, { defaultLocale }); // We update the locale in the query if it is detected. If it // wasn't detected it will fallback to the default locale. parsedUrl.query.__nextLocale = detectedLocale; // Mark if the locale was inferred from the default locale. if (inferredFromDefault) { parsedUrl.query.__nextInferredLocaleFromDefault = "1"; } else { delete parsedUrl.query.__nextInferredLocaleFromDefault; } } return { finished: false, pathname: newUrl, query: parsedDestination.query }; } }; }; if (Array.isArray(this.customRoutes.rewrites)) { afterFiles = this.customRoutes.rewrites.map((r)=>buildRewrite(r)); } else { beforeFiles = this.customRoutes.rewrites.beforeFiles.map((r)=>buildRewrite(r, false)); afterFiles = this.customRoutes.rewrites.afterFiles.map((r)=>buildRewrite(r)); fallback = this.customRoutes.rewrites.fallback.map((r)=>buildRewrite(r)); } } return { beforeFiles, afterFiles, fallback }; } getMiddlewareManifest() { if (this.minimalMode) return null; const manifest = require((0, _path.join)(this.serverDistDir, _constants.MIDDLEWARE_MANIFEST)); return manifest; } /** Returns the middleware routing item if there is one. */ getMiddleware() { var _manifest_middleware; const manifest = this.getMiddlewareManifest(); const middleware = manifest == null ? void 0 : (_manifest_middleware = manifest.middleware) == null ? void 0 : _manifest_middleware["/"]; if (!middleware) { return; } return { match: getMiddlewareMatcher(middleware), page: "/" }; } getEdgeFunctionsPages() { const manifest = this.getMiddlewareManifest(); if (!manifest) { return []; } return Object.keys(manifest.functions); } /** * Get information for the edge function located in the provided page * folder. If the edge function info can't be found it will throw * an error. */ getEdgeFunctionInfo(params) { const manifest = this.getMiddlewareManifest(); if (!manifest) { return null; } let foundPage; try { foundPage = (0, _denormalizepagepath.denormalizePagePath)((0, _normalizepagepath.normalizePagePath)(params.page)); } catch (err) { return null; } let pageInfo = params.middleware ? manifest.middleware[foundPage] : manifest.functions[foundPage]; if (!pageInfo) { if (!params.middleware) { throw new _utils.PageNotFoundError(foundPage); } return null; } return { name: pageInfo.name, paths: pageInfo.files.map((file)=>(0, _path.join)(this.distDir, file)), wasm: (pageInfo.wasm ?? []).map((binding)=>({ ...binding, filePath: (0, _path.join)(this.distDir, binding.filePath) })), assets: (pageInfo.assets ?? []).map((binding)=>{ return { ...binding, filePath: (0, _path.join)(this.distDir, binding.filePath) }; }) }; } /** * Checks if a middleware exists. This method is useful for the development * server where we need to check the filesystem. Here we just check the * middleware manifest. */ async hasMiddleware(pathname) { const info = this.getEdgeFunctionInfo({ page: pathname, middleware: true }); return Boolean(info && info.paths.length > 0); } /** * A placeholder for a function to be defined in the development server. * It will make sure that the root middleware or an edge function has been compiled * so that we can run it. */ async ensureMiddleware() {} async ensureEdgeFunction(_params) {} /** * This method gets all middleware matchers and execute them when the request * matches. It will make sure that each middleware exists and is compiled and * ready to be invoked. The development server will decorate it to add warns * and errors with rich traces. */ async runMiddleware(params) { // Middleware is skipped for on-demand revalidate requests if ((0, _apiutils.checkIsOnDemandRevalidate)(params.request, this.renderOpts.previewProps).isOnDemandRevalidate) { return { finished: false }; } let url; if (this.nextConfig.skipMiddlewareUrlNormalize) { url = (0, _requestmeta.getRequestMeta)(params.request, "__NEXT_INIT_URL"); } else { // For middleware to "fetch" we must always provide an absolute URL const query = (0, _querystring.urlQueryToSearchParams)(params.parsed.query).toString(); const locale = params.parsed.query.__nextLocale; url = `${(0, _requestmeta.getRequestMeta)(params.request, "_protocol")}://${this.hostname}:${this.port}${locale ? `/${locale}` : ""}${params.parsed.pathname}${query ? `?${query}` : ""}`; } if (!url.startsWith("http")) { throw new Error("To use middleware you must provide a `hostname` and `port` to the Next.js Server"); } const page = {}; const middleware = this.getMiddleware(); if (!middleware) { return { finished: false }; } if (!await this.hasMiddleware(middleware.page)) { return { finished: false }; } await this.ensureMiddleware(); const middlewareInfo = this.getEdgeFunctionInfo({ page: middleware.page, middleware: true }); if (!middlewareInfo) { throw new _utils.MiddlewareNotFoundError(); } const method = (params.request.method || "GET").toUpperCase(); const { run } = require("./web/sandbox"); const result = await run({ distDir: this.distDir, name: middlewareInfo.name, paths: middlewareInfo.paths, edgeFunctionEntry: middlewareInfo, request: { headers: params.request.headers, method, nextConfig: { basePath: this.nextConfig.basePath, i18n: this.nextConfig.i18n, trailingSlash: this.nextConfig.trailingSlash }, url: url, page: page, body: (0, _requestmeta.getRequestMeta)(params.request, "__NEXT_CLONABLE_BODY"), signal: (0, _nextrequest.signalFromNodeRequest)(params.request.originalRequest) }, useCache: true, onWarning: params.onWarning }); if (!this.renderOpts.dev) { result.waitUntil.catch((error)=>{ console.error(`Uncaught: middleware waitUntil errored`, error); }); } if (!result) { this.render404(params.request, params.response, params.parsed); return { finished: true }; } for (let [key, value] of result.response.headers){ if (key.toLowerCase() !== "set-cookie") continue; // Clear existing header. result.response.headers.delete(key); // Append each cookie individually. const cookies = (0, _utils1.splitCookiesString)(value); for (const cookie of cookies){ result.response.headers.append(key, cookie); } // Add cookies to request meta. (0, _requestmeta.addRequestMeta)(params.request, "_nextMiddlewareCookie", cookies); } return result; } generateCatchAllMiddlewareRoute(devReady) { if (this.minimalMode) return []; const routes = []; if (!this.renderOpts.dev || devReady) { if (this.getMiddleware()) { const middlewareCatchAllRoute = { match: (0, _pathmatch.getPathMatch)("/:path*"), matchesBasePath: true, matchesLocale: true, type: "route", name: "middleware catchall", fn: async (req, res, _params, parsed)=>{ const isMiddlewareInvoke = this.isRenderWorker && req.headers["x-middleware-invoke"]; const handleFinished = (finished = false)=>{ if (isMiddlewareInvoke && !finished) { res.setHeader("x-middleware-invoke", "1"); res.body("").send(); return { finished: true }; } return { finished }; }; if (this.isRenderWorker && !isMiddlewareInvoke) { return { finished: false }; } const middleware = this.getMiddleware(); if (!middleware) { return handleFinished(); } const initUrl = (0, _requestmeta.getRequestMeta)(req, "__NEXT_INIT_URL"); const parsedUrl = (0, _parseurl.parseUrl)(initUrl); const pathnameInfo = (0, _getnextpathnameinfo.getNextPathnameInfo)(parsedUrl.pathname, { nextConfig: this.nextConfig, i18nProvider: this.i18nProvider }); parsedUrl.pathname = pathnameInfo.pathname; const normalizedPathname = (0, _removetrailingslash.removeTrailingSlash)(parsed.pathname || ""); if (!middleware.match(normalizedPathname, req, parsedUrl.query)) { return handleFinished(); } let result; try { var _this_renderWorkers; await this.ensureMiddleware(); if (this.isRouterWorker && ((_this_renderWorkers = this.renderWorkers) == null ? void 0 : _this_renderWorkers.middleware)) { var _getRequestMeta; if (this.renderWorkersPromises) { await this.renderWorkersPromises; this.renderWorkersPromises = undefined; } const { port , hostname } = await this.renderWorkers.middleware.initialize(this.renderWorkerOpts); const renderUrl = new URL(initUrl); renderUrl.hostname = hostname; renderUrl.port = port + ""; const invokeHeaders = { ...req.headers, "x-invoke-path": "", "x-invoke-query": "", "x-middleware-invoke": "1" }; const invokeRes = await (0, _invokerequest.invokeRequest)(renderUrl.toString(), { headers: invokeHeaders, method: req.method }, (_getRequestMeta = (0, _requestmeta.getRequestMeta)(req, "__NEXT_CLONABLE_BODY")) == null ? void 0 : _getRequestMeta.cloneBodyStream()); const webResponse = new Response(null, { status: invokeRes.statusCode, headers: new Headers(invokeRes.headers) }); webResponse.invokeRes = invokeRes; result = { response: webResponse, waitUntil: Promise.resolve() }; for (const key of [ ...result.response.headers.keys() ]){ if ([ "content-encoding", "transfer-encoding", "keep-alive", "connection" ].includes(key)) { result.response.headers.delete(key); } else { const value = result.response.headers.get(key); // propagate this to req headers so it's // passed to the render worker for the page req.headers[key] = value || undefined; if (key.toLowerCase() === "set-cookie" && value) { (0, _requestmeta.addRequestMeta)(req, "_nextMiddlewareCookie", (0, _utils1.splitCookiesString)(value)); } } } } else { result = await this.runMiddleware({ request: req, response: res, parsedUrl: parsedUrl, parsed: parsed }); if (isMiddlewareInvoke && "response" in result) { for (const [key, value] of Object.entries((0, _utils1.toNodeOutgoingHttpHeaders)(result.response.headers))){ if (key !== "content-encoding" && value !== undefined) { res.setHeader(key, value); } } res.statusCode = result.response.status; const { originalResponse } = res; for await (const chunk of result.response.body || []){ if (originalResponse.closed) break; this.streamResponseChunk(originalResponse, chunk); } res.send(); return { finished: true }; } } } catch (err) { if ((0, _iserror.default)(err) && err.code === "ENOENT") { await this.render404(req, res, parsed); return { finished: true }; } if (err instanceof _utils.DecodeError) { res.statusCode = 400; this.renderError(err, req, res, parsed.pathname || ""); return { finished: true }; } const error = (0, _iserror.getProperError)(err); console.error(error); res.statusCode = 500; this.renderError(error, req, res, parsed.pathname || ""); return { finished: true }; } if ("finished" in result) { return result; } if (result.response.headers.has("x-middleware-rewrite")) { const value = result.response.headers.get("x-middleware-rewrite"); const rel = (0, _relativizeurl.relativizeURL)(value, initUrl); result.response.headers.set("x-middleware-rewrite", rel); } if (result.response.headers.has("x-middleware-override-headers")) { const overriddenHeaders = new Set(); for (const key of result.response.headers.get("x-middleware-override-headers").split(",")){ overriddenHeaders.add(key.trim()); } result.response.headers.delete("x-middleware-override-headers"); // Delete headers. for (const key of Object.keys(req.headers)){ if (!overriddenHeaders.has(key)) { delete req.headers[key]; } } // Update or add headers. for (const key of overriddenHeaders.keys()){ const valueKey = "x-middleware-request-" + key; const newValue = result.response.headers.get(valueKey); const oldValue = req.headers[key]; if (oldValue !== newValue) { req.headers[key] = newValue === null ? undefined : newValue; } result.response.headers.delete(valueKey); } } if (result.response.headers.has("Location")) { const value = result.response.headers.get("Location"); const rel = (0, _relativizeurl.relativizeURL)(value, initUrl); result.response.headers.set("Location", rel); } if (!result.response.headers.has("x-middleware-rewrite") && !result.response.headers.has("x-middleware-next") && !result.response.headers.has("Location")) { result.response.headers.set("x-middleware-refresh", "1"); } result.response.headers.delete("x-middleware-next"); for (const [key, value] of Object.entries((0, _utils1.toNodeOutgoingHttpHeaders)(result.response.headers))){ if ([ "x-middleware-rewrite", "x-middleware-redirect", "x-middleware-refresh" ].includes(key)) { continue; } if (key !== "content-encoding" && value !== undefined) { if (typeof value === "number") { res.setHeader(key, value.toString()); } else { res.setHeader(key, value); } } } res.statusCode = result.response.status; res.statusMessage = result.response.statusText; const location = result.response.headers.get("Location"); if (location) { res.statusCode = result.response.status; if (res.statusCode === 308) { res.setHeader("Refresh", `0;url=${location}`); } res.body(location).send(); return { finished: true }; } // If the middleware has set a `x-middleware-rewrite` header, we // need to rewrite the URL to the new path and re-run the request. if (result.response.headers.has("x-middleware-rewrite")) { const rewritePath = result.response.headers.get("x-middleware-rewrite"); const parsedDestination = (0, _parseurl.parseUrl)(rewritePath); const newUrl = parsedDestination.pathname; // If the destination has a protocol and host that doesn't match // the current request, we need to proxy the request to the // correct host. if (parsedDestination.protocol && (parsedDestination.port ? `${parsedDestination.hostname}:${parsedDestination.port}` : parsedDestination.hostname) !== req.headers.host) { return this.proxyRequest(req, res, parsedDestination); } // If this server has i18n enabled, we need to make sure to parse // the locale from the destination URL and add it to the query // string so that the next request is properly localized. if (this.i18nProvider) { const { detectedLocale } = this.i18nProvider.analyze(newUrl); if (detectedLocale) { parsedDestination.query.__nextLocale = detectedLocale; } } (0, _requestmeta.addRequestMeta)(req, "_nextRewroteUrl", newUrl); (0, _requestmeta.addRequestMeta)(req, "_nextDidRewrite", newUrl !== req.url); if (!isMiddlewareInvoke) { return { finished: false, pathname: newUrl, query: parsedDestination.query }; } } if (result.response.headers.has("x-middleware-refresh")) { res.statusCode = result.response.status; const { originalResponse } = res; const body = result.response.invokeRes || result.response.body || []; for await (const chunk of body){ if (originalResponse.closed) break; this.streamResponseChunk(originalResponse, chunk); } res.send(); return { finished: true }; } return { finished: false }; } }; routes.push(middlewareCatchAllRoute); } } return routes; } getPrerenderManifest() { var _this_renderOpts, _this_serverOptions, _this_renderWorkerOpts; if (this._cachedPreviewManifest) { return this._cachedPreviewManifest; } if (((_this_renderOpts = this.renderOpts) == null ? void 0 : _this_renderOpts.dev) || ((_this_serverOptions = this.serverOptions) == null ? void 0 : _this_serverOptions.dev) || ((_this_renderWorkerOpts = this.renderWorkerOpts) == null ? void 0 : _this_renderWorkerOpts.dev) || process.env.NODE_ENV === "development" || process.env.NEXT_PHASE === _constants.PHASE_PRODUCTION_BUILD) { this._cachedPreviewManifest = { version: 4, routes: {}, dynamicRoutes: {}, notFoundRoutes: [], preview: { previewModeId: require("crypto").randomBytes(16).toString("hex"), previewModeSigningKey: require("crypto").randomBytes(32).toString("hex"), previewModeEncryptionKey: require("crypto").randomBytes(32).toString("hex") } }; return this._cachedPreviewManifest; } const manifest = require((0, _path.join)(this.distDir, _constants.PRERENDER_MANIFEST)); return this._cachedPreviewManifest = manifest; } getRoutesManifest() { return (0, _tracer.getTracer)().trace(_constants2.NextNodeServerSpan.getRoutesManifest, ()=>require((0, _path.join)(this.distDir, _constants.ROUTES_MANIFEST))); } attachRequestMeta(req, parsedUrl, isUpgradeReq) { var _ref, _req_originalRequest; const protocol = ((_ref = (_req_originalRequest = req.originalRequest) == null ? void 0 : _req_originalRequest.socket) == null ? void 0 : _ref.encrypted) ? "https" : "http"; // When there are hostname and port we build an absolute URL const initUrl = this.hostname && this.port ? `${protocol}://${this.hostname}:${this.port}${req.url}` : this.nextConfig.experimental.trustHostHeader ? `https://${req.headers.host || "localhost"}${req.url}` : req.url; (0, _requestmeta.addRequestMeta)(req, "__NEXT_INIT_URL", initUrl); (0, _requestmeta.addRequestMeta)(req, "__NEXT_INIT_QUERY", { ...parsedUrl.query }); (0, _requestmeta.addRequestMeta)(req, "_protocol", protocol); if (!isUpgradeReq) { (0, _requestmeta.addRequestMeta)(req, "__NEXT_CLONABLE_BODY", (0, _bodystreams.getCloneableBody)(req.body)); } } async runEdgeFunction(params) { let edgeInfo; const { query , page , match } = params; if (!match) await this.ensureEdgeFunction({ page, appPaths: params.appPaths }); edgeInfo = this.getEdgeFunctionInfo({ page, middleware: false }); if (!edgeInfo) { return null; } // For edge to "fetch" we must always provide an absolute URL const isDataReq = !!query.__nextDataReq; const initialUrl = new URL((0, _requestmeta.getRequestMeta)(params.req, "__NEXT_INIT_URL") || "/", "http://n"); const queryString = (0, _querystring.urlQueryToSearchParams)({ ...Object.fromEntries(initialUrl.searchParams), ...query, ...params.params }).toString(); if (isDataReq) { params.req.headers["x-nextjs-data"] = "1"; } initialUrl.search = queryString; const url = initialUrl.toString(); if (!url.startsWith("http")) { throw new Error("To use middleware you must provide a `hostname` and `port` to the Next.js Server"); } const { run } = require("./web/sandbox"); const result = await run({ distDir: this.distDir, name: edgeInfo.name, paths: edgeInfo.paths, edgeFunctionEntry: edgeInfo, request: { headers: params.req.headers, method: params.req.method, nextConfig: { basePath: this.nextConfig.basePath, i18n: this.nextConfig.i18n, trailingSlash: this.nextConfig.trailingSlash }, url, page: { name: params.page, ...params.params && { params: params.params } }, body: (0, _requestmeta.getRequestMeta)(params.req, "__NEXT_CLONABLE_BODY"), signal: (0, _nextrequest.signalFromNodeRequest)(params.req.originalRequest) }, useCache: true, onWarning: params.onWarning, incrementalCache: globalThis.__incrementalCache || (0, _requestmeta.getRequestMeta)(params.req, "_nextIncrementalCache") }); params.res.statusCode = result.response.status; params.res.statusMessage = result.response.statusText; // TODO: (wyattjoh) investigate improving this result.response.headers.forEach((value, key)=>{ // The append handling is special cased for `set-cookie`. if (key.toLowerCase() === "set-cookie") { // TODO: (wyattjoh) replace with native response iteration when we can upgrade undici for (const cookie of (0, _utils1.splitCookiesString)(value)){ params.res.appendHeader(key, cookie); } } else { params.res.appendHeader(key, value); } }); const nodeResStream = params.res.originalResponse; if (result.response.body) { // TODO(gal): not sure that we always need to stream const { consumeUint8ArrayReadableStream } = require("next/dist/compiled/edge-runtime"); try { for await (const chunk of consumeUint8ArrayReadableStream(result.response.body)){ if (nodeResStream.closed) break; nodeResStream.write(chunk); } } finally{ nodeResStream.end(); } } else { nodeResStream.end(); } return result; } get serverDistDir() { return (0, _path.join)(this.distDir, _constants.SERVER_DIRECTORY); } } //# sourceMappingURL=next-server.js.map