next
Version:
The React Framework
990 lines • 148 kB
JavaScript
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { workAsyncStorage } from '../app-render/work-async-storage.external';
import React from 'react';
import RenderResult from '../render-result';
import { chainStreams, renderToInitialFizzStream, createDocumentClosingStream, continueFizzStream, continueDynamicPrerender, continueStaticPrerender, continueDynamicHTMLResume, streamToBuffer, streamToString } from '../stream-utils/node-web-streams-helper';
import { stripInternalQueries } from '../internal-utils';
import { NEXT_HMR_REFRESH_HEADER, NEXT_ROUTER_PREFETCH_HEADER, NEXT_ROUTER_STATE_TREE_HEADER, NEXT_ROUTER_STALE_TIME_HEADER, NEXT_URL, RSC_HEADER, NEXT_ROUTER_SEGMENT_PREFETCH_HEADER } from '../../client/components/app-router-headers';
import { createTrackedMetadataContext, createMetadataContext } from '../../lib/metadata/metadata-context';
import { createRequestStoreForRender } from '../async-storage/request-store';
import { createWorkStore } from '../async-storage/work-store';
import { getAccessFallbackErrorTypeByStatus, getAccessFallbackHTTPStatus, isHTTPAccessFallbackError } from '../../client/components/http-access-fallback/http-access-fallback';
import { getURLFromRedirectError, getRedirectStatusCodeFromError } from '../../client/components/redirect';
import { isRedirectError } from '../../client/components/redirect-error';
import { getImplicitTags } from '../lib/implicit-tags';
import { AppRenderSpan, NextNodeServerSpan } from '../lib/trace/constants';
import { getTracer } from '../lib/trace/tracer';
import { FlightRenderResult } from './flight-render-result';
import { createFlightReactServerErrorHandler, createHTMLReactServerErrorHandler, createHTMLErrorHandler, isUserLandError, getDigestForWellKnownError } from './create-error-handler';
import { getShortDynamicParamType, dynamicParamTypes } from './get-short-dynamic-param-type';
import { getSegmentParam } from './get-segment-param';
import { getScriptNonceFromHeader } from './get-script-nonce-from-header';
import { parseAndValidateFlightRouterState } from './parse-and-validate-flight-router-state';
import { createFlightRouterStateFromLoaderTree } from './create-flight-router-state-from-loader-tree';
import { handleAction } from './action-handler';
import { isBailoutToCSRError } from '../../shared/lib/lazy-dynamic/bailout-to-csr';
import { warn, error } from '../../build/output/log';
import { appendMutableCookies } from '../web/spec-extension/adapters/request-cookies';
import { createServerInsertedHTML } from './server-inserted-html';
import { getRequiredScripts } from './required-scripts';
import { addPathPrefix } from '../../shared/lib/router/utils/add-path-prefix';
import { makeGetServerInsertedHTML } from './make-get-server-inserted-html';
import { walkTreeWithFlightRouterState } from './walk-tree-with-flight-router-state';
import { createComponentTree, getRootParams } from './create-component-tree';
import { getAssetQueryString } from './get-asset-query-string';
import { setReferenceManifestsSingleton } from './encryption-utils';
import { DynamicState, parsePostponedState } from './postponed-state';
import { getDynamicDataPostponedState, getDynamicHTMLPostponedState, getPostponedFromState } from './postponed-state';
import { isDynamicServerError } from '../../client/components/hooks-server-context';
import { useFlightStream, createInlinedDataReadableStream } from './use-flight-response';
import { StaticGenBailoutError, isStaticGenBailoutError } from '../../client/components/static-generation-bailout';
import { getStackWithoutErrorMessage } from '../../lib/format-server-error';
import { accessedDynamicData, createPostponedAbortSignal, formatDynamicAPIAccesses, isPrerenderInterruptedError, createDynamicTrackingState, createDynamicValidationState, getFirstDynamicReason, trackAllowedDynamicAccess, throwIfDisallowedDynamic, consumeDynamicAccess } from './dynamic-rendering';
import { getClientComponentLoaderMetrics, wrapClientComponentLoader } from '../client-component-renderer-logger';
import { createServerModuleMap } from './action-utils';
import { isNodeNextRequest } from '../base-http/helpers';
import { parseParameter } from '../../shared/lib/router/utils/route-regex';
import { parseRelativeUrl } from '../../shared/lib/router/utils/parse-relative-url';
import AppRouter from '../../client/components/app-router';
import { getServerActionRequestMetadata } from '../lib/server-action-request-meta';
import { createInitialRouterState } from '../../client/components/router-reducer/create-initial-router-state';
import { createMutableActionQueue } from '../../shared/lib/router/action-queue';
import { getRevalidateReason } from '../instrumentation/utils';
import { PAGE_SEGMENT_KEY } from '../../shared/lib/segment';
import { DynamicServerError } from '../../client/components/hooks-server-context';
import { ServerPrerenderStreamResult } from './app-render-prerender-utils';
import { ReactServerResult, createReactServerPrerenderResult, createReactServerPrerenderResultFromRender, prerenderAndAbortInSequentialTasks, prerenderServerWithPhases, prerenderClientWithPhases } from './app-render-prerender-utils';
import { printDebugThrownValueForProspectiveRender } from './prospective-render-utils';
import { scheduleInSequentialTasks } from './app-render-render-utils';
import { waitAtLeastOneReactRenderTask } from '../../lib/scheduler';
import { workUnitAsyncStorage } from './work-unit-async-storage.external';
import { CacheSignal } from './cache-signal';
import { getTracedMetadata } from '../lib/trace/utils';
import { InvariantError } from '../../shared/lib/invariant-error';
import './clean-async-snapshot.external';
import { INFINITE_CACHE } from '../../lib/constants';
import { createComponentStylesAndScripts } from './create-component-styles-and-scripts';
import { parseLoaderTree } from './parse-loader-tree';
import { createPrerenderResumeDataCache, createRenderResumeDataCache } from '../resume-data-cache/resume-data-cache';
import isError from '../../lib/is-error';
import { isUseCacheTimeoutError } from '../use-cache/use-cache-errors';
import { createServerInsertedMetadata } from './metadata-insertion/create-server-inserted-metadata';
const flightDataPathHeadKey = 'h';
function parseRequestHeaders(headers, options) {
const isDevWarmupRequest = options.isDevWarmup === true;
// dev warmup requests are treated as prefetch RSC requests
const isPrefetchRequest = isDevWarmupRequest || headers[NEXT_ROUTER_PREFETCH_HEADER.toLowerCase()] !== undefined;
const isHmrRefresh = headers[NEXT_HMR_REFRESH_HEADER.toLowerCase()] !== undefined;
// dev warmup requests are treated as prefetch RSC requests
const isRSCRequest = isDevWarmupRequest || headers[RSC_HEADER.toLowerCase()] !== undefined;
const shouldProvideFlightRouterState = isRSCRequest && (!isPrefetchRequest || !options.isRoutePPREnabled);
const flightRouterState = shouldProvideFlightRouterState ? parseAndValidateFlightRouterState(headers[NEXT_ROUTER_STATE_TREE_HEADER.toLowerCase()]) : undefined;
// Checks if this is a prefetch of the Route Tree by the Segment Cache
const isRouteTreePrefetchRequest = headers[NEXT_ROUTER_SEGMENT_PREFETCH_HEADER.toLowerCase()] === '/_tree';
const csp = headers['content-security-policy'] || headers['content-security-policy-report-only'];
const nonce = typeof csp === 'string' ? getScriptNonceFromHeader(csp) : undefined;
return {
flightRouterState,
isPrefetchRequest,
isRouteTreePrefetchRequest,
isHmrRefresh,
isRSCRequest,
isDevWarmupRequest,
nonce
};
}
function createNotFoundLoaderTree(loaderTree) {
// Align the segment with parallel-route-default in next-app-loader
const components = loaderTree[2];
return [
'',
{
children: [
PAGE_SEGMENT_KEY,
{},
{
page: components['not-found']
}
]
},
components
];
}
function createDivergedMetadataComponents(Metadata, serveStreamingMetadata) {
function EmptyMetadata() {
return null;
}
const StreamingMetadata = serveStreamingMetadata ? Metadata : null;
const StaticMetadata = serveStreamingMetadata ? EmptyMetadata : Metadata;
return {
StaticMetadata,
StreamingMetadata
};
}
/**
* Returns a function that parses the dynamic segment and return the associated value.
*/ function makeGetDynamicParamFromSegment(params, pagePath, fallbackRouteParams) {
return function getDynamicParamFromSegment(// [slug] / [[slug]] / [...slug]
segment) {
const segmentParam = getSegmentParam(segment);
if (!segmentParam) {
return null;
}
const key = segmentParam.param;
let value = params[key];
if (fallbackRouteParams && fallbackRouteParams.has(segmentParam.param)) {
value = fallbackRouteParams.get(segmentParam.param);
} else if (Array.isArray(value)) {
value = value.map((i)=>encodeURIComponent(i));
} else if (typeof value === 'string') {
value = encodeURIComponent(value);
}
if (!value) {
const isCatchall = segmentParam.type === 'catchall';
const isOptionalCatchall = segmentParam.type === 'optional-catchall';
if (isCatchall || isOptionalCatchall) {
const dynamicParamType = dynamicParamTypes[segmentParam.type];
// handle the case where an optional catchall does not have a value,
// e.g. `/dashboard/[[...slug]]` when requesting `/dashboard`
if (isOptionalCatchall) {
return {
param: key,
value: null,
type: dynamicParamType,
treeSegment: [
key,
'',
dynamicParamType
]
};
}
// handle the case where a catchall or optional catchall does not have a value,
// e.g. `/foo/bar/hello` and `@slot/[...catchall]` or `@slot/[[...catchall]]` is matched
value = pagePath.split('/')// remove the first empty string
.slice(1)// replace any dynamic params with the actual values
.flatMap((pathSegment)=>{
const param = parseParameter(pathSegment);
// if the segment matches a param, return the param value
// otherwise, it's a static segment, so just return that
return params[param.key] ?? param.key;
});
return {
param: key,
value,
type: dynamicParamType,
// This value always has to be a string.
treeSegment: [
key,
value.join('/'),
dynamicParamType
]
};
}
}
const type = getShortDynamicParamType(segmentParam.type);
return {
param: key,
// The value that is passed to user code.
value: value,
// The value that is rendered in the router tree.
treeSegment: [
key,
Array.isArray(value) ? value.join('/') : value,
type
],
type: type
};
};
}
function NonIndex({ ctx }) {
const is404Page = ctx.pagePath === '/404';
const isInvalidStatusCode = typeof ctx.res.statusCode === 'number' && ctx.res.statusCode > 400;
// Only render noindex for page request, skip for server actions
if (!ctx.isAction && (is404Page || isInvalidStatusCode)) {
return /*#__PURE__*/ _jsx("meta", {
name: "robots",
content: "noindex"
});
}
return null;
}
/**
* This is used by server actions & client-side navigations to generate RSC data from a client-side request.
* This function is only called on "dynamic" requests (ie, there wasn't already a static response).
* It uses request headers (namely `Next-Router-State-Tree`) to determine where to start rendering.
*/ async function generateDynamicRSCPayload(ctx, options) {
// Flight data that is going to be passed to the browser.
// Currently a single item array but in the future multiple patches might be combined in a single request.
// We initialize `flightData` to an empty string because the client router knows how to tolerate
// it (treating it as an MPA navigation). The only time this function wouldn't generate flight data
// is for server actions, if the server action handler instructs this function to skip it. When the server
// action reducer sees a falsy value, it'll simply resolve the action with no data.
let flightData = '';
const { componentMod: { tree: loaderTree, createServerSearchParamsForMetadata, createServerParamsForMetadata, createMetadataComponents, MetadataBoundary, ViewportBoundary }, getDynamicParamFromSegment, appUsingSizeAdjustment, query, requestId, flightRouterState, workStore, url } = ctx;
const serveStreamingMetadata = !!ctx.renderOpts.serveStreamingMetadata;
if (!(options == null ? void 0 : options.skipFlight)) {
const preloadCallbacks = [];
const searchParams = createServerSearchParamsForMetadata(query, workStore);
const { ViewportTree, MetadataTree, getViewportReady, getMetadataReady, StreamingMetadataOutlet } = createMetadataComponents({
tree: loaderTree,
searchParams,
metadataContext: createTrackedMetadataContext(url.pathname, ctx.renderOpts, workStore),
getDynamicParamFromSegment,
appUsingSizeAdjustment,
createServerParamsForMetadata,
workStore,
MetadataBoundary,
ViewportBoundary,
serveStreamingMetadata
});
const { StreamingMetadata, StaticMetadata } = createDivergedMetadataComponents(()=>{
return(// Adding requestId as react key to make metadata remount for each render
/*#__PURE__*/ _jsx(MetadataTree, {}, requestId));
}, serveStreamingMetadata);
flightData = (await walkTreeWithFlightRouterState({
ctx,
loaderTreeToFilter: loaderTree,
parentParams: {},
flightRouterState,
// For flight, render metadata inside leaf page
rscHead: /*#__PURE__*/ _jsxs(React.Fragment, {
children: [
/*#__PURE__*/ _jsx(NonIndex, {
ctx: ctx
}),
/*#__PURE__*/ _jsx(ViewportTree, {}, requestId),
StreamingMetadata ? /*#__PURE__*/ _jsx(StreamingMetadata, {}) : null,
/*#__PURE__*/ _jsx(StaticMetadata, {})
]
}, flightDataPathHeadKey),
injectedCSS: new Set(),
injectedJS: new Set(),
injectedFontPreloadTags: new Set(),
rootLayoutIncluded: false,
getViewportReady,
getMetadataReady,
preloadCallbacks,
StreamingMetadataOutlet
})).map((path)=>path.slice(1)) // remove the '' (root) segment
;
}
// If we have an action result, then this is a server action response.
// We can rely on this because `ActionResult` will always be a promise, even if
// the result is falsey.
if (options == null ? void 0 : options.actionResult) {
return {
a: options.actionResult,
f: flightData,
b: ctx.sharedContext.buildId
};
}
// Otherwise, it's a regular RSC response.
return {
b: ctx.sharedContext.buildId,
f: flightData,
S: workStore.isStaticGeneration
};
}
function createErrorContext(ctx, renderSource) {
return {
routerKind: 'App Router',
routePath: ctx.pagePath,
routeType: ctx.isAction ? 'action' : 'render',
renderSource,
revalidateReason: getRevalidateReason(ctx.workStore)
};
}
/**
* Produces a RenderResult containing the Flight data for the given request. See
* `generateDynamicRSCPayload` for information on the contents of the render result.
*/ async function generateDynamicFlightRenderResult(req, ctx, requestStore, options) {
const renderOpts = ctx.renderOpts;
function onFlightDataRenderError(err) {
return renderOpts.onInstrumentationRequestError == null ? void 0 : renderOpts.onInstrumentationRequestError.call(renderOpts, err, req, createErrorContext(ctx, 'react-server-components-payload'));
}
const onError = createFlightReactServerErrorHandler(!!renderOpts.dev, onFlightDataRenderError);
const RSCPayload = await workUnitAsyncStorage.run(requestStore, generateDynamicRSCPayload, ctx, options);
if (// We only want this behavior when running `next dev`
renderOpts.dev && // We only want this behavior when we have React's dev builds available
process.env.NODE_ENV === 'development' && // We only have a Prerender environment for projects opted into dynamicIO
renderOpts.experimental.dynamicIO) {
const [resolveValidation, validationOutlet] = createValidationOutlet();
RSCPayload._validation = validationOutlet;
spawnDynamicValidationInDev(resolveValidation, ctx.componentMod.tree, ctx, false, ctx.clientReferenceManifest, ctx.workStore.route, requestStore);
}
// For app dir, use the bundled version of Flight server renderer (renderToReadableStream)
// which contains the subset React.
const flightReadableStream = workUnitAsyncStorage.run(requestStore, ctx.componentMod.renderToReadableStream, RSCPayload, ctx.clientReferenceManifest.clientModules, {
onError,
temporaryReferences: options == null ? void 0 : options.temporaryReferences
});
return new FlightRenderResult(flightReadableStream, {
fetchMetrics: ctx.workStore.fetchMetrics
});
}
/**
* Performs a "warmup" render of the RSC payload for a given route. This function is called by the server
* prior to an actual render request in Dev mode only. It's purpose is to fill caches so the actual render
* can accurately log activity in the right render context (Prerender vs Render).
*
* At the moment this implementation is mostly a fork of generateDynamicFlightRenderResult
*/ async function warmupDevRender(req, ctx) {
const renderOpts = ctx.renderOpts;
if (!renderOpts.dev) {
throw Object.defineProperty(new InvariantError('generateDynamicFlightRenderResult should never be called in `next start` mode.'), "__NEXT_ERROR_CODE", {
value: "E523",
enumerable: false,
configurable: true
});
}
const rootParams = getRootParams(ctx.componentMod.tree, ctx.getDynamicParamFromSegment);
function onFlightDataRenderError(err) {
return renderOpts.onInstrumentationRequestError == null ? void 0 : renderOpts.onInstrumentationRequestError.call(renderOpts, err, req, createErrorContext(ctx, 'react-server-components-payload'));
}
const onError = createFlightReactServerErrorHandler(true, onFlightDataRenderError);
// We're doing a dev warmup, so we should create a new resume data cache so
// we can fill it.
const prerenderResumeDataCache = createPrerenderResumeDataCache();
const renderController = new AbortController();
const prerenderController = new AbortController();
const cacheSignal = new CacheSignal();
const prerenderStore = {
type: 'prerender',
phase: 'render',
rootParams,
implicitTags: [],
renderSignal: renderController.signal,
controller: prerenderController,
cacheSignal,
dynamicTracking: null,
revalidate: INFINITE_CACHE,
expire: INFINITE_CACHE,
stale: INFINITE_CACHE,
tags: [],
prerenderResumeDataCache
};
const rscPayload = await workUnitAsyncStorage.run(prerenderStore, generateDynamicRSCPayload, ctx);
// For app dir, use the bundled version of Flight server renderer (renderToReadableStream)
// which contains the subset React.
workUnitAsyncStorage.run(prerenderStore, ctx.componentMod.renderToReadableStream, rscPayload, ctx.clientReferenceManifest.clientModules, {
onError,
signal: renderController.signal
});
// Wait for all caches to be finished filling
await cacheSignal.cacheReady();
// We unset the cache so any late over-run renders aren't able to write into this cache
prerenderStore.prerenderResumeDataCache = null;
// Abort the render
renderController.abort();
// We don't really want to return a result here but the stack of functions
// that calls into renderToHTML... expects a result. We should refactor this to
// lift the warmup pathway outside of renderToHTML... but for now this suffices
return new FlightRenderResult('', {
fetchMetrics: ctx.workStore.fetchMetrics,
devRenderResumeDataCache: createRenderResumeDataCache(prerenderResumeDataCache)
});
}
/**
* Crawlers will inadvertently think the canonicalUrl in the RSC payload should be crawled
* when our intention is to just seed the router state with the current URL.
* This function splits up the pathname so that we can later join it on
* when we're ready to consume the path.
*/ function prepareInitialCanonicalUrl(url) {
return (url.pathname + url.search).split('/');
}
// This is the data necessary to render <AppRouter /> when no SSR errors are encountered
async function getRSCPayload(tree, ctx, is404) {
const injectedCSS = new Set();
const injectedJS = new Set();
const injectedFontPreloadTags = new Set();
let missingSlots;
// We only track missing parallel slots in development
if (process.env.NODE_ENV === 'development') {
missingSlots = new Set();
}
const { getDynamicParamFromSegment, query, appUsingSizeAdjustment, componentMod: { GlobalError, createServerSearchParamsForMetadata, createServerParamsForMetadata, createMetadataComponents, MetadataBoundary, ViewportBoundary }, url, workStore } = ctx;
const initialTree = createFlightRouterStateFromLoaderTree(tree, getDynamicParamFromSegment, query);
const serveStreamingMetadata = !!ctx.renderOpts.serveStreamingMetadata;
const searchParams = createServerSearchParamsForMetadata(query, workStore);
const { ViewportTree, MetadataTree, getViewportReady, getMetadataReady, StreamingMetadataOutlet } = createMetadataComponents({
tree,
errorType: is404 ? 'not-found' : undefined,
searchParams,
metadataContext: createTrackedMetadataContext(url.pathname, ctx.renderOpts, workStore),
getDynamicParamFromSegment,
appUsingSizeAdjustment,
createServerParamsForMetadata,
workStore,
MetadataBoundary,
ViewportBoundary,
serveStreamingMetadata
});
const preloadCallbacks = [];
const { StreamingMetadata, StaticMetadata } = createDivergedMetadataComponents(()=>{
return(// Not add requestId as react key to ensure segment prefetch could result consistently if nothing changed
/*#__PURE__*/ _jsx(MetadataTree, {}));
}, serveStreamingMetadata);
const seedData = await createComponentTree({
ctx,
loaderTree: tree,
parentParams: {},
injectedCSS,
injectedJS,
injectedFontPreloadTags,
rootLayoutIncluded: false,
getViewportReady,
getMetadataReady,
missingSlots,
preloadCallbacks,
authInterrupts: ctx.renderOpts.experimental.authInterrupts,
StreamingMetadata,
StreamingMetadataOutlet
});
// When the `vary` response header is present with `Next-URL`, that means there's a chance
// it could respond differently if there's an interception route. We provide this information
// to `AppRouter` so that it can properly seed the prefetch cache with a prefix, if needed.
const varyHeader = ctx.res.getHeader('vary');
const couldBeIntercepted = typeof varyHeader === 'string' && varyHeader.includes(NEXT_URL);
const initialHead = /*#__PURE__*/ _jsxs(React.Fragment, {
children: [
/*#__PURE__*/ _jsx(NonIndex, {
ctx: ctx
}),
/*#__PURE__*/ _jsx(ViewportTree, {}, ctx.requestId),
/*#__PURE__*/ _jsx(StaticMetadata, {})
]
}, flightDataPathHeadKey);
const globalErrorStyles = await getGlobalErrorStyles(tree, ctx);
// Assume the head we're rendering contains only partial data if PPR is
// enabled and this is a statically generated response. This is used by the
// client Segment Cache after a prefetch to determine if it can skip the
// second request to fill in the dynamic data.
//
// See similar comment in create-component-tree.tsx for more context.
const isPossiblyPartialHead = workStore.isStaticGeneration && ctx.renderOpts.experimental.isRoutePPREnabled === true;
return {
// See the comment above the `Preloads` component (below) for why this is part of the payload
P: /*#__PURE__*/ _jsx(Preloads, {
preloadCallbacks: preloadCallbacks
}),
b: ctx.sharedContext.buildId,
p: ctx.assetPrefix,
c: prepareInitialCanonicalUrl(url),
i: !!couldBeIntercepted,
f: [
[
initialTree,
seedData,
initialHead,
isPossiblyPartialHead
]
],
m: missingSlots,
G: [
GlobalError,
globalErrorStyles
],
s: typeof ctx.renderOpts.postponed === 'string',
S: workStore.isStaticGeneration
};
}
/**
* Preload calls (such as `ReactDOM.preloadStyle` and `ReactDOM.preloadFont`) need to be called during rendering
* in order to create the appropriate preload tags in the DOM, otherwise they're a no-op. Since we invoke
* renderToReadableStream with a function that returns component props rather than a component itself, we use
* this component to "render " the preload calls.
*/ function Preloads({ preloadCallbacks }) {
preloadCallbacks.forEach((preloadFn)=>preloadFn());
return null;
}
// This is the data necessary to render <AppRouter /> when an error state is triggered
async function getErrorRSCPayload(tree, ctx, ssrError, errorType) {
const { getDynamicParamFromSegment, query, appUsingSizeAdjustment, componentMod: { GlobalError, createServerSearchParamsForMetadata, createServerParamsForMetadata, createMetadataComponents, MetadataBoundary, ViewportBoundary }, url, requestId, workStore } = ctx;
const serveStreamingMetadata = !!ctx.renderOpts.serveStreamingMetadata;
const searchParams = createServerSearchParamsForMetadata(query, workStore);
const { MetadataTree, ViewportTree } = createMetadataComponents({
tree,
searchParams,
// We create an untracked metadata context here because we can't postpone
// again during the error render.
metadataContext: createMetadataContext(url.pathname, ctx.renderOpts),
errorType,
getDynamicParamFromSegment,
appUsingSizeAdjustment,
createServerParamsForMetadata,
workStore,
MetadataBoundary,
ViewportBoundary,
serveStreamingMetadata: serveStreamingMetadata
});
const { StreamingMetadata, StaticMetadata } = createDivergedMetadataComponents(()=>/*#__PURE__*/ _jsx(React.Fragment, {
children: /*#__PURE__*/ _jsx(MetadataTree, {}, requestId)
}, flightDataPathHeadKey), serveStreamingMetadata);
const initialHead = /*#__PURE__*/ _jsxs(React.Fragment, {
children: [
/*#__PURE__*/ _jsx(NonIndex, {
ctx: ctx
}),
/*#__PURE__*/ _jsx(ViewportTree, {}, requestId),
process.env.NODE_ENV === 'development' && /*#__PURE__*/ _jsx("meta", {
name: "next-error",
content: "not-found"
}),
/*#__PURE__*/ _jsx(StaticMetadata, {})
]
}, flightDataPathHeadKey);
const initialTree = createFlightRouterStateFromLoaderTree(tree, getDynamicParamFromSegment, query);
let err = undefined;
if (ssrError) {
err = isError(ssrError) ? ssrError : Object.defineProperty(new Error(ssrError + ''), "__NEXT_ERROR_CODE", {
value: "E394",
enumerable: false,
configurable: true
});
}
// For metadata notFound error there's no global not found boundary on top
// so we create a not found page with AppRouter
const seedData = [
initialTree[0],
/*#__PURE__*/ _jsxs("html", {
id: "__next_error__",
children: [
/*#__PURE__*/ _jsx("head", {
children: StreamingMetadata ? /*#__PURE__*/ _jsx(StreamingMetadata, {}) : null
}),
/*#__PURE__*/ _jsx("body", {
children: process.env.NODE_ENV !== 'production' && err ? /*#__PURE__*/ _jsx("template", {
"data-next-error-message": err.message,
"data-next-error-digest": 'digest' in err ? err.digest : '',
"data-next-error-stack": err.stack
}) : null
})
]
}),
{},
null,
false
];
const globalErrorStyles = await getGlobalErrorStyles(tree, ctx);
const isPossiblyPartialHead = workStore.isStaticGeneration && ctx.renderOpts.experimental.isRoutePPREnabled === true;
return {
b: ctx.sharedContext.buildId,
p: ctx.assetPrefix,
c: prepareInitialCanonicalUrl(url),
m: undefined,
i: false,
f: [
[
initialTree,
seedData,
initialHead,
isPossiblyPartialHead
]
],
G: [
GlobalError,
globalErrorStyles
],
s: typeof ctx.renderOpts.postponed === 'string',
S: workStore.isStaticGeneration
};
}
// This component must run in an SSR context. It will render the RSC root component
function App({ reactServerStream, preinitScripts, clientReferenceManifest, nonce, ServerInsertedHTMLProvider, ServerInsertedMetadataProvider }) {
preinitScripts();
const response = React.use(useFlightStream(reactServerStream, clientReferenceManifest, nonce));
const initialState = createInitialRouterState({
initialFlightData: response.f,
initialCanonicalUrlParts: response.c,
initialParallelRoutes: new Map(),
// location is not initialized in the SSR render
// it's set to window.location during hydration
location: null,
couldBeIntercepted: response.i,
postponed: response.s,
prerendered: response.S
});
const actionQueue = createMutableActionQueue(initialState);
const { HeadManagerContext } = require('../../shared/lib/head-manager-context.shared-runtime');
return /*#__PURE__*/ _jsx(HeadManagerContext.Provider, {
value: {
appDir: true,
nonce
},
children: /*#__PURE__*/ _jsx(ServerInsertedMetadataProvider, {
children: /*#__PURE__*/ _jsx(ServerInsertedHTMLProvider, {
children: /*#__PURE__*/ _jsx(AppRouter, {
actionQueue: actionQueue,
globalErrorComponentAndStyles: response.G,
assetPrefix: response.p
})
})
})
});
}
// @TODO our error stream should be probably just use the same root component. But it was previously
// different I don't want to figure out if that is meaningful at this time so just keeping the behavior
// consistent for now.
function AppWithoutContext({ reactServerStream, preinitScripts, clientReferenceManifest, nonce }) {
preinitScripts();
const response = React.use(useFlightStream(reactServerStream, clientReferenceManifest, nonce));
const initialState = createInitialRouterState({
initialFlightData: response.f,
initialCanonicalUrlParts: response.c,
initialParallelRoutes: new Map(),
// location is not initialized in the SSR render
// it's set to window.location during hydration
location: null,
couldBeIntercepted: response.i,
postponed: response.s,
prerendered: response.S
});
const actionQueue = createMutableActionQueue(initialState);
return /*#__PURE__*/ _jsx(AppRouter, {
actionQueue: actionQueue,
globalErrorComponentAndStyles: response.G,
assetPrefix: response.p
});
}
async function renderToHTMLOrFlightImpl(req, res, url, pagePath, query, renderOpts, workStore, parsedRequestHeaders, requestEndedState, postponedState, implicitTags, serverComponentsHmrCache, sharedContext) {
const isNotFoundPath = pagePath === '/404';
if (isNotFoundPath) {
res.statusCode = 404;
}
// A unique request timestamp used by development to ensure that it's
// consistent and won't change during this request. This is important to
// avoid that resources can be deduped by React Float if the same resource is
// rendered or preloaded multiple times: `<link href="a.css?v={Date.now()}"/>`.
const requestTimestamp = Date.now();
const { serverActionsManifest, ComponentMod, nextFontManifest, serverActions, assetPrefix = '', enableTainting } = renderOpts;
// We need to expose the bundled `require` API globally for
// react-server-dom-webpack. This is a hack until we find a better way.
if (ComponentMod.__next_app__) {
const instrumented = wrapClientComponentLoader(ComponentMod);
// @ts-ignore
globalThis.__next_require__ = instrumented.require;
// When we are prerendering if there is a cacheSignal for tracking
// cache reads we wrap the loadChunk in this tracking. This allows us
// to treat chunk loading with similar semantics as cache reads to avoid
// async loading chunks from causing a prerender to abort too early.
const __next_chunk_load__ = (...args)=>{
const loadingChunk = instrumented.loadChunk(...args);
trackChunkLoading(loadingChunk);
return loadingChunk;
};
// @ts-expect-error
globalThis.__next_chunk_load__ = __next_chunk_load__;
}
if (process.env.NODE_ENV === 'development') {
// reset isr status at start of request
const { pathname } = new URL(req.url || '/', 'http://n');
renderOpts.setIsrStatus == null ? void 0 : renderOpts.setIsrStatus.call(renderOpts, pathname, null);
}
if (// The type check here ensures that `req` is correctly typed, and the
// environment variable check provides dead code elimination.
process.env.NEXT_RUNTIME !== 'edge' && isNodeNextRequest(req)) {
req.originalRequest.on('end', ()=>{
requestEndedState.ended = true;
if ('performance' in globalThis) {
const metrics = getClientComponentLoaderMetrics({
reset: true
});
if (metrics) {
getTracer().startSpan(NextNodeServerSpan.clientComponentLoading, {
startTime: metrics.clientComponentLoadStart,
attributes: {
'next.clientComponentLoadCount': metrics.clientComponentLoadCount,
'next.span_type': NextNodeServerSpan.clientComponentLoading
}
}).end(metrics.clientComponentLoadStart + metrics.clientComponentLoadTimes);
}
}
});
}
const metadata = {};
const appUsingSizeAdjustment = !!(nextFontManifest == null ? void 0 : nextFontManifest.appUsingSizeAdjust);
// TODO: fix this typescript
const clientReferenceManifest = renderOpts.clientReferenceManifest;
const serverModuleMap = createServerModuleMap({
serverActionsManifest
});
setReferenceManifestsSingleton({
page: workStore.page,
clientReferenceManifest,
serverActionsManifest,
serverModuleMap
});
ComponentMod.patchFetch();
// Pull out the hooks/references from the component.
const { tree: loaderTree, taintObjectReference } = ComponentMod;
if (enableTainting) {
taintObjectReference('Do not pass process.env to client components since it will leak sensitive data', process.env);
}
workStore.fetchMetrics = [];
metadata.fetchMetrics = workStore.fetchMetrics;
// don't modify original query object
query = {
...query
};
stripInternalQueries(query);
const { flightRouterState, isPrefetchRequest, isRSCRequest, isDevWarmupRequest, isHmrRefresh, nonce } = parsedRequestHeaders;
/**
* The metadata items array created in next-app-loader with all relevant information
* that we need to resolve the final metadata.
*/ let requestId;
if (process.env.NEXT_RUNTIME === 'edge') {
requestId = crypto.randomUUID();
} else {
requestId = require('next/dist/compiled/nanoid').nanoid();
}
/**
* Dynamic parameters. E.g. when you visit `/dashboard/vercel` which is rendered by `/dashboard/[slug]` the value will be {"slug": "vercel"}.
*/ const params = renderOpts.params ?? {};
const { isStaticGeneration, fallbackRouteParams } = workStore;
const getDynamicParamFromSegment = makeGetDynamicParamFromSegment(params, pagePath, fallbackRouteParams);
const isActionRequest = getServerActionRequestMetadata(req).isServerAction;
const ctx = {
componentMod: ComponentMod,
url,
renderOpts,
workStore,
parsedRequestHeaders,
getDynamicParamFromSegment,
query,
isPrefetch: isPrefetchRequest,
isAction: isActionRequest,
requestTimestamp,
appUsingSizeAdjustment,
flightRouterState,
requestId,
pagePath,
clientReferenceManifest,
assetPrefix,
isNotFoundPath,
nonce,
res,
sharedContext
};
getTracer().setRootSpanAttribute('next.route', pagePath);
if (isStaticGeneration) {
var _metadata_cacheControl;
// We're either building or revalidating. In either case we need to
// prerender our page rather than render it.
const prerenderToStreamWithTracing = getTracer().wrap(AppRenderSpan.getBodyResult, {
spanName: `prerender route (app) ${pagePath}`,
attributes: {
'next.route': pagePath
}
}, prerenderToStream);
const response = await prerenderToStreamWithTracing(req, res, ctx, metadata, workStore, loaderTree, implicitTags);
// If we're debugging partial prerendering, print all the dynamic API accesses
// that occurred during the render.
// @TODO move into renderToStream function
if (response.dynamicAccess && accessedDynamicData(response.dynamicAccess) && renderOpts.isDebugDynamicAccesses) {
warn('The following dynamic usage was detected:');
for (const access of formatDynamicAPIAccesses(response.dynamicAccess)){
warn(access);
}
}
// If we encountered any unexpected errors during build we fail the
// prerendering phase and the build.
if (response.digestErrorsMap.size) {
const buildFailingError = response.digestErrorsMap.values().next().value;
if (buildFailingError) throw buildFailingError;
}
// Pick first userland SSR error, which is also not a RSC error.
if (response.ssrErrors.length) {
const buildFailingError = response.ssrErrors.find((err)=>isUserLandError(err));
if (buildFailingError) throw buildFailingError;
}
const options = {
metadata
};
// If we have pending revalidates, wait until they are all resolved.
if (workStore.pendingRevalidates || workStore.pendingRevalidateWrites || workStore.revalidatedTags) {
var _workStore_incrementalCache;
const pendingPromise = Promise.all([
(_workStore_incrementalCache = workStore.incrementalCache) == null ? void 0 : _workStore_incrementalCache.revalidateTag(workStore.revalidatedTags || []),
...Object.values(workStore.pendingRevalidates || {}),
...workStore.pendingRevalidateWrites || []
]).finally(()=>{
if (process.env.NEXT_PRIVATE_DEBUG_CACHE) {
console.log('pending revalidates promise finished for:', url);
}
});
if (renderOpts.waitUntil) {
renderOpts.waitUntil(pendingPromise);
} else {
options.waitUntil = pendingPromise;
}
}
if (response.collectedTags) {
metadata.fetchTags = response.collectedTags.join(',');
}
// Let the client router know how long to keep the cached entry around.
const staleHeader = String(response.collectedStale);
res.setHeader(NEXT_ROUTER_STALE_TIME_HEADER, staleHeader);
metadata.headers ??= {};
metadata.headers[NEXT_ROUTER_STALE_TIME_HEADER] = staleHeader;
// If force static is specifically set to false, we should not revalidate
// the page.
if (workStore.forceStatic === false || response.collectedRevalidate === 0) {
metadata.cacheControl = {
revalidate: 0,
expire: undefined
};
} else {
// Copy the cache control value onto the render result metadata.
metadata.cacheControl = {
revalidate: response.collectedRevalidate >= INFINITE_CACHE ? false : response.collectedRevalidate,
expire: response.collectedExpire >= INFINITE_CACHE ? undefined : response.collectedExpire
};
}
// provide bailout info for debugging
if (((_metadata_cacheControl = metadata.cacheControl) == null ? void 0 : _metadata_cacheControl.revalidate) === 0) {
metadata.staticBailoutInfo = {
description: workStore.dynamicUsageDescription,
stack: workStore.dynamicUsageStack
};
}
return new RenderResult(await streamToString(response.stream), options);
} else {
// We're rendering dynamically
const renderResumeDataCache = renderOpts.devRenderResumeDataCache ?? (postponedState == null ? void 0 : postponedState.renderResumeDataCache);
const rootParams = getRootParams(loaderTree, ctx.getDynamicParamFromSegment);
const requestStore = createRequestStoreForRender(req, res, url, rootParams, implicitTags, renderOpts.onUpdateCookies, renderOpts.previewProps, isHmrRefresh, serverComponentsHmrCache, renderResumeDataCache);
if (process.env.NODE_ENV === 'development' && renderOpts.setIsrStatus && // The type check here ensures that `req` is correctly typed, and the
// environment variable check provides dead code elimination.
process.env.NEXT_RUNTIME !== 'edge' && isNodeNextRequest(req) && !isDevWarmupRequest) {
const setIsrStatus = renderOpts.setIsrStatus;
req.originalRequest.on('end', ()=>{
if (!requestStore.usedDynamic && !workStore.forceDynamic) {
// only node can be ISR so we only need to update the status here
const { pathname } = new URL(req.url || '/', 'http://n');
setIsrStatus(pathname, true);
}
});
}
if (isDevWarmupRequest) {
return warmupDevRender(req, ctx);
} else if (isRSCRequest) {
return generateDynamicFlightRenderResult(req, ctx, requestStore);
}
const renderToStreamWithTracing = getTracer().wrap(AppRenderSpan.getBodyResult, {
spanName: `render route (app) ${pagePath}`,
attributes: {
'next.route': pagePath
}
}, renderToStream);
let formState = null;
if (isActionRequest) {
// For action requests, we handle them differently with a special render result.
const actionRequestResult = await handleAction({
req,
res,
ComponentMod,
serverModuleMap,
generateFlight: generateDynamicFlightRenderResult,
workStore,
requestStore,
serverActions,
ctx
});
if (actionRequestResult) {
if (actionRequestResult.type === 'not-found') {
const notFoundLoaderTree = createNotFoundLoaderTree(loaderTree);
res.statusCode = 404;
const stream = await renderToStreamWithTracing(requestStore, req, res, ctx, workStore, notFoundLoaderTree, formState, postponedState);
return new RenderResult(stream, {
metadata
});
} else if (actionRequestResult.type === 'done') {
if (actionRequestResult.result) {
actionRequestResult.result.assignMetadata(metadata);
return actionRequestResult.result;
} else if (actionRequestResult.formState) {
formState = actionRequestResult.formState;
}
}
}
}
const options = {
metadata
};
const stream = await renderToStreamWithTracing(requestStore, req, res, ctx, workStore, loaderTree, formState, postponedState);
// If we have pending revalidates, wait until they are all resolved.
if (workStore.pendingRevalidates || workStore.pendingRevalidateWrites || workStore.revalidatedTags) {
var _workStore_incrementalCache1;
const pendingPromise = Promise.all([
(_workStore_incrementalCache1 = workStore.incrementalCache) == null ? void 0 : _workStore_incrementalCache1.revalidateTag(workStore.revalidatedTags || []),
...Object.values(workStore.pendingRevalidates || {}),
...workStore.pendingRevalidateWrites || []
]).finally(()=>{
if (process.env.NEXT_PRIVATE_DEBUG_CACHE) {
console.log('pending revalidates promise finished for:', url);
}
});
if (renderOpts.waitUntil) {
renderOpts.waitUntil(pendingPromise);
} else {
options.waitUntil = pendingPromise;
}
}
// Create the new render result for the response.
return new RenderResult(stream, options);
}
}
export const renderToHTMLOrFlight = (req, res, pagePath, query, fallbackRouteParams, renderOpts, serverComponentsHmrCache, isDevWarmup, sharedContext)=>{
if (!req.url) {
throw Object.defineProperty(new Error('Invalid URL'), "__NEXT_ERROR_CODE", {
value: "E182",
enumerable: false,
configurable: true
});
}
const url = parseRelativeUrl(req.url, undefined, false);
// We read these values from the request object as, in certain cases,
// base-server will strip them to opt into different rendering behavior.
const parsedRequestHeaders = parseRequestHeaders(req.headers, {
isDevWarmup,
isRoutePPREnabled: renderOpts.experimental.isRoutePPREnabled === true
});
const { isPrefetchRequest } = parsedRequestHeaders;
const requestEndedState = {
ended: false
};
let postponedState = null;
// If provided, the postpone state should be parsed so it can be provided to
// React.
if (typeof renderOpts.postponed === 'string') {
if (fallbackRouteParams) {
throw Object.defineProperty(new InvariantError('postponed state should not be provided when fallback params are provided'), "__NEXT_ERROR_CODE", {
value: "E592",
enumerable: false,
configurable: true
});
}
postponedState = parsePostponedState(renderOpts.postponed, renderOpts.params);
}
if ((postponedState == null ? void 0 : postponedState.renderResumeDataCache) && renderOpts.devRenderResumeDataCache) {
throw Object.defineProperty(new InvariantError('postponed state and dev warmup immutable resume data cache should not be provided together'), "__NEXT_ERROR_CODE", {
value: "E589",
enumerable: false,
configurable: true
});
}
const implicitTags = getImplicitTags(renderOpts.routeModule.definition.page, url, fallbackRouteParams);
const workStore = createWorkStore({
page: renderOpts.routeModule.definition.page,
fallbackRouteParams,
renderOpts,
requestEndedState,
// @TODO move to workUnitStore of type Request
isPrefetchRequest,
buildId: sharedContext.buildId
});
return workAsyncStorage.run(workStore, // The function to run
renderToHTMLOrFlightImpl, // all of it's args
req, res, url, pagePath, query, renderOpts, workStore, parsedRequestHeaders, requestEndedState, postponedState, implicitTags, serverComponentsHmrCache, sharedContext);
};
async function renderToStream(requestStore, req, res, ctx, workStore, tr