UNPKG

next

Version:

The React Framework

839 lines (838 loc) • 152 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 _fallbackparams = require("./request/fallback-params"); const _responsecache = require("./response-cache"); const _utils = require("../shared/lib/utils"); const _url = require("url"); const _formathostname = require("./lib/format-hostname"); 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 _runtimeconfigexternal = require("../shared/lib/runtime-config.external"); const _cachecontrol = require("./lib/cache-control"); 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 _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("./normalizers/locale-route-normalizer"); const _defaultroutematchermanager = require("./route-matcher-managers/default-route-matcher-manager"); const _apppageroutematcherprovider = require("./route-matcher-providers/app-page-route-matcher-provider"); const _approuteroutematcherprovider = require("./route-matcher-providers/app-route-route-matcher-provider"); const _pagesapiroutematcherprovider = require("./route-matcher-providers/pages-api-route-matcher-provider"); const _pagesroutematcherprovider = require("./route-matcher-providers/pages-route-matcher-provider"); const _servermanifestloader = require("./route-matcher-providers/helpers/manifest-loaders/server-manifest-loader"); const _tracer = require("./lib/trace/tracer"); const _constants1 = require("./lib/trace/constants"); const _i18nprovider = require("./lib/i18n-provider"); const _sendresponse = require("./send-response"); 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"); const _matchnextdatapathname = require("./lib/match-next-data-pathname"); const _getroutefromassetpath = /*#__PURE__*/ _interop_require_default(require("../shared/lib/router/utils/get-route-from-asset-path")); const _decodepathparams = require("./lib/router-utils/decode-path-params"); const _rsc = require("./normalizers/request/rsc"); const _stripflightheaders = require("./app-render/strip-flight-headers"); const _checks = require("./route-modules/checks"); const _prefetchrsc = require("./normalizers/request/prefetch-rsc"); const _nextdata = require("./normalizers/request/next-data"); const _serveractionrequestmeta = require("./lib/server-action-request-meta"); const _interceptionroutes = require("../shared/lib/router/utils/interception-routes"); const _toroute = require("./lib/to-route"); const _helpers = require("./base-http/helpers"); const _patchsetheader = require("./lib/patch-set-header"); const _ppr = require("./lib/experimental/ppr"); const _builtinrequestcontext = require("./after/builtin-request-context"); const _encodedTags = require("./stream-utils/encodedTags"); const _adapter = require("./web/adapter"); const _utils4 = require("./instrumentation/utils"); const _routekind = require("./route-kind"); const _fallback = require("../lib/fallback"); const _utils5 = require("./response-cache/utils"); const _scheduler = require("../lib/scheduler"); const _segmentprefixrsc = require("./normalizers/request/segment-prefix-rsc"); const _streamingmetadata = require("./lib/streaming-metadata"); const _handlers = require("./use-cache/handlers"); const _invarianterror = require("../shared/lib/invariant-error"); 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 = { __proto__: null }; 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 { getServerComponentsHmrCache() { return this.nextConfig.experimental.serverComponentsHmrCache ? globalThis.__serverComponentsHmrCache : undefined; } /** * This is used to persist cache scopes across * prefetch -> full route requests for dynamic IO * it's only fully used in dev */ constructor(options){ var _this_nextConfig_i18n, _this_nextConfig_experimental_amp, _this_nextConfig_i18n1; this.handleRSCRequest = (req, _res, parsedUrl)=>{ var _this_normalizers_segmentPrefetchRSC, _this_normalizers_prefetchRSC, _this_normalizers_rsc; if (!parsedUrl.pathname) return false; if ((_this_normalizers_segmentPrefetchRSC = this.normalizers.segmentPrefetchRSC) == null ? void 0 : _this_normalizers_segmentPrefetchRSC.match(parsedUrl.pathname)) { const result = this.normalizers.segmentPrefetchRSC.extract(parsedUrl.pathname); if (!result) return false; const { originalPathname, segmentPath } = result; parsedUrl.pathname = originalPathname; // Mark the request as a router prefetch request. req.headers[_approuterheaders.RSC_HEADER.toLowerCase()] = '1'; req.headers[_approuterheaders.NEXT_ROUTER_PREFETCH_HEADER.toLowerCase()] = '1'; req.headers[_approuterheaders.NEXT_ROUTER_SEGMENT_PREFETCH_HEADER.toLowerCase()] = segmentPath; (0, _requestmeta.addRequestMeta)(req, 'isRSCRequest', true); (0, _requestmeta.addRequestMeta)(req, 'isPrefetchRSCRequest', true); (0, _requestmeta.addRequestMeta)(req, 'segmentPrefetchRSCRequest', segmentPath); } else if ((_this_normalizers_prefetchRSC = this.normalizers.prefetchRSC) == null ? void 0 : _this_normalizers_prefetchRSC.match(parsedUrl.pathname)) { parsedUrl.pathname = this.normalizers.prefetchRSC.normalize(parsedUrl.pathname, true); // Mark the request as a router prefetch request. req.headers[_approuterheaders.RSC_HEADER.toLowerCase()] = '1'; req.headers[_approuterheaders.NEXT_ROUTER_PREFETCH_HEADER.toLowerCase()] = '1'; (0, _requestmeta.addRequestMeta)(req, 'isRSCRequest', true); (0, _requestmeta.addRequestMeta)(req, 'isPrefetchRSCRequest', true); } else if ((_this_normalizers_rsc = this.normalizers.rsc) == null ? void 0 : _this_normalizers_rsc.match(parsedUrl.pathname)) { parsedUrl.pathname = this.normalizers.rsc.normalize(parsedUrl.pathname, true); // Mark the request as a RSC request. req.headers[_approuterheaders.RSC_HEADER.toLowerCase()] = '1'; (0, _requestmeta.addRequestMeta)(req, 'isRSCRequest', true); } else if (req.headers['x-now-route-matches']) { // If we didn't match, return with the flight headers stripped. If in // minimal mode we didn't match based on the path, this can't be a RSC // request. This is because Vercel only sends this header during // revalidation requests and we want the cache to instead depend on the // request path for flight information. (0, _stripflightheaders.stripFlightHeaders)(req.headers); return false; } else if (req.headers[_approuterheaders.RSC_HEADER.toLowerCase()] === '1') { (0, _requestmeta.addRequestMeta)(req, 'isRSCRequest', true); if (req.headers[_approuterheaders.NEXT_ROUTER_PREFETCH_HEADER.toLowerCase()] === '1') { (0, _requestmeta.addRequestMeta)(req, 'isPrefetchRSCRequest', true); const segmentPrefetchRSCRequest = req.headers[_approuterheaders.NEXT_ROUTER_SEGMENT_PREFETCH_HEADER.toLowerCase()]; if (typeof segmentPrefetchRSCRequest === 'string') { (0, _requestmeta.addRequestMeta)(req, 'segmentPrefetchRSCRequest', segmentPrefetchRSCRequest); } } } else { // Otherwise just return without doing anything. return false; } if (req.url) { const parsed = (0, _url.parse)(req.url); parsed.pathname = parsedUrl.pathname; req.url = (0, _url.format)(parsed); } return false; }; this.handleNextDataRequest = async (req, res, parsedUrl)=>{ const middleware = await this.getMiddleware(); const params = (0, _matchnextdatapathname.matchNextDataPathname)(parsedUrl.pathname); // ignore for non-next data URLs if (!params || !params.path) { return false; } if (params.path[0] !== this.buildId) { // Ignore if its a middleware request when we aren't on edge. if (process.env.NEXT_RUNTIME !== 'edge' && (0, _requestmeta.getRequestMeta)(req, 'middlewareInvoke')) { return false; } // Make sure to 404 if the buildId isn't correct await this.render404(req, res, parsedUrl); return 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 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 (middleware) { 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 == null ? void 0 : (_req_headers_host = req.headers.host) == null ? void 0 : _req_headers_host.split(':', 1)[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. (0, _requestmeta.addRequestMeta)(req, 'locale', localePathResult.detectedLocale); (0, _requestmeta.addRequestMeta)(req, 'defaultLocale', defaultLocale); // If the locale is not detected from the path, we need to mark that // it was not inferred from default. if (!localePathResult.detectedLocale) { (0, _requestmeta.removeRequestMeta)(req, 'localeInferredFromDefault'); } // If no locale was detected and we don't have middleware, we need // to render a 404 page. if (!localePathResult.detectedLocale && !middleware) { (0, _requestmeta.addRequestMeta)(req, 'locale', defaultLocale); await this.render404(req, res, parsedUrl); return true; } } parsedUrl.pathname = pathname; (0, _requestmeta.addRequestMeta)(req, 'isNextDataReq', true); return false; }; this.handleNextImageRequest = ()=>false; this.handleCatchallRenderRequest = ()=>false; this.handleCatchallMiddlewareRequest = ()=>false; /** * Normalizes a pathname without attaching any metadata from any matched * normalizer. * * @param pathname the pathname to normalize * @returns the normalized pathname */ this.normalize = (pathname)=>{ const normalizers = []; if (this.normalizers.data) { normalizers.push(this.normalizers.data); } // We have to put the segment prefetch normalizer before the RSC normalizer // because the RSC normalizer will match the prefetch RSC routes too. if (this.normalizers.segmentPrefetchRSC) { normalizers.push(this.normalizers.segmentPrefetchRSC); } // We have to put the prefetch normalizer before the RSC normalizer // because the RSC normalizer will match the prefetch RSC routes too. if (this.normalizers.prefetchRSC) { normalizers.push(this.normalizers.prefetchRSC); } if (this.normalizers.rsc) { normalizers.push(this.normalizers.rsc); } for (const normalizer of normalizers){ if (!normalizer.match(pathname)) continue; return normalizer.normalize(pathname, true); } return pathname; }; this.normalizeAndAttachMetadata = async (req, res, url)=>{ let finished = await this.handleNextImageRequest(req, res, url); if (finished) return true; if (this.enabledDirectories.pages) { finished = await this.handleNextDataRequest(req, res, url); if (finished) return true; } return false; }; 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, hostname, port, experimentalTestProxy } = options; this.experimentalTestProxy = experimentalTestProxy; this.serverOptions = options; 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; if (this.hostname) { // we format the hostname so that it can be fetched this.fetchHostname = (0, _formathostname.formatHostname)(this.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 is a hack to avoid Webpack knowing this is equal to this.minimalMode // because we replace this.minimalMode to true in production bundles. const minimalModeKey = 'minimalMode'; this[minimalModeKey] = minimalMode || !!process.env.NEXT_PRIVATE_MINIMAL_MODE; this.enabledDirectories = this.getEnabledDirectories(dev); this.isAppPPREnabled = this.enabledDirectories.app && (0, _ppr.checkIsAppPPREnabled)(this.nextConfig.experimental.ppr); this.isAppSegmentPrefetchEnabled = this.enabledDirectories.app && this.nextConfig.experimental.clientSegmentCache === true; this.normalizers = { // We should normalize the pathname from the RSC prefix only in minimal // mode as otherwise that route is not exposed external to the server as // we instead only rely on the headers. rsc: this.enabledDirectories.app && this.minimalMode ? new _rsc.RSCPathnameNormalizer() : undefined, prefetchRSC: this.isAppPPREnabled && this.minimalMode ? new _prefetchrsc.PrefetchRSCPathnameNormalizer() : undefined, segmentPrefetchRSC: this.isAppSegmentPrefetchEnabled && this.minimalMode ? new _segmentprefixrsc.SegmentPrefixRSCPathnameNormalizer() : undefined, data: this.enabledDirectories.pages ? new _nextdata.NextDataPathnameNormalizer(this.buildId) : undefined }; this.nextFontManifest = this.getNextFontManifest(); if (process.env.NEXT_RUNTIME !== 'edge') { process.env.NEXT_DEPLOYMENT_ID = this.nextConfig.deploymentId || ''; } this.renderOpts = { supportsDynamicResponse: true, trailingSlash: this.nextConfig.trailingSlash, deploymentId: this.nextConfig.deploymentId, strictNextHead: this.nextConfig.experimental.strictNextHead ?? true, poweredByHeader: this.nextConfig.poweredByHeader, canonicalBase: this.nextConfig.amp.canonicalBase || '', generateEtags, previewProps: this.getPrerenderManifest().preview, 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, 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: this.enabledDirectories.app, cacheLifeProfiles: this.nextConfig.experimental.cacheLife, enableTainting: this.nextConfig.experimental.taint, 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, // @ts-expect-error internal field not publicly exposed isExperimentalCompile: this.nextConfig.experimental.isExperimentalCompile, // `htmlLimitedBots` is passed to server as serialized config in string format htmlLimitedBots: this.nextConfig.htmlLimitedBots, experimental: { expireTime: this.nextConfig.expireTime, clientTraceMetadata: this.nextConfig.experimental.clientTraceMetadata, dynamicIO: this.nextConfig.experimental.dynamicIO ?? false, clientSegmentCache: this.nextConfig.experimental.clientSegmentCache ?? false, inlineCss: this.nextConfig.experimental.inlineCss ?? false, authInterrupts: !!this.nextConfig.experimental.authInterrupts }, onInstrumentationRequestError: this.instrumentationOnRequestError.bind(this), reactMaxHeadersLength: this.nextConfig.reactMaxHeadersLength }; // Initialize next/config with the environment configuration (0, _runtimeconfigexternal.setConfig)({ serverRuntimeConfig, publicRuntimeConfig }); this.pagesManifest = this.getPagesManifest(); this.appPathsManifest = this.getAppPathsManifest(); this.appPathRoutes = this.getAppPathRoutes(); this.interceptionRoutePatterns = this.getinterceptionRoutePatterns(); // Configure the routes. this.matchers = this.getRouteMatchers(); // 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. void this.matchers.reload(); this.setAssetPrefix(assetPrefix); this.responseCache = this.getResponseCache({ dev }); } reloadMatchers() { return this.matchers.reload(); } getRouteMatchers() { // 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.enabledDirectories.app) { // Match app pages under `app/`. matchers.push(new _apppageroutematcherprovider.AppPageRouteMatcherProvider(this.distDir, manifestLoader)); matchers.push(new _approuteroutematcherprovider.AppRouteRouteMatcherProvider(this.distDir, manifestLoader)); } return matchers; } async instrumentationOnRequestError(...args) { const [err, req, ctx] = args; if (this.instrumentation) { try { await (this.instrumentation.onRequestError == null ? void 0 : this.instrumentation.onRequestError.call(this.instrumentation, err, { path: req.url || '', method: req.method || 'GET', // Normalize middleware headers and other server request headers headers: req instanceof _adapter.NextRequestHint ? Object.fromEntries(req.headers.entries()) : req.headers }, ctx)); } catch (handlerErr) { // Log the soft error and continue, since errors can thrown from react stream handler console.error('Error in instrumentation.onRequestError:', handlerErr); } } } logError(err) { if (this.quiet) return; _log.error(err); } async handleRequest(req, res, parsedUrl) { await this.prepare(); const method = req.method.toUpperCase(); const tracer = (0, _tracer.getTracer)(); return tracer.withPropagatedContext(req.headers, ()=>{ return tracer.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; const isRSCRequest = (0, _requestmeta.getRequestMeta)(req, 'isRSCRequest') ?? false; span.setAttributes({ 'http.status_code': res.statusCode, 'next.rsc': isRSCRequest }); const rootSpanAttributes = tracer.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 name = isRSCRequest ? `RSC ${method} ${route}` : `${method} ${route}`; span.setAttributes({ 'next.route': route, 'http.route': route, 'next.span_name': name }); span.updateName(name); } else { span.updateName(isRSCRequest ? `RSC ${method} ${req.url}` : `${method} ${req.url}`); } })); }); } async handleRequestImpl(req, res, parsedUrl) { try { var _originalRequest_socket, _originalRequest_socket1, _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 (0, _patchsetheader.patchSetHeaderWithCookieSupport)(req, (0, _helpers.isNodeNextResponse)(res) ? res.originalResponse : res); const urlParts = (req.url || '').split('?', 1); 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; } // Parse url if parsedUrl not provided if (!parsedUrl || typeof parsedUrl !== 'object') { if (!req.url) { throw Object.defineProperty(new Error('Invariant: url can not be undefined'), "__NEXT_ERROR_CODE", { value: "E123", enumerable: false, configurable: true }); } parsedUrl = (0, _url.parse)(req.url, true); } if (!parsedUrl.pathname) { throw Object.defineProperty(new Error("Invariant: pathname can't be empty"), "__NEXT_ERROR_CODE", { value: "E412", enumerable: false, configurable: 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)); } // Update the `x-forwarded-*` headers. const { originalRequest = null } = (0, _helpers.isNodeNextRequest)(req) ? req : {}; const xForwardedProto = originalRequest == null ? void 0 : originalRequest.headers['x-forwarded-proto']; const isHttps = xForwardedProto ? xForwardedProto === 'https' : !!(originalRequest == null ? void 0 : (_originalRequest_socket = originalRequest.socket) == null ? void 0 : _originalRequest_socket.encrypted); req.headers['x-forwarded-host'] ??= req.headers['host'] ?? this.hostname; req.headers['x-forwarded-port'] ??= this.port ? this.port.toString() : isHttps ? '443' : '80'; req.headers['x-forwarded-proto'] ??= isHttps ? 'https' : 'http'; req.headers['x-forwarded-for'] ??= originalRequest == null ? void 0 : (_originalRequest_socket1 = originalRequest.socket) == null ? void 0 : _originalRequest_socket1.remoteAddress; // This should be done before any normalization of the pathname happens as // it captures the initial URL. this.attachRequestMeta(req, parsedUrl); let finished = await this.handleRSCRequest(req, res, parsedUrl); if (finished) return; 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); (0, _requestmeta.addRequestMeta)(req, 'defaultLocale', 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); } const useMatchedPathHeader = this.minimalMode && typeof req.headers[_constants2.MATCHED_PATH_HEADER] === 'string'; // TODO: merge handling with invokePath if (useMatchedPathHeader) { try { var _this_normalizers_data, _this_i18nProvider1, _this_getRoutesManifest; if (this.enabledDirectories.app) { // 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 { pathname: matchedPath } = new URL(req.headers[_constants2.MATCHED_PATH_HEADER], 'http://localhost'); let { pathname: urlPathname } = new URL(req.url, 'http://localhost'); // 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 ((_this_normalizers_data = this.normalizers.data) == null ? void 0 : _this_normalizers_data.match(urlPathname)) { (0, _requestmeta.addRequestMeta)(req, 'isNextDataReq', true); } else if (this.isAppPPREnabled && this.minimalMode && req.headers[_constants2.NEXT_RESUME_HEADER] === '1' && req.method === 'POST') { // Decode the postponed state from the request body, it will come as // an array of buffers, so collect them and then concat them to form // the string. const body = []; for await (const chunk of req.body){ body.push(chunk); } const postponed = Buffer.concat(body).toString('utf8'); (0, _requestmeta.addRequestMeta)(req, 'postponed', postponed); } matchedPath = this.normalize(matchedPath); const normalizedUrlPath = this.stripNextDataPath(urlPathname); // 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) { (0, _requestmeta.addRequestMeta)(req, 'locale', 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) { (0, _requestmeta.addRequestMeta)(req, 'localeInferredFromDefault', true); } else { (0, _requestmeta.removeRequestMeta)(req, 'localeInferredFromDefault'); } } // TODO: check if this is needed any more? matchedPath = (0, _denormalizepagepath.denormalizePagePath)(matchedPath); let srcPathname = matchedPath; let pageIsDynamic = (0, _utils1.isDynamicRoute)(srcPathname); if (!pageIsDynamic) { const match = await this.matchers.match(srcPathname, { 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. pageIsDynamic = typeof 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 rewriteParamKeys = Object.keys(utils.handleRewrites(req, parsedUrl)); const didRewrite = pathnameBeforeRewrite !== parsedUrl.pathname; if (didRewrite && parsedUrl.pathname) { (0, _requestmeta.addRequestMeta)(req, 'rewroteURL', parsedUrl.pathname); } // Create a copy of the query params to avoid mutating the original // object. This prevents any overlapping query params that have the // same normalized key from causing issues. const queryParams = { ...parsedUrl.query }; for (const [key, value] of Object.entries(parsedUrl.query)){ const normalizedKey = (0, _utils3.normalizeNextQueryParam)(key); if (!normalizedKey) continue; // Remove the prefixed key from the query params because we want // to consume it for the dynamic route matcher. delete parsedUrl.query[key]; if (typeof value === 'undefined') continue; queryParams[normalizedKey] = value; } // interpolate dynamic params and normalize URL if needed if (pageIsDynamic) { let params = {}; let paramsResult = utils.normalizeDynamicRouteParams(queryParams, false); // 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 && !(0, _utils1.isDynamicRoute)(normalizedUrlPath)) { let matcherParams = utils.dynamicRouteMatcher == null ? void 0 : utils.dynamicRouteMatcher.call(utils, normalizedUrlPath); if (matcherParams) { utils.normalizeDynamicRouteParams(matcherParams, false); Object.assign(paramsResult.params, matcherParams); paramsResult.hasValidParams = true; } } // if an action request is bypassing a prerender and we // don't have the params in the URL since it was prerendered // and matched during handle: 'filesystem' rather than dynamic route // resolving we need to parse the params from the matched-path. // Note: this is similar to above case but from match-path instead // of from the request URL since a rewrite could cause that to not // match the src pathname if (// we can have a collision with /index and a top-level /[slug] matchedPath !== '/index' && !paramsResult.hasValidParams && !(0, _utils1.isDynamicRoute)(matchedPath)) { let matcherParams = utils.dynamicRouteMatcher == null ? void 0 : utils.dynamicRouteMatcher.call(utils, matchedPath); if (matcherParams) { const curParamsResult = utils.normalizeDynamicRouteParams(matcherParams, false); if (curParamsResult.hasValidParams) { Object.assign(params, matcherParams); paramsResult = curParamsResult; } } } if (paramsResult.hasValidParams) { params = paramsResult.params; } const routeMatchesHeader = req.headers['x-now-route-matches']; if (typeof routeMatchesHeader === 'string' && routeMatchesHeader && (0, _utils1.isDynamicRoute)(matchedPath) && !paramsResult.hasValidParams) { const routeMatches = utils.getParamsFromRouteMatches(routeMatchesHeader); if (routeMatches) { paramsResult = utils.normalizeDynamicRouteParams(routeMatches, true); if (paramsResult.hasValidParams) { params = paramsResult.params; } } } // Try to parse the params from the query if we couldn't parse them // from the route matches but ignore missing optional params. if (!paramsResult.hasValidParams) { paramsResult = utils.normalizeDynamicRouteParams(queryParams, true); if (paramsResult.hasValidParams) { params = paramsResult.params; } } // handle the actual dynamic route name being requested if (utils.defaultRouteMatches && normalizedUrlPath === srcPathname && !paramsResult.hasValidParams && !utils.normalizeDynamicRouteParams({ ...params }, true).hasValidParams) { params = utils.defaultRouteMatches; // Mark that the default route matches were set on the request // during routing. (0, _requestmeta.addRequestMeta)(req, 'didSetDefaultRouteMatches', true); } if (params) { matchedPath = utils.interpolateDynamicPath(srcPathname, params); req.url = utils.interpolateDynamicPath(req.url, params); // If the request is for a segment prefetch, we need to update the // segment prefetch request path to include the interpolated // params. let segmentPrefetchRSCRequest = (0, _requestmeta.getRequestMeta)(req, 'segmentPrefetchRSCRequest'); if (segmentPrefetchRSCRequest && (0, _utils1.isDynamicRoute)(segmentPrefetchRSCRequest, false)) { segmentPrefetchRSCRequest = utils.interpolateDynamicPath(segmentPrefetchRSCRequest, params); req.headers[_approuterheaders.NEXT_ROUTER_SEGMENT_PREFETCH_HEADER.toLowerCase()] = segmentPrefetchRSCRequest; (0, _requestmeta.addRequestMeta)(req, 'segmentPrefetchRSCRequest', segmentPrefetchRSCRequest); } } } if (pageIsDynamic || didRewrite) { var _utils_defaultRouteRegex; utils.normalizeVercelUrl(req, [ ...rewriteParamKeys, ...Object.keys(((_utils_defaultRouteRegex = utils.defaultRouteRegex) == null ? void 0 : _utils_defaultRouteRegex.groups) || {}) ]); } parsedUrl.pathname = matchedPath; url.pathname = parsedUrl.pathname; finished = await this.normalizeAndAttachMetadata(req, res, parsedUrl); if (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; } } (0, _requestmeta.addRequestMeta)(req, 'isLocaleDomain', Boolean(domainLocale)); if (pathnameInfo.locale) { req.url = (0, _url.format)(url); (0, _requestmeta.addRequestMeta)(req, 'didStripLocale', 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 || !(0, _requestmeta.getRequestMeta)(req, 'locale')) { // If the locale is in the pathname, add it to the query string. if (pathnameInfo.locale) { (0, _requestmeta.addRequestMeta)(req, 'locale', pathnameInfo.locale); } else if (defaultLocale) { (0, _requestmeta.addRequestMeta)(req, 'locale', defaultLocale); (0, _requestmeta.addRequestMeta)(req, 'localeInferredFromDefault', true); } } // 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, 'incrementalCache')) { let protocol = 'https:'; try { const parsedFullUrl = new URL((0, _requestmeta.getRequestMeta)(req, 'initURL') || '/', 'http://n'); protocol = parsedFullUrl.protocol; } catch {} const incrementalCache = await this.getIncrementalCache({ requestHeaders: Object.assign({}, req.headers), requestProtocol: protocol.substring(0, protocol.length - 1) }); incrementalCache.resetRequestCache(); (0, _requestmeta.addRequestMeta)(req, 'incrementalCache', incrementalCache); globalThis.__incrementalCache = incrementalCache; } // If the header is present, receive the expired tags from all the // cache handlers. const handlers = (0, _handlers.getCacheHandlers)(); if (handlers) { const header = req.headers[_constants2.NEXT_CACHE_REVALIDATED_TAGS_HEADER];