UNPKG

next

Version:

The React Framework

956 lines 50.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); 0 && (module.exports = { GlobalError: null, __next_app__: null, handler: null, pages: null, routeModule: null, tree: null }); function _export(target, all) { for(var name in all)Object.defineProperty(target, name, { enumerable: true, get: all[name] }); } _export(exports, { GlobalError: function() { return _VAR_MODULE_GLOBAL_ERROR.default; }, __next_app__: function() { return __next_app__; }, handler: function() { return handler; }, pages: function() { return pages; }, routeModule: function() { return routeModule; }, tree: function() { return tree; } }); 0 && __export(require("../../server/app-render/entry-base")); const _modulecompiled = require("../../server/route-modules/app-page/module.compiled"); const _routekind = require("../../server/route-kind"); const _utils = require("../../server/instrumentation/utils"); const _tracer = require("../../server/lib/trace/tracer"); const _requestmeta = require("../../server/request-meta"); const _constants = require("../../server/lib/trace/constants"); const _interopdefault = require("../../server/app-render/interop-default"); const _node = require("../../server/base-http/node"); const _ppr = require("../../server/lib/experimental/ppr"); const _fallbackparams = require("../../server/request/fallback-params"); const _encryptionutils = require("../../server/app-render/encryption-utils"); const _streamingmetadata = require("../../server/lib/streaming-metadata"); const _actionutils = require("../../server/app-render/action-utils"); const _apppaths = require("../../shared/lib/router/utils/app-paths"); const _serveractionrequestmeta = require("../../server/lib/server-action-request-meta"); const _approuterheaders = require("../../client/components/app-router-headers"); const _isbot = require("../../shared/lib/router/utils/is-bot"); const _responsecache = require("../../server/response-cache"); const _fallback = require("../../lib/fallback"); const _renderresult = /*#__PURE__*/ _interop_require_default(require("../../server/render-result")); const _constants1 = require("../../lib/constants"); const _encodedtags = require("../../server/stream-utils/encoded-tags"); const _sendpayload = require("../../server/send-payload"); const _nofallbackerrorexternal = require("../../shared/lib/no-fallback-error.external"); const _VAR_MODULE_GLOBAL_ERROR = /*#__PURE__*/ _interop_require_default(require("VAR_MODULE_GLOBAL_ERROR")); const _entrybase = /*#__PURE__*/ _interop_require_wildcard(_export_star(require("../../server/app-render/entry-base"), exports)); const _redirectstatuscode = require("../../client/components/redirect-status-code"); 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 = { __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; } const __next_app__ = { require: __next_app_require__, loadChunk: __next_app_load_chunk__ }; const routeModule = new _modulecompiled.AppPageRouteModule({ definition: { kind: _routekind.RouteKind.APP_PAGE, page: 'VAR_DEFINITION_PAGE', pathname: 'VAR_DEFINITION_PATHNAME', // The following aren't used in production. bundlePath: '', filename: '', appPaths: [] }, userland: { loaderTree: tree }, distDir: process.env.__NEXT_RELATIVE_DIST_DIR || '', projectDir: process.env.__NEXT_RELATIVE_PROJECT_DIR || '' }); async function handler(req, res, ctx) { var _this; let srcPage = 'VAR_DEFINITION_PAGE'; // turbopack doesn't normalize `/index` in the page name // so we need to to process dynamic routes properly // TODO: fix turbopack providing differing value from webpack if (process.env.TURBOPACK) { srcPage = srcPage.replace(/\/index$/, '') || '/'; } else if (srcPage === '/index') { // we always normalize /index specifically srcPage = '/'; } const multiZoneDraftMode = process.env.__NEXT_MULTI_ZONE_DRAFT_MODE; const initialPostponed = (0, _requestmeta.getRequestMeta)(req, 'postponed'); // TODO: replace with more specific flags const minimalMode = (0, _requestmeta.getRequestMeta)(req, 'minimalMode'); const prepareResult = await routeModule.prepare(req, res, { srcPage, multiZoneDraftMode }); if (!prepareResult) { res.statusCode = 400; res.end('Bad Request'); ctx.waitUntil == null ? void 0 : ctx.waitUntil.call(ctx, Promise.resolve()); return null; } const { buildId, query, params, parsedUrl, pageIsDynamic, buildManifest, nextFontManifest, reactLoadableManifest, serverActionsManifest, clientReferenceManifest, subresourceIntegrityManifest, prerenderManifest, isDraftMode, resolvedPathname, revalidateOnlyGenerated, routerServerContext, nextConfig } = prepareResult; const pathname = parsedUrl.pathname || '/'; const normalizedSrcPage = (0, _apppaths.normalizeAppPath)(srcPage); let { isOnDemandRevalidate } = prepareResult; const prerenderInfo = prerenderManifest.dynamicRoutes[normalizedSrcPage]; const isPrerendered = prerenderManifest.routes[resolvedPathname]; let isSSG = Boolean(prerenderInfo || isPrerendered || prerenderManifest.routes[normalizedSrcPage]); const userAgent = req.headers['user-agent'] || ''; const botType = (0, _isbot.getBotType)(userAgent); const isHtmlBot = (0, _streamingmetadata.isHtmlBotRequest)(req); /** * If true, this indicates that the request being made is for an app * prefetch request. */ const isPrefetchRSCRequest = (0, _requestmeta.getRequestMeta)(req, 'isPrefetchRSCRequest') ?? Boolean(req.headers[_approuterheaders.NEXT_ROUTER_PREFETCH_HEADER]); // NOTE: Don't delete headers[RSC] yet, it still needs to be used in renderToHTML later const isRSCRequest = (0, _requestmeta.getRequestMeta)(req, 'isRSCRequest') ?? Boolean(req.headers[_approuterheaders.RSC_HEADER]); const isPossibleServerAction = (0, _serveractionrequestmeta.getIsPossibleServerAction)(req); /** * If the route being rendered is an app page, and the ppr feature has been * enabled, then the given route _could_ support PPR. */ const couldSupportPPR = (0, _ppr.checkIsAppPPREnabled)(nextConfig.experimental.ppr); // When enabled, this will allow the use of the `?__nextppronly` query to // enable debugging of the static shell. const hasDebugStaticShellQuery = process.env.__NEXT_EXPERIMENTAL_STATIC_SHELL_DEBUGGING === '1' && typeof query.__nextppronly !== 'undefined' && couldSupportPPR; // When enabled, this will allow the use of the `?__nextppronly` query // to enable debugging of the fallback shell. const hasDebugFallbackShellQuery = hasDebugStaticShellQuery && query.__nextppronly === 'fallback'; // This page supports PPR if it is marked as being `PARTIALLY_STATIC` in the // prerender manifest and this is an app page. const isRoutePPREnabled = couldSupportPPR && (((_this = prerenderManifest.routes[normalizedSrcPage] ?? prerenderManifest.dynamicRoutes[normalizedSrcPage]) == null ? void 0 : _this.renderingMode) === 'PARTIALLY_STATIC' || // Ideally we'd want to check the appConfig to see if this page has PPR // enabled or not, but that would require plumbing the appConfig through // to the server during development. We assume that the page supports it // but only during development. hasDebugStaticShellQuery && (routeModule.isDev === true || (routerServerContext == null ? void 0 : routerServerContext.experimentalTestProxy) === true)); const isDebugStaticShell = hasDebugStaticShellQuery && isRoutePPREnabled; // We should enable debugging dynamic accesses when the static shell // debugging has been enabled and we're also in development mode. const isDebugDynamicAccesses = isDebugStaticShell && routeModule.isDev === true; const isDebugFallbackShell = hasDebugFallbackShellQuery && isRoutePPREnabled; // If we're in minimal mode, then try to get the postponed information from // the request metadata. If available, use it for resuming the postponed // render. const minimalPostponed = isRoutePPREnabled ? initialPostponed : undefined; // If PPR is enabled, and this is a RSC request (but not a prefetch), then // we can use this fact to only generate the flight data for the request // because we can't cache the HTML (as it's also dynamic). const isDynamicRSCRequest = isRoutePPREnabled && isRSCRequest && !isPrefetchRSCRequest; // Need to read this before it's stripped by stripFlightHeaders. We don't // need to transfer it to the request meta because it's only read // within this function; the static segment data should have already been // generated, so we will always either return a static response or a 404. const segmentPrefetchHeader = (0, _requestmeta.getRequestMeta)(req, 'segmentPrefetchRSCRequest'); // TODO: investigate existing bug with shouldServeStreamingMetadata always // being true for a revalidate due to modifying the base-server this.renderOpts // when fixing this to correct logic it causes hydration issue since we set // serveStreamingMetadata to true during export let serveStreamingMetadata = !userAgent ? true : (0, _streamingmetadata.shouldServeStreamingMetadata)(userAgent, nextConfig.htmlLimitedBots); if (isHtmlBot && isRoutePPREnabled) { isSSG = false; serveStreamingMetadata = false; } // In development, we always want to generate dynamic HTML. let supportsDynamicResponse = // If we're in development, we always support dynamic HTML, unless it's // a data request, in which case we only produce static HTML. routeModule.isDev === true || // If this is not SSG or does not have static paths, then it supports // dynamic HTML. !isSSG || // If this request has provided postponed data, it supports dynamic // HTML. typeof initialPostponed === 'string' || // If this is a dynamic RSC request, then this render supports dynamic // HTML (it's dynamic). isDynamicRSCRequest; // When html bots request PPR page, perform the full dynamic rendering. const shouldWaitOnAllReady = isHtmlBot && isRoutePPREnabled; let ssgCacheKey = null; if (!isDraftMode && isSSG && !supportsDynamicResponse && !isPossibleServerAction && !minimalPostponed && !isDynamicRSCRequest) { ssgCacheKey = resolvedPathname; } // the staticPathKey differs from ssgCacheKey since // ssgCacheKey is null in dev since we're always in "dynamic" // mode in dev to bypass the cache, but we still need to honor // dynamicParams = false in dev mode let staticPathKey = ssgCacheKey; if (!staticPathKey && routeModule.isDev) { staticPathKey = resolvedPathname; } const ComponentMod = { ..._entrybase, tree, pages, GlobalError: _VAR_MODULE_GLOBAL_ERROR.default, handler, routeModule, __next_app__ }; // Before rendering (which initializes component tree modules), we have to // set the reference manifests to our global store so Server Action's // encryption util can access to them at the top level of the page module. if (serverActionsManifest && clientReferenceManifest) { (0, _encryptionutils.setReferenceManifestsSingleton)({ page: srcPage, clientReferenceManifest, serverActionsManifest, serverModuleMap: (0, _actionutils.createServerModuleMap)({ serverActionsManifest }) }); } const method = req.method || 'GET'; const tracer = (0, _tracer.getTracer)(); const activeSpan = tracer.getActiveScopeSpan(); try { const invokeRouteModule = async (span, context)=>{ const nextReq = new _node.NodeNextRequest(req); const nextRes = new _node.NodeNextResponse(res); // TODO: adapt for putting the RDC inside the postponed data // If we're in dev, and this isn't a prefetch or a server action, // we should seed the resume data cache. if (process.env.NODE_ENV === 'development') { if (nextConfig.experimental.dynamicIO && !isPrefetchRSCRequest && !context.renderOpts.isPossibleServerAction) { const warmup = await routeModule.warmup(nextReq, nextRes, context); // If the warmup is successful, we should use the resume data // cache from the warmup. if (warmup.metadata.renderResumeDataCache) { context.renderOpts.renderResumeDataCache = warmup.metadata.renderResumeDataCache; } } } return routeModule.render(nextReq, nextRes, context).finally(()=>{ if (!span) return; span.setAttributes({ 'http.status_code': res.statusCode, 'next.rsc': false }); const rootSpanAttributes = tracer.getRootSpanAttributes(); // We were unable to get attributes, probably OTEL is not enabled if (!rootSpanAttributes) { return; } if (rootSpanAttributes.get('next.span_type') !== _constants.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 = `${method} ${route}`; span.setAttributes({ 'next.route': route, 'http.route': route, 'next.span_name': name }); span.updateName(name); } else { span.updateName(`${method} ${req.url}`); } }); }; const doRender = async ({ span, postponed, fallbackRouteParams })=>{ const context = { query, params, page: normalizedSrcPage, sharedContext: { buildId }, serverComponentsHmrCache: (0, _requestmeta.getRequestMeta)(req, 'serverComponentsHmrCache'), fallbackRouteParams, renderOpts: { App: ()=>null, Document: ()=>null, pageConfig: {}, ComponentMod, Component: (0, _interopdefault.interopDefault)(ComponentMod), params, routeModule, page: srcPage, postponed, shouldWaitOnAllReady, serveStreamingMetadata, supportsDynamicResponse: typeof postponed === 'string' || supportsDynamicResponse, buildManifest, nextFontManifest, reactLoadableManifest, subresourceIntegrityManifest, serverActionsManifest, clientReferenceManifest, setIsrStatus: routerServerContext == null ? void 0 : routerServerContext.setIsrStatus, dir: routeModule.projectDir, isDraftMode, isRevalidate: isSSG && !postponed && !isDynamicRSCRequest, botType, isOnDemandRevalidate, isPossibleServerAction, assetPrefix: nextConfig.assetPrefix, nextConfigOutput: nextConfig.output, crossOrigin: nextConfig.crossOrigin, trailingSlash: nextConfig.trailingSlash, previewProps: prerenderManifest.preview, deploymentId: nextConfig.deploymentId, enableTainting: nextConfig.experimental.taint, htmlLimitedBots: nextConfig.htmlLimitedBots, devtoolSegmentExplorer: nextConfig.experimental.devtoolSegmentExplorer, reactMaxHeadersLength: nextConfig.reactMaxHeadersLength, multiZoneDraftMode, incrementalCache: (0, _requestmeta.getRequestMeta)(req, 'incrementalCache'), cacheLifeProfiles: nextConfig.experimental.cacheLife, basePath: nextConfig.basePath, serverActions: nextConfig.experimental.serverActions, ...isDebugStaticShell || isDebugDynamicAccesses ? { nextExport: true, supportsDynamicResponse: false, isStaticGeneration: true, isRevalidate: true, isDebugDynamicAccesses: isDebugDynamicAccesses } : {}, experimental: { isRoutePPREnabled, expireTime: nextConfig.expireTime, staleTimes: nextConfig.experimental.staleTimes, dynamicIO: Boolean(nextConfig.experimental.dynamicIO), clientSegmentCache: Boolean(nextConfig.experimental.clientSegmentCache), dynamicOnHover: Boolean(nextConfig.experimental.dynamicOnHover), inlineCss: Boolean(nextConfig.experimental.inlineCss), authInterrupts: Boolean(nextConfig.experimental.authInterrupts), clientTraceMetadata: nextConfig.experimental.clientTraceMetadata || [] }, waitUntil: ctx.waitUntil, onClose: (cb)=>{ res.on('close', cb); }, onAfterTaskError: ()=>{}, onInstrumentationRequestError: (error, _request, errorContext)=>routeModule.onRequestError(req, error, errorContext, routerServerContext), err: (0, _requestmeta.getRequestMeta)(req, 'invokeError'), dev: routeModule.isDev } }; const result = await invokeRouteModule(span, context); const { metadata } = result; const { cacheControl, headers = {}, // Add any fetch tags that were on the page to the response headers. fetchTags: cacheTags } = metadata; if (cacheTags) { headers[_constants1.NEXT_CACHE_TAGS_HEADER] = cacheTags; } // Pull any fetch metrics from the render onto the request. ; req.fetchMetrics = metadata.fetchMetrics; // we don't throw static to dynamic errors in dev as isSSG // is a best guess in dev since we don't have the prerender pass // to know whether the path is actually static or not if (isSSG && (cacheControl == null ? void 0 : cacheControl.revalidate) === 0 && !routeModule.isDev && !isRoutePPREnabled) { const staticBailoutInfo = metadata.staticBailoutInfo; const err = Object.defineProperty(new Error(`Page changed from static to dynamic at runtime ${resolvedPathname}${(staticBailoutInfo == null ? void 0 : staticBailoutInfo.description) ? `, reason: ${staticBailoutInfo.description}` : ``}` + `\nsee more here https://nextjs.org/docs/messages/app-static-to-dynamic-error`), "__NEXT_ERROR_CODE", { value: "E132", enumerable: false, configurable: true }); if (staticBailoutInfo == null ? void 0 : staticBailoutInfo.stack) { const stack = staticBailoutInfo.stack; err.stack = err.message + stack.substring(stack.indexOf('\n')); } throw err; } return { value: { kind: _responsecache.CachedRouteKind.APP_PAGE, html: result, headers, rscData: metadata.flightData, postponed: metadata.postponed, status: metadata.statusCode, segmentData: metadata.segmentData }, cacheControl }; }; const responseGenerator = async ({ hasResolved, previousCacheEntry, isRevalidating, span })=>{ const isProduction = routeModule.isDev === false; const didRespond = hasResolved || res.writableEnded; // skip on-demand revalidate if cache is not present and // revalidate-if-generated is set if (isOnDemandRevalidate && revalidateOnlyGenerated && !previousCacheEntry && !minimalMode) { if (routerServerContext == null ? void 0 : routerServerContext.render404) { await routerServerContext.render404(req, res); } else { res.statusCode = 404; res.end('This page could not be found'); } return null; } let fallbackMode; if (prerenderInfo) { fallbackMode = (0, _fallback.parseFallbackField)(prerenderInfo.fallback); } // When serving a bot request, we want to serve a blocking render and not // the prerendered page. This ensures that the correct content is served // to the bot in the head. if (fallbackMode === _fallback.FallbackMode.PRERENDER && (0, _isbot.isBot)(userAgent)) { fallbackMode = _fallback.FallbackMode.BLOCKING_STATIC_RENDER; } if ((previousCacheEntry == null ? void 0 : previousCacheEntry.isStale) === -1) { isOnDemandRevalidate = true; } // TODO: adapt for PPR // only allow on-demand revalidate for fallback: true/blocking // or for prerendered fallback: false paths if (isOnDemandRevalidate && (fallbackMode !== _fallback.FallbackMode.NOT_FOUND || previousCacheEntry)) { fallbackMode = _fallback.FallbackMode.BLOCKING_STATIC_RENDER; } if (!minimalMode && fallbackMode !== _fallback.FallbackMode.BLOCKING_STATIC_RENDER && staticPathKey && !didRespond && !isDraftMode && pageIsDynamic && (isProduction || !isPrerendered)) { // if the page has dynamicParams: false and this pathname wasn't // prerendered trigger the no fallback handling if (// In development, fall through to render to handle missing // getStaticPaths. (isProduction || prerenderInfo) && // When fallback isn't present, abort this render so we 404 fallbackMode === _fallback.FallbackMode.NOT_FOUND) { throw new _nofallbackerrorexternal.NoFallbackError(); } let fallbackResponse; if (isRoutePPREnabled && !isRSCRequest) { // We use the response cache here to handle the revalidation and // management of the fallback shell. fallbackResponse = await routeModule.handleResponse({ cacheKey: isProduction ? normalizedSrcPage : null, req, nextConfig, routeKind: _routekind.RouteKind.APP_PAGE, isFallback: true, prerenderManifest, isRoutePPREnabled, responseGenerator: async ()=>doRender({ span, // We pass `undefined` as rendering a fallback isn't resumed // here. postponed: undefined, fallbackRouteParams: // If we're in production or we're debugging the fallback // shell then we should postpone when dynamic params are // accessed. isProduction || isDebugFallbackShell ? (0, _fallbackparams.getFallbackRouteParams)(normalizedSrcPage) : null }), waitUntil: ctx.waitUntil }); // If the fallback response was set to null, then we should return null. if (fallbackResponse === null) return null; // Otherwise, if we did get a fallback response, we should return it. if (fallbackResponse) { // Remove the cache control from the response to prevent it from being // used in the surrounding cache. delete fallbackResponse.cacheControl; return fallbackResponse; } } } // Only requests that aren't revalidating can be resumed. If we have the // minimal postponed data, then we should resume the render with it. const postponed = !isOnDemandRevalidate && !isRevalidating && minimalPostponed ? minimalPostponed : undefined; // When we're in minimal mode, if we're trying to debug the static shell, // we should just return nothing instead of resuming the dynamic render. if ((isDebugStaticShell || isDebugDynamicAccesses) && typeof postponed !== 'undefined') { return { cacheControl: { revalidate: 1, expire: undefined }, value: { kind: _responsecache.CachedRouteKind.PAGES, html: _renderresult.default.fromStatic(''), pageData: {}, headers: undefined, status: undefined } }; } // If this is a dynamic route with PPR enabled and the default route // matches were set, then we should pass the fallback route params to // the renderer as this is a fallback revalidation request. const fallbackRouteParams = pageIsDynamic && isRoutePPREnabled && ((0, _requestmeta.getRequestMeta)(req, 'renderFallbackShell') || isDebugFallbackShell) ? (0, _fallbackparams.getFallbackRouteParams)(pathname) : null; // Perform the render. return doRender({ span, postponed, fallbackRouteParams }); }; const handleResponse = async (span)=>{ var _cacheEntry_value, _cachedData_headers; const cacheEntry = await routeModule.handleResponse({ cacheKey: ssgCacheKey, responseGenerator: (c)=>responseGenerator({ span, ...c }), routeKind: _routekind.RouteKind.APP_PAGE, isOnDemandRevalidate, isRoutePPREnabled, req, nextConfig, prerenderManifest, waitUntil: ctx.waitUntil }); if (isDraftMode) { res.setHeader('Cache-Control', 'private, no-cache, no-store, max-age=0, must-revalidate'); } // In dev, we should not cache pages for any reason. if (routeModule.isDev) { res.setHeader('Cache-Control', 'no-store, must-revalidate'); } if (!cacheEntry) { if (ssgCacheKey) { // A cache entry might not be generated if a response is written // in `getInitialProps` or `getServerSideProps`, but those shouldn't // have a cache key. If we do have a cache key but we don't end up // with a cache entry, then either Next.js or the application has a // bug that needs fixing. throw Object.defineProperty(new Error('invariant: cache entry required but not generated'), "__NEXT_ERROR_CODE", { value: "E62", enumerable: false, configurable: true }); } return null; } if (((_cacheEntry_value = cacheEntry.value) == null ? void 0 : _cacheEntry_value.kind) !== _responsecache.CachedRouteKind.APP_PAGE) { var _cacheEntry_value1; throw Object.defineProperty(new Error(`Invariant app-page handler received invalid cache entry ${(_cacheEntry_value1 = cacheEntry.value) == null ? void 0 : _cacheEntry_value1.kind}`), "__NEXT_ERROR_CODE", { value: "E707", enumerable: false, configurable: true }); } const didPostpone = typeof cacheEntry.value.postponed === 'string'; if (isSSG && // We don't want to send a cache header for requests that contain dynamic // data. If this is a Dynamic RSC request or wasn't a Prefetch RSC // request, then we should set the cache header. !isDynamicRSCRequest && (!didPostpone || isPrefetchRSCRequest)) { if (!minimalMode) { // set x-nextjs-cache header to match the header // we set for the image-optimizer res.setHeader('x-nextjs-cache', isOnDemandRevalidate ? 'REVALIDATED' : cacheEntry.isMiss ? 'MISS' : cacheEntry.isStale ? 'STALE' : 'HIT'); } // Set a header used by the client router to signal the response is static // and should respect the `static` cache staleTime value. res.setHeader(_approuterheaders.NEXT_IS_PRERENDER_HEADER, '1'); } const { value: cachedData } = cacheEntry; // Coerce the cache control parameter from the render. let cacheControl; // If this is a resume request in minimal mode it is streamed with dynamic // content and should not be cached. if (minimalPostponed) { cacheControl = { revalidate: 0, expire: undefined }; } else if (minimalMode && isRSCRequest && !isPrefetchRSCRequest && isRoutePPREnabled) { cacheControl = { revalidate: 0, expire: undefined }; } else if (!routeModule.isDev) { // If this is a preview mode request, we shouldn't cache it if (isDraftMode) { cacheControl = { revalidate: 0, expire: undefined }; } else if (!isSSG) { if (!res.getHeader('Cache-Control')) { cacheControl = { revalidate: 0, expire: undefined }; } } else if (cacheEntry.cacheControl) { // If the cache entry has a cache control with a revalidate value that's // a number, use it. if (typeof cacheEntry.cacheControl.revalidate === 'number') { var _cacheEntry_cacheControl; if (cacheEntry.cacheControl.revalidate < 1) { throw Object.defineProperty(new Error(`Invalid revalidate configuration provided: ${cacheEntry.cacheControl.revalidate} < 1`), "__NEXT_ERROR_CODE", { value: "E22", enumerable: false, configurable: true }); } cacheControl = { revalidate: cacheEntry.cacheControl.revalidate, expire: ((_cacheEntry_cacheControl = cacheEntry.cacheControl) == null ? void 0 : _cacheEntry_cacheControl.expire) ?? nextConfig.expireTime }; } else { cacheControl = { revalidate: _constants1.CACHE_ONE_YEAR, expire: undefined }; } } } cacheEntry.cacheControl = cacheControl; if (typeof segmentPrefetchHeader === 'string' && (cachedData == null ? void 0 : cachedData.kind) === _responsecache.CachedRouteKind.APP_PAGE && cachedData.segmentData) { var _cachedData_headers1; // This is a prefetch request issued by the client Segment Cache. These // should never reach the application layer (lambda). We should either // respond from the cache (HIT) or respond with 204 No Content (MISS). // Set a header to indicate that PPR is enabled for this route. This // lets the client distinguish between a regular cache miss and a cache // miss due to PPR being disabled. In other contexts this header is used // to indicate that the response contains dynamic data, but here we're // only using it to indicate that the feature is enabled — the segment // response itself contains whether the data is dynamic. res.setHeader(_approuterheaders.NEXT_DID_POSTPONE_HEADER, '2'); // Add the cache tags header to the response if it exists and we're in // minimal mode while rendering a static page. const tags = (_cachedData_headers1 = cachedData.headers) == null ? void 0 : _cachedData_headers1[_constants1.NEXT_CACHE_TAGS_HEADER]; if (minimalMode && isSSG && tags && typeof tags === 'string') { res.setHeader(_constants1.NEXT_CACHE_TAGS_HEADER, tags); } const matchedSegment = cachedData.segmentData.get(segmentPrefetchHeader); if (matchedSegment !== undefined) { // Cache hit return (0, _sendpayload.sendRenderResult)({ req, res, type: 'rsc', generateEtags: nextConfig.generateEtags, poweredByHeader: nextConfig.poweredByHeader, result: _renderresult.default.fromStatic(matchedSegment), cacheControl: cacheEntry.cacheControl }); } // Cache miss. Either a cache entry for this route has not been generated // (which technically should not be possible when PPR is enabled, because // at a minimum there should always be a fallback entry) or there's no // match for the requested segment. Respond with a 204 No Content. We // don't bother to respond with 404, because these requests are only // issued as part of a prefetch. res.statusCode = 204; return (0, _sendpayload.sendRenderResult)({ req, res, type: 'rsc', generateEtags: nextConfig.generateEtags, poweredByHeader: nextConfig.poweredByHeader, result: _renderresult.default.fromStatic(''), cacheControl: cacheEntry.cacheControl }); } // If there's a callback for `onCacheEntry`, call it with the cache entry // and the revalidate options. const onCacheEntry = (0, _requestmeta.getRequestMeta)(req, 'onCacheEntry'); if (onCacheEntry) { const finished = await onCacheEntry({ ...cacheEntry, // TODO: remove this when upstream doesn't // always expect this value to be "PAGE" value: { ...cacheEntry.value, kind: 'PAGE' } }, { url: (0, _requestmeta.getRequestMeta)(req, 'initURL') }); if (finished) { // TODO: maybe we have to end the request? return null; } } // If the request has a postponed state and it's a resume request we // should error. if (didPostpone && minimalPostponed) { throw Object.defineProperty(new Error('Invariant: postponed state should not be present on a resume request'), "__NEXT_ERROR_CODE", { value: "E396", enumerable: false, configurable: true }); } if (cachedData.headers) { const headers = { ...cachedData.headers }; if (!minimalMode || !isSSG) { delete headers[_constants1.NEXT_CACHE_TAGS_HEADER]; } for (let [key, value] of Object.entries(headers)){ if (typeof value === 'undefined') continue; if (Array.isArray(value)) { for (const v of value){ res.appendHeader(key, v); } } else if (typeof value === 'number') { value = value.toString(); res.appendHeader(key, value); } else { res.appendHeader(key, value); } } } // Add the cache tags header to the response if it exists and we're in // minimal mode while rendering a static page. const tags = (_cachedData_headers = cachedData.headers) == null ? void 0 : _cachedData_headers[_constants1.NEXT_CACHE_TAGS_HEADER]; if (minimalMode && isSSG && tags && typeof tags === 'string') { res.setHeader(_constants1.NEXT_CACHE_TAGS_HEADER, tags); } // If the request is a data request, then we shouldn't set the status code // from the response because it should always be 200. This should be gated // behind the experimental PPR flag. if (cachedData.status && (!isRSCRequest || !isRoutePPREnabled)) { res.statusCode = cachedData.status; } // Redirect information is encoded in RSC payload, so we don't need to use redirect status codes if (!minimalMode && cachedData.status && _redirectstatuscode.RedirectStatusCode[cachedData.status] && isRSCRequest) { res.statusCode = 200; } // Mark that the request did postpone. if (didPostpone) { res.setHeader(_approuterheaders.NEXT_DID_POSTPONE_HEADER, '1'); } // we don't go through this block when preview mode is true // as preview mode is a dynamic request (bypasses cache) and doesn't // generate both HTML and payloads in the same request so continue to just // return the generated payload if (isRSCRequest && !isDraftMode) { // If this is a dynamic RSC request, then stream the response. if (typeof cachedData.rscData === 'undefined') { if (cachedData.postponed) { throw Object.defineProperty(new Error('Invariant: Expected postponed to be undefined'), "__NEXT_ERROR_CODE", { value: "E372", enumerable: false, configurable: true }); } return (0, _sendpayload.sendRenderResult)({ req, res, type: 'rsc', generateEtags: nextConfig.generateEtags, poweredByHeader: nextConfig.poweredByHeader, result: cachedData.html, // Dynamic RSC responses cannot be cached, even if they're // configured with `force-static` because we have no way of // distinguishing between `force-static` and pages that have no // postponed state. // TODO: distinguish `force-static` from pages with no postponed state (static) cacheControl: isDynamicRSCRequest ? { revalidate: 0, expire: undefined } : cacheEntry.cacheControl }); } // As this isn't a prefetch request, we should serve the static flight // data. return (0, _sendpayload.sendRenderResult)({ req, res, type: 'rsc', generateEtags: nextConfig.generateEtags, poweredByHeader: nextConfig.poweredByHeader, result: _renderresult.default.fromStatic(cachedData.rscData), cacheControl: cacheEntry.cacheControl }); } // This is a request for HTML data. let body = cachedData.html; // If there's no postponed state, we should just serve the HTML. This // should also be the case for a resume request because it's completed // as a server render (rather than a static render). if (!didPostpone || minimalMode) { return (0, _sendpayload.sendRenderResult)({ req, res, type: 'html', generateEtags: nextConfig.generateEtags, poweredByHeader: nextConfig.poweredByHeader, result: body, cacheControl: cacheEntry.cacheControl }); } // If we're debugging the static shell or the dynamic API accesses, we // should just serve the HTML without resuming the render. The returned // HTML will be the static shell so all the Dynamic API's will be used // during static generation. if (isDebugStaticShell || isDebugDynamicAccesses) { // Since we're not resuming the render, we need to at least add the // closing body and html tags to create valid HTML. body.chain(new ReadableStream({ start (controller) { controller.enqueue(_encodedtags.ENCODED_TAGS.CLOSED.BODY_AND_HTML); controller.close(); } })); return (0, _sendpayload.sendRenderResult)({ req, res, type: 'html', generateEtags: nextConfig.generateEtags, poweredByHeader: nextConfig.poweredByHeader, result: body, cacheControl: { revalidate: 0, expire: undefined } }); } // This request has postponed, so let's create a new transformer that the // dynamic data can pipe to that will attach the dynamic data to the end // of the response. const transformer = new TransformStream(); body.chain(transformer.readable); // Perform the render again, but this time, provide the postponed state. // We don't await because we want the result to start streaming now, and // we've already chained the transformer's readable to the render result. doRender({ span, postponed: cachedData.postponed, // This is a resume render, not a fallback render, so we don't need to // set this. fallbackRouteParams: null }).then(async (result)=>{ var _result_value; if (!result) { throw Object.defineProperty(new Error('Invariant: expected a result to be returned'), "__NEXT_ERROR_CODE", { value: "E463", enumerable: false, configurable: true }); } if (((_result_value = result.value) == null ? void 0 : _result_value.kind) !== _responsecache.CachedRouteKind.APP_PAGE) { var _result_value1; throw Object.defineProperty(new Error(`Invariant: expected a page response, got ${(_result_value1 = result.value) == null ? void 0 : _result_value1.kind}`), "__NEXT_ERROR_CODE", { value: "E305", enumerable: false, configurable: true }); } // Pipe the resume result to the transformer. await result.value.html.pipeTo(transformer.writable); }).catch((err)=>{ // An error occurred during piping or preparing the render, abort // the transformers writer so we can terminate the stream. transformer.writable.abort(err).catch((e)=>{ console.error("couldn't abort transformer", e); }); }); return (0, _sendpayload.sendRenderResult)({ req, res, type: 'html', generateEtags: nextConfig.generateEtags, poweredByHeader: nextConfig.poweredByHeader, result: body, // We don't want to cache the response if it has postponed data because // the response being sent to the client it's dynamic parts are streamed // to the client on the same request. cacheControl: { revalidate: 0, expire: undefined } }); }; // TODO: activeSpan code path is for when wrapped by // next-server can be removed when this is no longer used if (activeSpan) { await handleResponse(activeSpan); } else { return await tracer.withPropagatedContext(req.headers, ()=>tracer.trace(_constants.BaseServerSpan.handleRequest, { spanName: `${method} ${req.url}`, kind: _tracer.SpanKind.SERVER, attributes: { 'http.method': method, 'http.target': req.url } }, handleResponse)); } } catch (err) { // if we aren't wrapped by base-server handle here if (!activeSpan && !(err instanceof _nofallbackerrorexternal.NoFallbackError)) { await routeModule.onRequestError(req, err, { routerKind: 'App Router', routePath: srcPage, routeType: 'render', revalidateReason: (0, _utils.getRevalidateReason)({ isRevalidate: isSSG, isOnDemandRevalidate }) }, routerServerContext); } // rethrow so that we can handle serving error page throw e