This commit is contained in:
Kismet Hasanaj
2026-05-02 20:07:02 +02:00
parent ce8672e283
commit 34dc9aec52
9428 changed files with 1733330 additions and 0 deletions
+52
View File
@@ -0,0 +1,52 @@
import { COOKIE_NAME_PRERENDER_BYPASS, checkIsOnDemandRevalidate } from '../api-utils';
export class DraftModeProvider {
constructor(previewProps, req, cookies, mutableCookies){
var _cookies_get;
// The logic for draftMode() is very similar to tryGetPreviewData()
// but Draft Mode does not have any data associated with it.
const isOnDemandRevalidate = previewProps && checkIsOnDemandRevalidate(req, previewProps).isOnDemandRevalidate;
const cookieValue = (_cookies_get = cookies.get(COOKIE_NAME_PRERENDER_BYPASS)) == null ? void 0 : _cookies_get.value;
this._isEnabled = Boolean(!isOnDemandRevalidate && cookieValue && previewProps && (cookieValue === previewProps.previewModeId || // In dev mode, the cookie can be actual hash value preview id but the preview props can still be `development-id`.
process.env.NODE_ENV !== 'production' && previewProps.previewModeId === 'development-id'));
this._previewModeId = previewProps == null ? void 0 : previewProps.previewModeId;
this._mutableCookies = mutableCookies;
}
get isEnabled() {
return this._isEnabled;
}
enable() {
if (!this._previewModeId) {
throw Object.defineProperty(new Error('Invariant: previewProps missing previewModeId this should never happen'), "__NEXT_ERROR_CODE", {
value: "E93",
enumerable: false,
configurable: true
});
}
this._mutableCookies.set({
name: COOKIE_NAME_PRERENDER_BYPASS,
value: this._previewModeId,
httpOnly: true,
sameSite: process.env.NODE_ENV !== 'development' ? 'none' : 'lax',
secure: process.env.NODE_ENV !== 'development',
path: '/'
});
this._isEnabled = true;
}
disable() {
// To delete a cookie, set `expires` to a date in the past:
// https://tools.ietf.org/html/rfc6265#section-4.1.1
// `Max-Age: 0` is not valid, thus ignored, and the cookie is persisted.
this._mutableCookies.set({
name: COOKIE_NAME_PRERENDER_BYPASS,
value: '',
httpOnly: true,
sameSite: process.env.NODE_ENV !== 'development' ? 'none' : 'lax',
secure: process.env.NODE_ENV !== 'development',
path: '/',
expires: new Date(0)
});
this._isEnabled = false;
}
}
//# sourceMappingURL=draft-mode-provider.js.map
@@ -0,0 +1 @@
{"version":3,"sources":["../../../../src/server/async-storage/draft-mode-provider.ts"],"sourcesContent":["import type { IncomingMessage } from 'http'\nimport type { ReadonlyRequestCookies } from '../web/spec-extension/adapters/request-cookies'\nimport type { ResponseCookies } from '../web/spec-extension/cookies'\nimport type { BaseNextRequest } from '../base-http'\nimport type { NextRequest } from '../web/spec-extension/request'\n\nimport {\n COOKIE_NAME_PRERENDER_BYPASS,\n checkIsOnDemandRevalidate,\n} from '../api-utils'\nimport type { __ApiPreviewProps } from '../api-utils'\n\nexport class DraftModeProvider {\n /**\n * @internal - this declaration is stripped via `tsc --stripInternal`\n */\n private _isEnabled: boolean\n\n /**\n * @internal - this declaration is stripped via `tsc --stripInternal`\n */\n private readonly _previewModeId: string | undefined\n\n /**\n * @internal - this declaration is stripped via `tsc --stripInternal`\n */\n private readonly _mutableCookies: ResponseCookies\n\n constructor(\n previewProps: __ApiPreviewProps | undefined,\n req: IncomingMessage | BaseNextRequest<unknown> | NextRequest,\n cookies: ReadonlyRequestCookies,\n mutableCookies: ResponseCookies\n ) {\n // The logic for draftMode() is very similar to tryGetPreviewData()\n // but Draft Mode does not have any data associated with it.\n const isOnDemandRevalidate =\n previewProps &&\n checkIsOnDemandRevalidate(req, previewProps).isOnDemandRevalidate\n\n const cookieValue = cookies.get(COOKIE_NAME_PRERENDER_BYPASS)?.value\n\n this._isEnabled = Boolean(\n !isOnDemandRevalidate &&\n cookieValue &&\n previewProps &&\n (cookieValue === previewProps.previewModeId ||\n // In dev mode, the cookie can be actual hash value preview id but the preview props can still be `development-id`.\n (process.env.NODE_ENV !== 'production' &&\n previewProps.previewModeId === 'development-id'))\n )\n\n this._previewModeId = previewProps?.previewModeId\n this._mutableCookies = mutableCookies\n }\n\n get isEnabled() {\n return this._isEnabled\n }\n\n enable() {\n if (!this._previewModeId) {\n throw new Error(\n 'Invariant: previewProps missing previewModeId this should never happen'\n )\n }\n\n this._mutableCookies.set({\n name: COOKIE_NAME_PRERENDER_BYPASS,\n value: this._previewModeId,\n httpOnly: true,\n sameSite: process.env.NODE_ENV !== 'development' ? 'none' : 'lax',\n secure: process.env.NODE_ENV !== 'development',\n path: '/',\n })\n\n this._isEnabled = true\n }\n\n disable() {\n // To delete a cookie, set `expires` to a date in the past:\n // https://tools.ietf.org/html/rfc6265#section-4.1.1\n // `Max-Age: 0` is not valid, thus ignored, and the cookie is persisted.\n this._mutableCookies.set({\n name: COOKIE_NAME_PRERENDER_BYPASS,\n value: '',\n httpOnly: true,\n sameSite: process.env.NODE_ENV !== 'development' ? 'none' : 'lax',\n secure: process.env.NODE_ENV !== 'development',\n path: '/',\n expires: new Date(0),\n })\n\n this._isEnabled = false\n }\n}\n"],"names":["COOKIE_NAME_PRERENDER_BYPASS","checkIsOnDemandRevalidate","DraftModeProvider","constructor","previewProps","req","cookies","mutableCookies","isOnDemandRevalidate","cookieValue","get","value","_isEnabled","Boolean","previewModeId","process","env","NODE_ENV","_previewModeId","_mutableCookies","isEnabled","enable","Error","set","name","httpOnly","sameSite","secure","path","disable","expires","Date"],"mappings":"AAMA,SACEA,4BAA4B,EAC5BC,yBAAyB,QACpB,eAAc;AAGrB,OAAO,MAAMC;IAgBXC,YACEC,YAA2C,EAC3CC,GAA6D,EAC7DC,OAA+B,EAC/BC,cAA+B,CAC/B;YAOoBD;QANpB,mEAAmE;QACnE,4DAA4D;QAC5D,MAAME,uBACJJ,gBACAH,0BAA0BI,KAAKD,cAAcI,oBAAoB;QAEnE,MAAMC,eAAcH,eAAAA,QAAQI,GAAG,CAACV,kDAAZM,aAA2CK,KAAK;QAEpE,IAAI,CAACC,UAAU,GAAGC,QAChB,CAACL,wBACCC,eACAL,gBACCK,CAAAA,gBAAgBL,aAAaU,aAAa,IACzC,mHAAmH;QAClHC,QAAQC,GAAG,CAACC,QAAQ,KAAK,gBACxBb,aAAaU,aAAa,KAAK,gBAAgB;QAGvD,IAAI,CAACI,cAAc,GAAGd,gCAAAA,aAAcU,aAAa;QACjD,IAAI,CAACK,eAAe,GAAGZ;IACzB;IAEA,IAAIa,YAAY;QACd,OAAO,IAAI,CAACR,UAAU;IACxB;IAEAS,SAAS;QACP,IAAI,CAAC,IAAI,CAACH,cAAc,EAAE;YACxB,MAAM,qBAEL,CAFK,IAAII,MACR,2EADI,qBAAA;uBAAA;4BAAA;8BAAA;YAEN;QACF;QAEA,IAAI,CAACH,eAAe,CAACI,GAAG,CAAC;YACvBC,MAAMxB;YACNW,OAAO,IAAI,CAACO,cAAc;YAC1BO,UAAU;YACVC,UAAUX,QAAQC,GAAG,CAACC,QAAQ,KAAK,gBAAgB,SAAS;YAC5DU,QAAQZ,QAAQC,GAAG,CAACC,QAAQ,KAAK;YACjCW,MAAM;QACR;QAEA,IAAI,CAAChB,UAAU,GAAG;IACpB;IAEAiB,UAAU;QACR,2DAA2D;QAC3D,oDAAoD;QACpD,wEAAwE;QACxE,IAAI,CAACV,eAAe,CAACI,GAAG,CAAC;YACvBC,MAAMxB;YACNW,OAAO;YACPc,UAAU;YACVC,UAAUX,QAAQC,GAAG,CAACC,QAAQ,KAAK,gBAAgB,SAAS;YAC5DU,QAAQZ,QAAQC,GAAG,CAACC,QAAQ,KAAK;YACjCW,MAAM;YACNE,SAAS,IAAIC,KAAK;QACpB;QAEA,IAAI,CAACnB,UAAU,GAAG;IACpB;AACF","ignoreList":[0]}
+118
View File
@@ -0,0 +1,118 @@
import { FLIGHT_HEADERS } from '../../client/components/app-router-headers';
import { HeadersAdapter } from '../web/spec-extension/adapters/headers';
import { MutableRequestCookiesAdapter, RequestCookiesAdapter, responseCookiesToRequestCookies, createCookiesWithMutableAccessCheck } from '../web/spec-extension/adapters/request-cookies';
import { ResponseCookies, RequestCookies } from '../web/spec-extension/cookies';
import { DraftModeProvider } from './draft-mode-provider';
import { splitCookiesString } from '../web/utils';
function getHeaders(headers) {
const cleaned = HeadersAdapter.from(headers);
for (const header of FLIGHT_HEADERS){
cleaned.delete(header);
}
return HeadersAdapter.seal(cleaned);
}
function getMutableCookies(headers, onUpdateCookies) {
const cookies = new RequestCookies(HeadersAdapter.from(headers));
return MutableRequestCookiesAdapter.wrap(cookies, onUpdateCookies);
}
/**
* If middleware set cookies in this request (indicated by `x-middleware-set-cookie`),
* then merge those into the existing cookie object, so that when `cookies()` is accessed
* it's able to read the newly set cookies.
*/ function mergeMiddlewareCookies(req, existingCookies) {
if ('x-middleware-set-cookie' in req.headers && typeof req.headers['x-middleware-set-cookie'] === 'string') {
const setCookieValue = req.headers['x-middleware-set-cookie'];
const responseHeaders = new Headers();
for (const cookie of splitCookiesString(setCookieValue)){
responseHeaders.append('set-cookie', cookie);
}
const responseCookies = new ResponseCookies(responseHeaders);
// Transfer cookies from ResponseCookies to RequestCookies
for (const cookie of responseCookies.getAll()){
existingCookies.set(cookie);
}
}
}
export function createRequestStoreForRender(req, res, url, rootParams, implicitTags, onUpdateCookies, previewProps, isHmrRefresh, serverComponentsHmrCache, renderResumeDataCache, fallbackParams) {
return createRequestStoreImpl(// Pages start in render phase by default
'render', req, res, url, rootParams, implicitTags, onUpdateCookies, renderResumeDataCache, previewProps, isHmrRefresh, serverComponentsHmrCache, fallbackParams);
}
export function createRequestStoreForAPI(req, url, implicitTags, onUpdateCookies, previewProps) {
return createRequestStoreImpl(// API routes start in action phase by default
'action', req, undefined, url, {}, implicitTags, onUpdateCookies, null, previewProps, false, undefined, null);
}
function createRequestStoreImpl(phase, req, res, url, rootParams, implicitTags, onUpdateCookies, renderResumeDataCache, previewProps, isHmrRefresh, serverComponentsHmrCache, fallbackParams) {
function defaultOnUpdateCookies(cookies) {
if (res) {
res.setHeader('Set-Cookie', cookies);
}
}
const cache = {};
return {
type: 'request',
phase,
implicitTags,
// Rather than just using the whole `url` here, we pull the parts we want
// to ensure we don't use parts of the URL that we shouldn't. This also
// lets us avoid requiring an empty string for `search` in the type.
url: {
pathname: url.pathname,
search: url.search ?? ''
},
rootParams,
get headers () {
if (!cache.headers) {
// Seal the headers object that'll freeze out any methods that could
// mutate the underlying data.
cache.headers = getHeaders(req.headers);
}
return cache.headers;
},
get cookies () {
if (!cache.cookies) {
// if middleware is setting cookie(s), then include those in
// the initial cached cookies so they can be read in render
const requestCookies = new RequestCookies(HeadersAdapter.from(req.headers));
mergeMiddlewareCookies(req, requestCookies);
// Seal the cookies object that'll freeze out any methods that could
// mutate the underlying data.
cache.cookies = RequestCookiesAdapter.seal(requestCookies);
}
return cache.cookies;
},
set cookies (value){
cache.cookies = value;
},
get mutableCookies () {
if (!cache.mutableCookies) {
const mutableCookies = getMutableCookies(req.headers, onUpdateCookies || (res ? defaultOnUpdateCookies : undefined));
mergeMiddlewareCookies(req, mutableCookies);
cache.mutableCookies = mutableCookies;
}
return cache.mutableCookies;
},
get userspaceMutableCookies () {
if (!cache.userspaceMutableCookies) {
const userspaceMutableCookies = createCookiesWithMutableAccessCheck(this);
cache.userspaceMutableCookies = userspaceMutableCookies;
}
return cache.userspaceMutableCookies;
},
get draftMode () {
if (!cache.draftMode) {
cache.draftMode = new DraftModeProvider(previewProps, req, this.cookies, this.mutableCookies);
}
return cache.draftMode;
},
renderResumeDataCache: renderResumeDataCache ?? null,
isHmrRefresh,
serverComponentsHmrCache: serverComponentsHmrCache || globalThis.__serverComponentsHmrCache,
fallbackParams
};
}
export function synchronizeMutableCookies(store) {
// TODO: does this need to update headers as well?
store.cookies = RequestCookiesAdapter.seal(responseCookiesToRequestCookies(store.mutableCookies));
}
//# sourceMappingURL=request-store.js.map
File diff suppressed because one or more lines are too long
+11
View File
@@ -0,0 +1,11 @@
/**
* Implementations provide a wrapping function that will provide the storage to
* async calls derived from the provided callback function.
*
* @param storage underlying storage object
* @param context context used to create the storage object
* @param callback function to call within the scope of the storage
* @returns the result of the callback
*/ export { };
//# sourceMappingURL=with-store.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"sources":["../../../../src/server/async-storage/with-store.ts"],"sourcesContent":["import type { AsyncLocalStorage } from 'async_hooks'\n\n/**\n * Implementations provide a wrapping function that will provide the storage to\n * async calls derived from the provided callback function.\n *\n * @param storage underlying storage object\n * @param context context used to create the storage object\n * @param callback function to call within the scope of the storage\n * @returns the result of the callback\n */\nexport type WithStore<Store extends {}, Context extends {}> = <Result>(\n storage: AsyncLocalStorage<Store>,\n context: Context,\n callback: (store: Store) => Result\n) => Result\n"],"names":[],"mappings":"AAEA;;;;;;;;CAQC,GACD,WAIW","ignoreList":[0]}
+81
View File
@@ -0,0 +1,81 @@
import { AfterContext } from '../after/after-context';
import { normalizeAppPath } from '../../shared/lib/router/utils/app-paths';
import { createLazyResult } from '../lib/lazy-result';
import { getCacheHandlerEntries } from '../use-cache/handlers';
import { createSnapshot } from '../app-render/async-local-storage';
export function createWorkStore({ page, renderOpts, isPrefetchRequest, buildId, previouslyRevalidatedTags, nonce }) {
/**
* Rules of Static & Dynamic HTML:
*
* 1.) We must generate static HTML unless the caller explicitly opts
* in to dynamic HTML support.
*
* 2.) If dynamic HTML support is requested, we must honor that request
* or throw an error. It is the sole responsibility of the caller to
* ensure they aren't e.g. requesting dynamic HTML for a static page.
*
* 3.) If the request is in draft mode, we must generate dynamic HTML.
*
* 4.) If the request is a server action, we must generate dynamic HTML.
*
* These rules help ensure that other existing features like request caching,
* coalescing, and ISR continue working as intended.
*/ const isStaticGeneration = !renderOpts.shouldWaitOnAllReady && !renderOpts.supportsDynamicResponse && !renderOpts.isDraftMode && !renderOpts.isPossibleServerAction;
const shouldTrackFetchMetrics = !!process.env.__NEXT_DEV_SERVER || // The only times we want to track fetch metrics outside of development is
// when we are performing a static generation and we either are in debug
// mode, or tracking fetch metrics was specifically opted into.
isStaticGeneration && (!!process.env.NEXT_DEBUG_BUILD || process.env.NEXT_SSG_FETCH_METRICS === '1');
const store = {
isStaticGeneration,
page,
route: normalizeAppPath(page),
incrementalCache: // we fallback to a global incremental cache for edge-runtime locally
// so that it can access the fs cache without mocks
renderOpts.incrementalCache || globalThis.__incrementalCache,
cacheLifeProfiles: renderOpts.cacheLifeProfiles,
isBuildTimePrerendering: renderOpts.isBuildTimePrerendering,
fetchCache: renderOpts.fetchCache,
isOnDemandRevalidate: renderOpts.isOnDemandRevalidate,
isDraftMode: renderOpts.isDraftMode,
isPrefetchRequest,
buildId,
reactLoadableManifest: (renderOpts == null ? void 0 : renderOpts.reactLoadableManifest) || {},
assetPrefix: (renderOpts == null ? void 0 : renderOpts.assetPrefix) || '',
nonce,
afterContext: createAfterContext(renderOpts),
cacheComponentsEnabled: renderOpts.cacheComponents,
previouslyRevalidatedTags,
refreshTagsByCacheKind: createRefreshTagsByCacheKind(),
runInCleanSnapshot: createSnapshot(),
shouldTrackFetchMetrics,
reactServerErrorsByDigest: new Map()
};
// TODO: remove this when we resolve accessing the store outside the execution context
renderOpts.store = store;
return store;
}
function createAfterContext(renderOpts) {
const { waitUntil, onClose, onAfterTaskError } = renderOpts;
return new AfterContext({
waitUntil,
onClose,
onTaskError: onAfterTaskError
});
}
/**
* Creates a map with lazy results that refresh tags for the respective cache
* kind when they're awaited for the first time.
*/ function createRefreshTagsByCacheKind() {
const refreshTagsByCacheKind = new Map();
const cacheHandlers = getCacheHandlerEntries();
if (cacheHandlers) {
for (const [kind, cacheHandler] of cacheHandlers){
if ('refreshTags' in cacheHandler) {
refreshTagsByCacheKind.set(kind, createLazyResult(async ()=>cacheHandler.refreshTags()));
}
}
}
return refreshTagsByCacheKind;
}
//# sourceMappingURL=work-store.js.map
File diff suppressed because one or more lines are too long