feat(blog): add file-based blog with dynamic slugs, MDX content and layout shell

- Introduced blog routing using Next.js App Router
- Implemented dynamic [slug] pages for blog posts
- Added MDX-based content loading via lib/posts
- Integrated shared TopBar layout with navigation
- Established clear content, lib and component separation
This commit is contained in:
PascalSchattenburg
2026-01-22 14:14:15 +01:00
parent b717952234
commit d147843c76
10412 changed files with 2475583 additions and 0 deletions

View File

@@ -0,0 +1,5 @@
declare const DIMMED_STYLE = "dimmed";
declare const HIDDEN_STYLE = "hidden";
type LogStyle = typeof DIMMED_STYLE | typeof HIDDEN_STYLE;
export declare function setAbortedLogsStyle(style: LogStyle): void;
export {};

View File

@@ -0,0 +1,246 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
Object.defineProperty(exports, "setAbortedLogsStyle", {
enumerable: true,
get: function() {
return setAbortedLogsStyle;
}
});
const _picocolors = require("../../lib/picocolors");
const _consoleasyncstorageexternal = require("../app-render/console-async-storage.external");
const _workunitasyncstorageexternal = require("../app-render/work-unit-async-storage.external");
const _runtimereactsexternal = require("../runtime-reacts.external");
// eslint-disable-next-line @typescript-eslint/no-unused-vars -- we may use later and want parity with the HIDDEN_STYLE value
const DIMMED_STYLE = 'dimmed';
const HIDDEN_STYLE = 'hidden';
let currentAbortedLogsStyle = 'dimmed';
function setAbortedLogsStyle(style) {
currentAbortedLogsStyle = style;
}
const isColorSupported = (0, _picocolors.dim)('test') !== 'test';
// 50% opacity for dimmed text
const dimStyle = 'color: color(from currentColor xyz x y z / 0.5);';
const reactBadgeFormat = '\x1b[0m\x1b[7m%c%s\x1b[0m%c ';
function dimmedConsoleArgs(...inputArgs) {
if (!isColorSupported) {
return inputArgs;
}
const newArgs = inputArgs.slice(0);
let template = '';
let argumentsPointer = 0;
if (typeof inputArgs[0] === 'string') {
const originalTemplateString = inputArgs[0];
// Remove the original template string from the args.
newArgs.splice(argumentsPointer, 1);
argumentsPointer += 1;
let i = 0;
if (originalTemplateString.startsWith(reactBadgeFormat)) {
i = reactBadgeFormat.length;
// for `format` we already moved the pointer earlier
// style, badge, reset style
argumentsPointer += 3;
template += reactBadgeFormat;
// React's badge reset styles, reapply dimming
template += '\x1b[2m%c';
// argumentsPointer includes template
newArgs.splice(argumentsPointer - 1, 0, dimStyle);
// dim the badge
newArgs[0] += `;${dimStyle}`;
}
for(i; i < originalTemplateString.length; i++){
const currentChar = originalTemplateString[i];
if (currentChar !== '%') {
template += currentChar;
continue;
}
const nextChar = originalTemplateString[i + 1];
++i;
switch(nextChar){
case 'f':
case 'O':
case 'o':
case 'd':
case 's':
case 'i':
case 'c':
++argumentsPointer;
template += `%${nextChar}`;
break;
default:
template += `%${nextChar}`;
}
}
}
for(argumentsPointer; argumentsPointer < inputArgs.length; ++argumentsPointer){
const arg = inputArgs[argumentsPointer];
const argType = typeof arg;
if (argumentsPointer > 0) {
template += ' ';
}
switch(argType){
case 'boolean':
case 'string':
template += '%s';
break;
case 'bigint':
template += '%s';
break;
case 'number':
if (arg % 0) {
template += '%f';
} else {
template += '%d';
}
break;
case 'object':
template += '%O';
break;
case 'symbol':
case 'undefined':
case 'function':
template += '%s';
break;
default:
// deopt to string for new, unknown types
template += '%s';
}
}
template += '\x1b[22m';
return [
(0, _picocolors.dim)(`%c${template}`),
dimStyle,
...newArgs
];
}
function convertToDimmedArgs(methodName, args) {
switch(methodName){
case 'dir':
case 'dirxml':
case 'group':
case 'groupCollapsed':
case 'groupEnd':
case 'table':
{
// These methods cannot be colorized because they don't take a formatting string.
return args;
}
case 'assert':
{
// assert takes formatting options as the second argument.
return [
args[0]
].concat(...dimmedConsoleArgs(args[1], ...args.slice(2)));
}
case 'error':
case 'debug':
case 'info':
case 'log':
case 'trace':
case 'warn':
return dimmedConsoleArgs(args[0], ...args.slice(1));
default:
return methodName;
}
}
// Based on https://github.com/facebook/react/blob/28dc0776be2e1370fe217549d32aee2519f0cf05/packages/react-server/src/ReactFlightServer.js#L248
function patchConsoleMethod(methodName) {
const descriptor = Object.getOwnPropertyDescriptor(console, methodName);
if (descriptor && (descriptor.configurable || descriptor.writable) && typeof descriptor.value === 'function') {
const originalMethod = descriptor.value;
const originalName = Object.getOwnPropertyDescriptor(originalMethod, 'name');
const wrapperMethod = function(...args) {
var _getClientReact, _getServerReact;
const consoleStore = _consoleasyncstorageexternal.consoleAsyncStorage.getStore();
// First we see if there is a cache signal for our current scope. If we're in a client render it'll
// come from the client React cacheSignal implementation. If we are in a server render it'll come from
// the server React cacheSignal implementation. Any particular console call will be in one, the other, or neither
// scope and these signals return null if you are out of scope so this can be called from a single global patch
// and still work properly.
const signal = ((_getClientReact = (0, _runtimereactsexternal.getClientReact)()) == null ? void 0 : _getClientReact.cacheSignal()) ?? ((_getServerReact = (0, _runtimereactsexternal.getServerReact)()) == null ? void 0 : _getServerReact.cacheSignal());
if (signal) {
// We are in a React Server render and can consult the React cache signal to determine if logs
// are now dimmable.
if (signal.aborted) {
if (currentAbortedLogsStyle === HIDDEN_STYLE) {
return;
}
return applyWithDimming.call(this, consoleStore, originalMethod, methodName, args);
} else if ((consoleStore == null ? void 0 : consoleStore.dim) === true) {
return applyWithDimming.call(this, consoleStore, originalMethod, methodName, args);
} else {
return originalMethod.apply(this, args);
}
}
// We need to fall back to checking the work unit store for two reasons.
// 1. Client React does not yet implement cacheSignal (it always returns null)
// 2. route.ts files aren't rendered with React but do have prerender semantics
// TODO in the future we should be able to remove this once there is a runnable cache
// scope independent of actual React rendering.
const workUnitStore = _workunitasyncstorageexternal.workUnitAsyncStorage.getStore();
switch(workUnitStore == null ? void 0 : workUnitStore.type){
case 'prerender':
case 'prerender-runtime':
// These can be hit in a route handler. In the future we can use potential React.createCache API
// to create a cache scope for arbitrary computation and can move over to cacheSignal exclusively.
// fallthrough
case 'prerender-client':
// This is a react-dom/server render and won't have a cacheSignal until React adds this for the client world.
const renderSignal = workUnitStore.renderSignal;
if (renderSignal.aborted) {
if (currentAbortedLogsStyle === HIDDEN_STYLE) {
return;
}
return applyWithDimming.call(this, consoleStore, originalMethod, methodName, args);
}
// intentional fallthrough
case 'prerender-legacy':
case 'prerender-ppr':
case 'cache':
case 'unstable-cache':
case 'private-cache':
case 'request':
case undefined:
if ((consoleStore == null ? void 0 : consoleStore.dim) === true) {
return applyWithDimming.call(this, consoleStore, originalMethod, methodName, args);
} else {
return originalMethod.apply(this, args);
}
default:
workUnitStore;
}
};
if (originalName) {
Object.defineProperty(wrapperMethod, 'name', originalName);
}
Object.defineProperty(console, methodName, {
value: wrapperMethod
});
}
}
function applyWithDimming(consoleStore, method, methodName, args) {
if ((consoleStore == null ? void 0 : consoleStore.dim) === true) {
return method.apply(this, convertToDimmedArgs(methodName, args));
} else {
return _consoleasyncstorageexternal.consoleAsyncStorage.run(DIMMED_STORE, method.bind(this, ...convertToDimmedArgs(methodName, args)));
}
}
const DIMMED_STORE = {
dim: true
};
patchConsoleMethod('error');
patchConsoleMethod('assert');
patchConsoleMethod('debug');
patchConsoleMethod('dir');
patchConsoleMethod('dirxml');
patchConsoleMethod('group');
patchConsoleMethod('groupCollapsed');
patchConsoleMethod('groupEnd');
patchConsoleMethod('info');
patchConsoleMethod('log');
patchConsoleMethod('table');
patchConsoleMethod('trace');
patchConsoleMethod('warn');
//# sourceMappingURL=console-dim.external.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,12 @@
/**
* Patches console methods to exit the workUnitAsyncStorage so that inside the host implementation
* sync IO can be called. This is relevant for example with runtimes that patch console methods to
* prepend a timestamp to the log output.
*
* Note that this will only exit for already installed patched console methods. If you further patch
* the console method after this and add any sync IO there it will trigger sync IO warnings while prerendering.
*
* This is a pragmatic concession because layering the patches if you install your own log implementation
* after they are installed is very tricky to do correctly because the order matters
*/
export {};

View File

@@ -0,0 +1,49 @@
/**
* Patches console methods to exit the workUnitAsyncStorage so that inside the host implementation
* sync IO can be called. This is relevant for example with runtimes that patch console methods to
* prepend a timestamp to the log output.
*
* Note that this will only exit for already installed patched console methods. If you further patch
* the console method after this and add any sync IO there it will trigger sync IO warnings while prerendering.
*
* This is a pragmatic concession because layering the patches if you install your own log implementation
* after they are installed is very tricky to do correctly because the order matters
*/ "use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
const _workunitasyncstorageexternal = require("../app-render/work-unit-async-storage.external");
function patchConsoleMethod(methodName) {
const descriptor = Object.getOwnPropertyDescriptor(console, methodName);
if (descriptor && (descriptor.configurable || descriptor.writable) && typeof descriptor.value === 'function') {
const originalMethod = descriptor.value;
const originalName = Object.getOwnPropertyDescriptor(originalMethod, 'name');
let wrapperMethod = function(...args) {
return _workunitasyncstorageexternal.workUnitAsyncStorage.exit(()=>originalMethod.apply(console, args));
};
if (originalName) {
Object.defineProperty(wrapperMethod, 'name', originalName);
}
Object.defineProperty(console, methodName, {
value: wrapperMethod
});
}
}
// We patch the same methods that React and our dev patch do.
// We may find other methods that could benefit from patching but if
// they exist we ought to consider patching them in all three places
patchConsoleMethod('error');
patchConsoleMethod('assert');
patchConsoleMethod('debug');
patchConsoleMethod('dir');
patchConsoleMethod('dirxml');
patchConsoleMethod('group');
patchConsoleMethod('groupCollapsed');
patchConsoleMethod('groupEnd');
patchConsoleMethod('info');
patchConsoleMethod('log');
patchConsoleMethod('table');
patchConsoleMethod('trace');
patchConsoleMethod('warn');
//# sourceMappingURL=console-exit.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../src/server/node-environment-extensions/console-exit.tsx"],"sourcesContent":["/**\n * Patches console methods to exit the workUnitAsyncStorage so that inside the host implementation\n * sync IO can be called. This is relevant for example with runtimes that patch console methods to\n * prepend a timestamp to the log output.\n *\n * Note that this will only exit for already installed patched console methods. If you further patch\n * the console method after this and add any sync IO there it will trigger sync IO warnings while prerendering.\n *\n * This is a pragmatic concession because layering the patches if you install your own log implementation\n * after they are installed is very tricky to do correctly because the order matters\n */\n\nimport { workUnitAsyncStorage } from '../app-render/work-unit-async-storage.external'\n\ntype ConsoleMethodName = keyof Console\n\nfunction patchConsoleMethod(methodName: ConsoleMethodName): void {\n const descriptor = Object.getOwnPropertyDescriptor(console, methodName)\n if (\n descriptor &&\n (descriptor.configurable || descriptor.writable) &&\n typeof descriptor.value === 'function'\n ) {\n const originalMethod = descriptor.value\n const originalName = Object.getOwnPropertyDescriptor(originalMethod, 'name')\n let wrapperMethod = function (...args: any[]) {\n return workUnitAsyncStorage.exit(() =>\n originalMethod.apply(console, args)\n )\n }\n if (originalName) {\n Object.defineProperty(wrapperMethod, 'name', originalName)\n }\n Object.defineProperty(console, methodName, {\n value: wrapperMethod,\n })\n }\n}\n\n// We patch the same methods that React and our dev patch do.\n// We may find other methods that could benefit from patching but if\n// they exist we ought to consider patching them in all three places\npatchConsoleMethod('error')\npatchConsoleMethod('assert')\npatchConsoleMethod('debug')\npatchConsoleMethod('dir')\npatchConsoleMethod('dirxml')\npatchConsoleMethod('group')\npatchConsoleMethod('groupCollapsed')\npatchConsoleMethod('groupEnd')\npatchConsoleMethod('info')\npatchConsoleMethod('log')\npatchConsoleMethod('table')\npatchConsoleMethod('trace')\npatchConsoleMethod('warn')\n"],"names":["patchConsoleMethod","methodName","descriptor","Object","getOwnPropertyDescriptor","console","configurable","writable","value","originalMethod","originalName","wrapperMethod","args","workUnitAsyncStorage","exit","apply","defineProperty"],"mappings":"AAAA;;;;;;;;;;CAUC;;;;8CAEoC;AAIrC,SAASA,mBAAmBC,UAA6B;IACvD,MAAMC,aAAaC,OAAOC,wBAAwB,CAACC,SAASJ;IAC5D,IACEC,cACCA,CAAAA,WAAWI,YAAY,IAAIJ,WAAWK,QAAQ,AAAD,KAC9C,OAAOL,WAAWM,KAAK,KAAK,YAC5B;QACA,MAAMC,iBAAiBP,WAAWM,KAAK;QACvC,MAAME,eAAeP,OAAOC,wBAAwB,CAACK,gBAAgB;QACrE,IAAIE,gBAAgB,SAAU,GAAGC,IAAW;YAC1C,OAAOC,kDAAoB,CAACC,IAAI,CAAC,IAC/BL,eAAeM,KAAK,CAACV,SAASO;QAElC;QACA,IAAIF,cAAc;YAChBP,OAAOa,cAAc,CAACL,eAAe,QAAQD;QAC/C;QACAP,OAAOa,cAAc,CAACX,SAASJ,YAAY;YACzCO,OAAOG;QACT;IACF;AACF;AAEA,6DAA6D;AAC7D,oEAAoE;AACpE,oEAAoE;AACpEX,mBAAmB;AACnBA,mBAAmB;AACnBA,mBAAmB;AACnBA,mBAAmB;AACnBA,mBAAmB;AACnBA,mBAAmB;AACnBA,mBAAmB;AACnBA,mBAAmB;AACnBA,mBAAmB;AACnBA,mBAAmB;AACnBA,mBAAmB;AACnBA,mBAAmB;AACnBA,mBAAmB","ignoreList":[0]}

View File

@@ -0,0 +1 @@
export {};

View File

@@ -0,0 +1,56 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
const _consoleasyncstorageexternal = require("../app-render/console-async-storage.external");
const _filelogger = require("../dev/browser-logs/file-logger");
const _console = require("../../client/lib/console");
// Based on https://github.com/facebook/react/blob/28dc0776be2e1370fe217549d32aee2519f0cf05/packages/react-server/src/ReactFlightServer.js#L248
function patchConsoleMethodDEV(methodName) {
const descriptor = Object.getOwnPropertyDescriptor(console, methodName);
if (descriptor && (descriptor.configurable || descriptor.writable) && typeof descriptor.value === 'function') {
const originalMethod = descriptor.value;
const originalName = Object.getOwnPropertyDescriptor(originalMethod, 'name');
const wrapperMethod = function(...args) {
const consoleStore = _consoleasyncstorageexternal.consoleAsyncStorage.getStore();
if ((consoleStore == null ? void 0 : consoleStore.dim) === true) {
// In this context the log args are already dimmed. We use the console store
// to decide if this log is ignorable for reporting in our file logger.
return originalMethod.apply(this, args);
} else {
const ret = originalMethod.apply(this, args);
const fileLogger = (0, _filelogger.getFileLogger)();
const message = (0, _console.formatConsoleArgs)(args);
// Strip ANSI escape codes for file logging
// eslint-disable-next-line no-control-regex
const ansiEscapeRegex = new RegExp('\u001b\\[[0-9;]*m', 'g');
const cleanMessage = message.replace(ansiEscapeRegex, '');
fileLogger.logServer(methodName.toUpperCase(), cleanMessage);
return ret;
}
};
if (originalName) {
Object.defineProperty(wrapperMethod, 'name', originalName);
}
Object.defineProperty(console, methodName, {
value: wrapperMethod
});
}
}
if (process.env.NODE_ENV === 'development') {
patchConsoleMethodDEV('error');
patchConsoleMethodDEV('assert');
patchConsoleMethodDEV('debug');
patchConsoleMethodDEV('dir');
patchConsoleMethodDEV('dirxml');
patchConsoleMethodDEV('group');
patchConsoleMethodDEV('groupCollapsed');
patchConsoleMethodDEV('groupEnd');
patchConsoleMethodDEV('info');
patchConsoleMethodDEV('log');
patchConsoleMethodDEV('table');
patchConsoleMethodDEV('trace');
patchConsoleMethodDEV('warn');
}
//# sourceMappingURL=console-file.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../src/server/node-environment-extensions/console-file.tsx"],"sourcesContent":["import { consoleAsyncStorage } from '../app-render/console-async-storage.external'\nimport { getFileLogger } from '../dev/browser-logs/file-logger'\nimport { formatConsoleArgs } from '../../client/lib/console'\n\ntype InterceptableConsoleMethod =\n | 'error'\n | 'assert'\n | 'debug'\n | 'dir'\n | 'dirxml'\n | 'group'\n | 'groupCollapsed'\n | 'groupEnd'\n | 'info'\n | 'log'\n | 'table'\n | 'trace'\n | 'warn'\n\n// Based on https://github.com/facebook/react/blob/28dc0776be2e1370fe217549d32aee2519f0cf05/packages/react-server/src/ReactFlightServer.js#L248\nfunction patchConsoleMethodDEV(methodName: InterceptableConsoleMethod): void {\n const descriptor = Object.getOwnPropertyDescriptor(console, methodName)\n if (\n descriptor &&\n (descriptor.configurable || descriptor.writable) &&\n typeof descriptor.value === 'function'\n ) {\n const originalMethod = descriptor.value\n const originalName = Object.getOwnPropertyDescriptor(originalMethod, 'name')\n const wrapperMethod = function (this: typeof console, ...args: any[]) {\n const consoleStore = consoleAsyncStorage.getStore()\n\n if (consoleStore?.dim === true) {\n // In this context the log args are already dimmed. We use the console store\n // to decide if this log is ignorable for reporting in our file logger.\n return originalMethod.apply(this, args)\n } else {\n const ret = originalMethod.apply(this, args)\n\n const fileLogger = getFileLogger()\n const message = formatConsoleArgs(args)\n // Strip ANSI escape codes for file logging\n // eslint-disable-next-line no-control-regex\n const ansiEscapeRegex = new RegExp('\\u001b\\\\[[0-9;]*m', 'g')\n const cleanMessage = message.replace(ansiEscapeRegex, '')\n fileLogger.logServer(methodName.toUpperCase(), cleanMessage)\n return ret\n }\n }\n if (originalName) {\n Object.defineProperty(wrapperMethod, 'name', originalName)\n }\n Object.defineProperty(console, methodName, {\n value: wrapperMethod,\n })\n }\n}\n\nif (process.env.NODE_ENV === 'development') {\n patchConsoleMethodDEV('error')\n patchConsoleMethodDEV('assert')\n patchConsoleMethodDEV('debug')\n patchConsoleMethodDEV('dir')\n patchConsoleMethodDEV('dirxml')\n patchConsoleMethodDEV('group')\n patchConsoleMethodDEV('groupCollapsed')\n patchConsoleMethodDEV('groupEnd')\n patchConsoleMethodDEV('info')\n patchConsoleMethodDEV('log')\n patchConsoleMethodDEV('table')\n patchConsoleMethodDEV('trace')\n patchConsoleMethodDEV('warn')\n}\n"],"names":["patchConsoleMethodDEV","methodName","descriptor","Object","getOwnPropertyDescriptor","console","configurable","writable","value","originalMethod","originalName","wrapperMethod","args","consoleStore","consoleAsyncStorage","getStore","dim","apply","ret","fileLogger","getFileLogger","message","formatConsoleArgs","ansiEscapeRegex","RegExp","cleanMessage","replace","logServer","toUpperCase","defineProperty","process","env","NODE_ENV"],"mappings":";;;;6CAAoC;4BACN;yBACI;AAiBlC,+IAA+I;AAC/I,SAASA,sBAAsBC,UAAsC;IACnE,MAAMC,aAAaC,OAAOC,wBAAwB,CAACC,SAASJ;IAC5D,IACEC,cACCA,CAAAA,WAAWI,YAAY,IAAIJ,WAAWK,QAAQ,AAAD,KAC9C,OAAOL,WAAWM,KAAK,KAAK,YAC5B;QACA,MAAMC,iBAAiBP,WAAWM,KAAK;QACvC,MAAME,eAAeP,OAAOC,wBAAwB,CAACK,gBAAgB;QACrE,MAAME,gBAAgB,SAAgC,GAAGC,IAAW;YAClE,MAAMC,eAAeC,gDAAmB,CAACC,QAAQ;YAEjD,IAAIF,CAAAA,gCAAAA,aAAcG,GAAG,MAAK,MAAM;gBAC9B,4EAA4E;gBAC5E,uEAAuE;gBACvE,OAAOP,eAAeQ,KAAK,CAAC,IAAI,EAAEL;YACpC,OAAO;gBACL,MAAMM,MAAMT,eAAeQ,KAAK,CAAC,IAAI,EAAEL;gBAEvC,MAAMO,aAAaC,IAAAA,yBAAa;gBAChC,MAAMC,UAAUC,IAAAA,0BAAiB,EAACV;gBAClC,2CAA2C;gBAC3C,4CAA4C;gBAC5C,MAAMW,kBAAkB,IAAIC,OAAO,qBAAqB;gBACxD,MAAMC,eAAeJ,QAAQK,OAAO,CAACH,iBAAiB;gBACtDJ,WAAWQ,SAAS,CAAC1B,WAAW2B,WAAW,IAAIH;gBAC/C,OAAOP;YACT;QACF;QACA,IAAIR,cAAc;YAChBP,OAAO0B,cAAc,CAAClB,eAAe,QAAQD;QAC/C;QACAP,OAAO0B,cAAc,CAACxB,SAASJ,YAAY;YACzCO,OAAOG;QACT;IACF;AACF;AAEA,IAAImB,QAAQC,GAAG,CAACC,QAAQ,KAAK,eAAe;IAC1ChC,sBAAsB;IACtBA,sBAAsB;IACtBA,sBAAsB;IACtBA,sBAAsB;IACtBA,sBAAsB;IACtBA,sBAAsB;IACtBA,sBAAsB;IACtBA,sBAAsB;IACtBA,sBAAsB;IACtBA,sBAAsB;IACtBA,sBAAsB;IACtBA,sBAAsB;IACtBA,sBAAsB;AACxB","ignoreList":[0]}

View File

@@ -0,0 +1 @@
export {};

View File

@@ -0,0 +1,52 @@
/**
* We extend `Date` during builds and revalidates to ensure that prerenders don't observe the clock as a source of IO
* When cacheComponents is enabled. The current time is a form of IO even though it resolves synchronously. When cacheComponents is
* enabled we need to ensure that clock time is excluded from prerenders unless it is cached.
*
* There is tension here because time is used for both output and introspection. While arbitrary we intend to reserve
* `Date` for output use cases and `performance` for introspection use cases. If you want to measure
* how long something takes use `performance.timeOrigin` and `performance.now()` rather than `Date.now()` for instance.
*
* The extensions here never error nor alter the underlying Date objects, strings, and numbers created and thus should be transparent to callers.
*/ "use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
const _utils = require("./utils");
function createNow(originalNow) {
return ({
now: function now() {
(0, _utils.io)('`Date.now()`', 'time');
return originalNow();
}
})['now'.slice()].bind(null);
}
function createDate(originalConstructor) {
const properties = Object.getOwnPropertyDescriptors(originalConstructor);
properties.now.value = createNow(originalConstructor.now);
const apply = Reflect.apply;
const construct = Reflect.construct;
const newConstructor = Object.defineProperties(// Ideally this should not minify the name.
function Date1() {
if (new.target === undefined) {
(0, _utils.io)('`Date()`', 'time');
return apply(originalConstructor, undefined, arguments);
}
if (arguments.length === 0) {
(0, _utils.io)('`new Date()`', 'time');
}
return construct(originalConstructor, arguments, new.target);
}, properties);
Object.defineProperty(originalConstructor.prototype, 'constructor', {
value: newConstructor
});
return newConstructor;
}
try {
// eslint-disable-next-line no-native-reassign
Date = createDate(Date);
} catch {
console.error('Failed to install `Date` class extension. When using `cacheComponents`, APIs that read the current time will not correctly trigger dynamic behavior.');
}
//# sourceMappingURL=date.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../src/server/node-environment-extensions/date.tsx"],"sourcesContent":["/**\n * We extend `Date` during builds and revalidates to ensure that prerenders don't observe the clock as a source of IO\n * When cacheComponents is enabled. The current time is a form of IO even though it resolves synchronously. When cacheComponents is\n * enabled we need to ensure that clock time is excluded from prerenders unless it is cached.\n *\n * There is tension here because time is used for both output and introspection. While arbitrary we intend to reserve\n * `Date` for output use cases and `performance` for introspection use cases. If you want to measure\n * how long something takes use `performance.timeOrigin` and `performance.now()` rather than `Date.now()` for instance.\n *\n * The extensions here never error nor alter the underlying Date objects, strings, and numbers created and thus should be transparent to callers.\n */\nimport { io } from './utils'\n\nfunction createNow(originalNow: typeof Date.now) {\n return {\n now: function now() {\n io('`Date.now()`', 'time')\n return originalNow()\n },\n }['now'.slice() as 'now'].bind(null)\n}\n\nfunction createDate(originalConstructor: typeof Date): typeof Date {\n const properties = Object.getOwnPropertyDescriptors(originalConstructor)\n properties.now.value = createNow(originalConstructor.now)\n\n const apply = Reflect.apply\n const construct = Reflect.construct\n\n const newConstructor = Object.defineProperties(\n // Ideally this should not minify the name.\n function Date() {\n if (new.target === undefined) {\n io('`Date()`', 'time')\n return apply(originalConstructor, undefined, arguments)\n }\n if (arguments.length === 0) {\n io('`new Date()`', 'time')\n }\n return construct(originalConstructor, arguments, new.target)\n },\n properties\n )\n Object.defineProperty(originalConstructor.prototype, 'constructor', {\n value: newConstructor,\n })\n return newConstructor as typeof Date\n}\n\ntry {\n // eslint-disable-next-line no-native-reassign\n Date = createDate(Date)\n} catch {\n console.error(\n 'Failed to install `Date` class extension. When using `cacheComponents`, APIs that read the current time will not correctly trigger dynamic behavior.'\n )\n}\n"],"names":["createNow","originalNow","now","io","slice","bind","createDate","originalConstructor","properties","Object","getOwnPropertyDescriptors","value","apply","Reflect","construct","newConstructor","defineProperties","Date","undefined","arguments","length","defineProperty","prototype","console","error"],"mappings":"AAAA;;;;;;;;;;CAUC;;;;uBACkB;AAEnB,SAASA,UAAUC,WAA4B;IAC7C,OAAO,CAAA;QACLC,KAAK,SAASA;YACZC,IAAAA,SAAE,EAAC,gBAAgB;YACnB,OAAOF;QACT;IACF,CAAA,CAAC,CAAC,MAAMG,KAAK,GAAY,CAACC,IAAI,CAAC;AACjC;AAEA,SAASC,WAAWC,mBAAgC;IAClD,MAAMC,aAAaC,OAAOC,yBAAyB,CAACH;IACpDC,WAAWN,GAAG,CAACS,KAAK,GAAGX,UAAUO,oBAAoBL,GAAG;IAExD,MAAMU,QAAQC,QAAQD,KAAK;IAC3B,MAAME,YAAYD,QAAQC,SAAS;IAEnC,MAAMC,iBAAiBN,OAAOO,gBAAgB,CAC5C,2CAA2C;IAC3C,SAASC;QACP,IAAI,eAAeC,WAAW;YAC5Bf,IAAAA,SAAE,EAAC,YAAY;YACf,OAAOS,MAAML,qBAAqBW,WAAWC;QAC/C;QACA,IAAIA,UAAUC,MAAM,KAAK,GAAG;YAC1BjB,IAAAA,SAAE,EAAC,gBAAgB;QACrB;QACA,OAAOW,UAAUP,qBAAqBY,WAAW;IACnD,GACAX;IAEFC,OAAOY,cAAc,CAACd,oBAAoBe,SAAS,EAAE,eAAe;QAClEX,OAAOI;IACT;IACA,OAAOA;AACT;AAEA,IAAI;IACF,8CAA8C;IAC9CE,OAAOX,WAAWW;AACpB,EAAE,OAAM;IACNM,QAAQC,KAAK,CACX;AAEJ","ignoreList":[0]}

View File

@@ -0,0 +1 @@
export {};

View File

@@ -0,0 +1,8 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
const _patcherrorinspect = require("../patch-error-inspect");
(0, _patcherrorinspect.patchErrorInspectNodeJS)(globalThis.Error);
//# sourceMappingURL=error-inspect.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../src/server/node-environment-extensions/error-inspect.tsx"],"sourcesContent":["import { patchErrorInspectNodeJS } from '../patch-error-inspect'\n\npatchErrorInspectNodeJS(globalThis.Error)\n"],"names":["patchErrorInspectNodeJS","globalThis","Error"],"mappings":";;;;mCAAwC;AAExCA,IAAAA,0CAAuB,EAACC,WAAWC,KAAK","ignoreList":[0]}

View File

@@ -0,0 +1,73 @@
declare const originalSetImmediate: typeof setImmediate;
export { originalSetImmediate as unpatchedSetImmediate };
/**
* **WARNING: This function changes the usual behavior of the event loop!**
* **Be VERY careful about where you call it.**
*
* Starts capturing calls to `setImmediate` to run them as "fast immediates".
* All calls captured in this way will be executed after the current task
* (after callbacks from `process.nextTick()`, microtasks, and nextTicks scheduled from microtasks).
* This function needs to be called again in each task that needs the
* "fast immediates" behavior.
*
* ### Motivation
*
* We don't want `setImmediate` to be considered IO in Cache Components.
* To achieve this in a staged (pre)render, we want to allow immediates scheduled
* in stage N to run before stage N+1.
* Since we schedule stages using sequential `setTimeout`, this isn't possible without
* intercepting `setImmediate` and doing the scheduling on our own.
* We refer to this as a "fast immediate".
*
* Notably, this affects React's `scheduleWork` in render, which uses `setImmediate`.
* This is desirable -- if async work was scheduled during a stage, then it should
* get to run before we finish that stage.
*
* ### Example
*
* ```ts
* setTimeout(() => {
* runPendingImmediatesAfterCurrentTask()
* console.log("timeout 1")
* setImmediate(() => {
* console.log("immediate!!!")
* })
* })
* setTimeout(() => {
* console.log("timeout 2")
* })
* ```
* will print
*
* ```
* timeout 1
* immediate!!!
* timeout 2
* ```
*
* instead of the usual order
* ```
* timeout 1
* timeout 2
* immediate!!!
* ```
* > **NOTE**
* > The above is *most common* order, but it's not guaranteed.
* > Under some circumstances (e.g. when the event loop is blocked on CPU work),
* > Node will reorder things and run the immediate before timeout 2.
* > So, in a sense, we're just making this reordering happen consistently.
*
* Recursive `setImmediate` calls will also be executed as "fast immediates".
* If multiple immediates were scheduled, `process.nextTick()` (and associated microtasks)
* will be allowed to execute between them.
* See the unit tests for more examples.
* */
export declare function DANGEROUSLY_runPendingImmediatesAfterCurrentTask(): void;
/**
* This should always be called a task after `DANGEROUSLY_runPendingImmediatesAfterCurrentTask`
* to make sure that everything executed as expected and we're not left in an inconsistent state.
* Ideally, this wouldn't be necessary, but we're not in control of the event loop
* and need to guard against unexpected behaviors not forseen in this implementation,
* so we have to be defensive.
*/
export declare function expectNoPendingImmediates(): void;

View File

@@ -0,0 +1,570 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
0 && (module.exports = {
DANGEROUSLY_runPendingImmediatesAfterCurrentTask: null,
expectNoPendingImmediates: null,
unpatchedSetImmediate: null
});
function _export(target, all) {
for(var name in all)Object.defineProperty(target, name, {
enumerable: true,
get: all[name]
});
}
_export(exports, {
DANGEROUSLY_runPendingImmediatesAfterCurrentTask: function() {
return DANGEROUSLY_runPendingImmediatesAfterCurrentTask;
},
expectNoPendingImmediates: function() {
return expectNoPendingImmediates;
},
unpatchedSetImmediate: function() {
return originalSetImmediate;
}
});
const _nodeutil = require("node:util");
const _invarianterror = require("../../shared/lib/invariant-error");
const _asynclocalstorage = require("../app-render/async-local-storage");
var ExecutionState = /*#__PURE__*/ function(ExecutionState) {
ExecutionState[ExecutionState["Waiting"] = 1] = "Waiting";
ExecutionState[ExecutionState["Working"] = 2] = "Working";
ExecutionState[ExecutionState["Finished"] = 3] = "Finished";
ExecutionState[ExecutionState["Abandoned"] = 4] = "Abandoned";
return ExecutionState;
}(ExecutionState || {});
let wasEnabledAtLeastOnce = false;
let pendingNextTicks = 0;
let currentExecution = null;
const originalSetImmediate = globalThis.setImmediate;
const originalClearImmediate = globalThis.clearImmediate;
const originalNextTick = process.nextTick;
function install() {
if (process.env.NEXT_RUNTIME === 'edge') {
// Nothing to patch. The exported functions all error if used in the edge runtime,
// so we're not going to violate any assumptions by not patching.
return;
} else {
debug == null ? void 0 : debug('installing fast setImmediate patch');
const nodeTimers = require('node:timers');
globalThis.setImmediate = nodeTimers.setImmediate = // Workaround for missing __promisify__ which is not a real property
patchedSetImmediate;
globalThis.clearImmediate = nodeTimers.clearImmediate = patchedClearImmediate;
const nodeTimersPromises = require('node:timers/promises');
nodeTimersPromises.setImmediate = patchedSetImmediatePromise;
process.nextTick = patchedNextTick;
}
}
function DANGEROUSLY_runPendingImmediatesAfterCurrentTask() {
if (process.env.NEXT_RUNTIME === 'edge') {
throw Object.defineProperty(new _invarianterror.InvariantError('DANGEROUSLY_runPendingImmediatesAfterCurrentTask cannot be called in the edge runtime'), "__NEXT_ERROR_CODE", {
value: "E960",
enumerable: false,
configurable: true
});
} else {
const execution = startCapturingImmediates();
try {
scheduleWorkAfterNextTicksAndMicrotasks(execution);
} catch (err) {
// If this error comes from a bail() call, rethrow it.
if (execution.state === 4) {
throw err;
}
// Otherwise, bail out here.
bail(execution, Object.defineProperty(new _invarianterror.InvariantError('An unexpected error occurred while starting to capture immediates', {
cause: err
}), "__NEXT_ERROR_CODE", {
value: "E964",
enumerable: false,
configurable: true
}));
}
}
}
function expectNoPendingImmediates() {
if (process.env.NEXT_RUNTIME === 'edge') {
throw Object.defineProperty(new _invarianterror.InvariantError('expectNoPendingImmediates cannot be called in the edge runtime'), "__NEXT_ERROR_CODE", {
value: "E962",
enumerable: false,
configurable: true
});
} else {
if (currentExecution !== null) {
bail(currentExecution, Object.defineProperty(new _invarianterror.InvariantError(`Expected all captured immediates to have been executed (state: ${ExecutionState[currentExecution.state]})`), "__NEXT_ERROR_CODE", {
value: "E958",
enumerable: false,
configurable: true
}));
}
}
}
/**
* Wait until all nextTicks and microtasks spawned from the current task are done,
* then execute any immediates that they queued.
* */ function scheduleWorkAfterNextTicksAndMicrotasks(execution) {
if (execution.state !== 1) {
throw Object.defineProperty(new _invarianterror.InvariantError(`scheduleWorkAfterTicksAndMicrotasks can only be called while waiting (state: ${ExecutionState[execution.state]})`), "__NEXT_ERROR_CODE", {
value: "E959",
enumerable: false,
configurable: true
});
}
// We want to execute "fast immediates" after all the nextTicks and microtasks
// spawned from the current task are done.
// The ordering here is:
//
// 1. sync code
// 2. process.nextTick (scheduled from sync code, or from one of these nextTicks)
// 3. microtasks
// 4. process.nextTick (scheduled from microtasks, e.g. `queueMicrotask(() => process.nextTick(callback))`)
//
// We want to run to run in step 4, because that's the latest point before the next tick.
// However, there might also be other callbacks scheduled to run in that step.
// But importantly, they had to be scheduled using a `process.nextTick`,
// so we can detect them by checking if `pendingNextTicks > 0`.
// In that case, we'll just reschedule ourselves in the same way again to let them run first.
// (this process can theoretically repeat multiple times, hence the recursion).
queueMicrotask(()=>{
// (note that this call won't increment `pendingNextTicks`,
// only the patched `process.nextTick` does that, so this won't loop infinitely)
originalNextTick(()=>{
// We're now in a nextTick, which means that we're executing inside `processTicksAndRejections`:
// https://github.com/nodejs/node/blob/d546e7fd0bc3cbb4bcc2baae6f3aa44d2e81a413/lib/internal/process/task_queues.js#L84
// All the work scheduled here will happen within that `processTicksAndRejections` loop.
// Reading the source of `processTicksAndRejections` can help understand the timing here --
// All we're really doing is strategically pushing callbacks into the two queues
// (nextTicks and microtasks) that that function is currently looping over.
try {
if (execution.state === 4 || currentExecution !== execution) {
debug == null ? void 0 : debug(`scheduler :: the execution was abandoned`);
return;
}
if (pendingNextTicks > 0) {
// Other nextTicks have been scheduled. Let those run first, then try again --
// we're simulating a event loop task, so all nextTicks should be exhausted before we execute.
debug == null ? void 0 : debug(`scheduler :: yielding to ${pendingNextTicks} nextTicks`);
return scheduleWorkAfterNextTicksAndMicrotasks(execution);
}
// There's no other nextTicks, we're the last one, so we're about to move on to the next task (likely a timer).
// Now, we can try and execute any queued immediates.
return performWork(execution);
} catch (err) {
// If this error comes from a bail() call, rethrow it.
// typescript can't tell that the state might've been mutated
// and the narrowing from above is no longer valid
const executionAfterWork = execution;
if (executionAfterWork.state === 4) {
throw err;
}
// Otherwise, bail out here (which will trigger an uncaught exception)
// Note that we're using the same microtask trick as `safelyRunNextTickCallback`.
queueMicrotask(()=>{
bail(execution, Object.defineProperty(new _invarianterror.InvariantError('An unexpected error occurred while executing immediates', {
cause: err
}), "__NEXT_ERROR_CODE", {
value: "E955",
enumerable: false,
configurable: true
}));
});
}
});
});
}
/** Execute one immediate, and schedule a check for more (in case there's others in the queue) */ function performWork(execution) {
if (execution.state === 4) {
return;
}
debug == null ? void 0 : debug(`scheduler :: performing work`);
if (execution.state !== 1) {
throw Object.defineProperty(new _invarianterror.InvariantError(`performWork can only be called while waiting (state: ${ExecutionState[execution.state]})`), "__NEXT_ERROR_CODE", {
value: "E956",
enumerable: false,
configurable: true
});
}
execution.state = 2;
const queueItem = takeNextActiveQueueItem(execution);
if (queueItem === null) {
debug == null ? void 0 : debug(`scheduler :: no immediates queued, exiting`);
stopCapturingImmediates(execution);
return;
}
debug == null ? void 0 : debug(`scheduler :: executing queued immediate`);
const { immediateObject, callback, args } = queueItem;
immediateObject[INTERNALS].queueItem = null;
clearQueueItem(queueItem);
// Execute the immediate.
// If a sync error was thrown in the immediate, we want to trigger a `uncaughtException`.
// However, we're executing in a nextTick, and if a nextTick callback errors,
// It'll break out of `processTicksAndRejections` (note the lack of a `catch` block):
// https://github.com/nodejs/node/blob/d546e7fd0bc3cbb4bcc2baae6f3aa44d2e81a413/lib/internal/process/task_queues.js#L81-L97
// Meaning that the event loop will stop executing nextTicks and move on to the next timer
// (or other phase of the event loop, but we expect to be running in a sequence of timers here).
// Then, the remaining ticks will run after that timer, since they're still in the queue.
//
// This would completely break the timing we're trying to achieve here --
// The point of this patch is to execute immediates before the next timer!
// So, we need to work around this behavior. (both here and in our `process.nextTick` patch).
//
// We can sidestep this by catching the synchronous error and rethrowing it in a microtask.
// (NOTE: if we use `queueMicrotask`, it'll trigger `uncaughtException`, not `unhandledRejection`,
// because there's no promise being rejected.)
//
// This will make `uncaughtException` happen:
// - Before the next fast immediate (`scheduleWorkAfterNextTicksAndMicrotasks` also uses `queueMicrotask`).
// This is good, and matches usual observable behavior of immediates.
// - AFTER nextTicks scheduled from the immediate itself.
// This deviates from native setImmediate, which would call `uncaughtException` first,
// and skip ahead to the next task as explained above.
//
// This is technically an observable difference in behavior, but it seems niche enough that
// it shouldn't cause problems -- we don't expect user code to use `uncaughtException` for control flow,
// only error reporting, so subtly changing the timing shouldn't matter.
let didThrow = false;
let thrownValue = undefined;
queueMicrotask(()=>{
if (didThrow) {
debug == null ? void 0 : debug('scheduler :: rethrowing sync error from immediate in microtask');
throw thrownValue;
}
});
try {
if (args !== null) {
callback.apply(null, args);
} else {
callback();
}
} catch (err) {
// We'll rethrow the error in the microtask above.
didThrow = true;
thrownValue = err;
}
// Schedule the loop again in case there's more immediates after this one.
// Note that we can't just check if the queue is empty now, because new immediates
// might still be scheduled asynchronously, from an upcoming nextTick or microtask.
execution.state = 1;
scheduleWorkAfterNextTicksAndMicrotasks(execution);
}
function takeNextActiveQueueItem(execution) {
// Find the first (if any) queued immediate that wasn't cleared.
// We don't remove immediates from the array when they're cleared,
// so this requires some legwork to exclude (and possibly drop) cleared items.
const { queuedImmediates } = execution;
let firstActiveItem = null;
let firstActiveItemIndex = -1;
for(let i = 0; i < queuedImmediates.length; i++){
const item = queuedImmediates[i];
if (!item.isCleared) {
firstActiveItem = item;
firstActiveItemIndex = i;
break;
}
}
if (firstActiveItem === null) {
// We didn't find an active item.
// If the queue isn't empty, then it must only contain cleared items. Empty it.
if (queuedImmediates.length > 0) {
queuedImmediates.length = 0;
}
return null;
}
// Remove all items up to and including `nextActiveItemIndex` from the queue.
// (if it's not the first item, then it must be preceded by cleared items, which we want to drop anyway)
if (firstActiveItemIndex === 0) {
// Fast path - drop the first item
// (`splice` creates a result array for the removed items, so this is more efficient)
queuedImmediates.shift();
} else {
queuedImmediates.splice(0, firstActiveItemIndex + 1);
}
return firstActiveItem;
}
function startCapturingImmediates() {
if (currentExecution !== null) {
bail(currentExecution, Object.defineProperty(new _invarianterror.InvariantError(`Cannot start capturing immediates again without finishing the previous task (state: ${ExecutionState[currentExecution.state]})`), "__NEXT_ERROR_CODE", {
value: "E954",
enumerable: false,
configurable: true
}));
}
wasEnabledAtLeastOnce = true;
const execution = {
state: 1,
queuedImmediates: []
};
currentExecution = execution;
return execution;
}
function stopCapturingImmediates(execution) {
if (execution.state === 4) {
return;
}
// This check enforces that we run performWork at least once before stopping
// to make sure that we've waited for all the nextTicks and microtasks
// that might've scheduled some immediates after sync code.
if (execution.state !== 2) {
throw Object.defineProperty(new _invarianterror.InvariantError(`Cannot stop capturing immediates before execution is finished (state: ${ExecutionState[execution.state]})`), "__NEXT_ERROR_CODE", {
value: "E957",
enumerable: false,
configurable: true
});
}
execution.state = 3;
if (currentExecution === execution) {
currentExecution = null;
}
}
function bail(execution, error) {
// Reset the state as best we can to prevent further crashes.
// Otherwise, any subsequent call to `DANGEROUSLY_runPendingImmediatesAfterCurrentTask`
// would error, requiring a server restart to fix.
if (currentExecution === execution) {
currentExecution = null;
}
execution.state = 4;
// If we have any queued immediates, schedule them with native `setImmediate` and clear the queue.
// We don't want to skip running them altogether, because that could lead to
// e.g. hanging promises (for `new Promise((resolve) => setImmediate(resolve))`),
// but we're in an inconsistent state and can't run them as fast immediates,
// so this is the next best thing.
for (const queueItem of execution.queuedImmediates){
if (queueItem.isCleared) {
continue;
}
scheduleQueuedImmediateAsNativeImmediate(queueItem);
}
execution.queuedImmediates.length = 0;
// Don't reset `pendingNextTicks` -- it will reset to 0 on its own as the nextTicks execute.
// If we set it to 0 here while we still have pending ticks, they'd decrement it below 0.
throw error;
}
function scheduleQueuedImmediateAsNativeImmediate(queueItem) {
const { callback, args, immediateObject } = queueItem;
const hasRef = immediateObject[INTERNALS].hasRef;
clearQueueItem(queueItem);
const nativeImmediate = args !== null ? originalSetImmediate(callback, ...args) : originalSetImmediate(callback);
if (!hasRef) {
nativeImmediate.unref();
}
// Make our fake immediate object proxy all relevant operations
// (clearing, ref(), unref(), hasRef()) to the actual native immediate.
proxyQueuedImmediateToNativeImmediate(immediateObject, nativeImmediate);
}
function clearQueueItem(originalQueueItem) {
const queueItem = originalQueueItem;
queueItem.isCleared = true;
queueItem.callback = null;
queueItem.args = null;
queueItem.immediateObject = null;
}
function patchedNextTick() {
if (currentExecution === null) {
return originalNextTick.apply(null, // @ts-expect-error: this is valid, but typescript doesn't get it
arguments);
}
if (arguments.length === 0 || typeof arguments[0] !== 'function') {
// Let the original nextTick error for invalid arguments
// so that we don't have to mirror the error message.
originalNextTick.apply(null, // @ts-expect-error: explicitly passing arguments that we know are invalid
arguments);
// We expect the above call to throw. If it didn't, something's broken.
bail(currentExecution, Object.defineProperty(new _invarianterror.InvariantError('Expected process.nextTick to reject invalid arguments'), "__NEXT_ERROR_CODE", {
value: "E966",
enumerable: false,
configurable: true
}));
}
debug == null ? void 0 : debug(`scheduler :: process.nextTick called (previous pending: ${pendingNextTicks})`);
const callback = arguments[0];
const args = arguments.length > 1 ? Array.prototype.slice.call(arguments, 1) : null;
pendingNextTicks += 1;
return originalNextTick(safelyRunNextTickCallback, callback, args);
}
function safelyRunNextTickCallback(callback, args) {
pendingNextTicks -= 1;
debug == null ? void 0 : debug(`scheduler :: process.nextTick executing (still pending: ${pendingNextTicks})`);
// Synchronous errors in nextTick break out of `processTicksAndRejections` and cause us
// to move on to the next timer without having executed the whole nextTick queue,
// which breaks our entire scheduling mechanism. See `performWork` for more details.
try {
if (args !== null) {
callback.apply(null, args);
} else {
callback();
}
} catch (err) {
// We want to make sure `nextTick` is cheap, so unlike `performWork`,
// we only queue the microtask if an error actually occurs.
// This (observably) changes the timing of `uncaughtException` even more,
// because it'll run after microtasks queued from the nextTick,
// but hopefully this is niche enough to not affect any real world code.
queueMicrotask(()=>{
debug == null ? void 0 : debug(`scheduler :: rethrowing sync error from nextTick in a microtask`);
throw err;
});
}
}
function patchedSetImmediate() {
if (currentExecution === null) {
return originalSetImmediate.apply(null, // @ts-expect-error: this is valid, but typescript doesn't get it
arguments);
}
if (arguments.length === 0 || typeof arguments[0] !== 'function') {
// Let the original setImmediate error for invalid arguments
// so that we don't have to mirror the error message.
originalSetImmediate.apply(null, // @ts-expect-error: explicitly passing arguments that we know are invalid
arguments);
// We expect the above call to throw. If it didn't, something's broken.
bail(currentExecution, Object.defineProperty(new _invarianterror.InvariantError('Expected setImmediate to reject invalid arguments'), "__NEXT_ERROR_CODE", {
value: "E965",
enumerable: false,
configurable: true
}));
}
const callback = arguments[0];
const args = arguments.length > 1 ? Array.prototype.slice.call(arguments, 1) : null;
// Normally, Node would capture and propagate the async context to the immediate.
// We'll be running it on our own queue, so we need to propagate it ourselves.
const callbackWithAsyncContext = (0, _asynclocalstorage.bindSnapshot)(callback);
const immediateObject = new NextImmediate();
const queueItem = {
isCleared: false,
callback: callbackWithAsyncContext,
args,
immediateObject
};
currentExecution.queuedImmediates.push(queueItem);
immediateObject[INTERNALS].queueItem = queueItem;
return immediateObject;
}
function patchedSetImmediatePromise(value, options) {
if (currentExecution === null) {
const originalPromisify = // @ts-expect-error: the types for `promisify.custom` are strange
originalSetImmediate[_nodeutil.promisify.custom];
return originalPromisify(value, options);
}
return new Promise((resolve, reject)=>{
// The abort signal makes the promise reject.
// If it is already aborted, we reject immediately.
const signal = options == null ? void 0 : options.signal;
if (signal && signal.aborted) {
return reject(signal.reason);
}
const immediate = patchedSetImmediate(resolve, value);
// Unref-ing only really has an observable effect if we bail out to a native immediate,
// but we do it for completeness
if ((options == null ? void 0 : options.ref) === false) {
immediate.unref();
}
if (signal) {
signal.addEventListener('abort', ()=>{
patchedClearImmediate(immediate);
reject(signal.reason);
}, {
once: true
});
}
});
}
patchedSetImmediate[_nodeutil.promisify.custom] = patchedSetImmediatePromise;
const patchedClearImmediate = (immediateObject)=>{
// NOTE: we defensively check for patched immediates even if we're not
// currently capturing immediates, because the objects returned from
// the patched setImmediate can be kept around for arbitrarily long.
// As an optimization, we only do this if the patch was enabled at least once --
// otherwise, no patched objects could've been created.
if (wasEnabledAtLeastOnce && immediateObject && typeof immediateObject === 'object' && INTERNALS in immediateObject) {
;
immediateObject[Symbol.dispose]();
} else {
originalClearImmediate(immediateObject);
}
};
//========================================================
const INTERNALS = Symbol.for('next.Immediate.internals');
function proxyQueuedImmediateToNativeImmediate(immediateObject, nativeImmediate) {
immediateObject[INTERNALS].hasRef = null;
immediateObject[INTERNALS].queueItem = null;
immediateObject[INTERNALS].nativeImmediate = nativeImmediate;
}
/** Implements a shim for the native `Immediate` class returned by `setImmediate` */ class NextImmediate {
hasRef() {
const internals = this[INTERNALS];
if (internals.queueItem) {
return internals.hasRef;
} else if (internals.nativeImmediate) {
return internals.nativeImmediate.hasRef();
} else {
// if we're no longer queued (cleared or executed), hasRef is always false
return false;
}
}
ref() {
const internals = this[INTERNALS];
if (internals.queueItem) {
internals.hasRef = true;
} else if (internals.nativeImmediate) {
internals.nativeImmediate.ref();
}
return this;
}
unref() {
const internals = this[INTERNALS];
if (internals.queueItem) {
internals.hasRef = false;
} else if (internals.nativeImmediate) {
internals.nativeImmediate.unref();
}
return this;
}
/**
* Node invokes `_onImmediate` when an immediate is executed:
* https://github.com/nodejs/node/blob/42d363205715ffa5a4a6d90f4be1311487053d65/lib/internal/timers.js#L504
* It's visible on the public types, so we want to have it here for parity, but it's a noop.
* */ _onImmediate() {}
[Symbol.dispose]() {
// This is equivalent to `clearImmediate`.
const internals = this[INTERNALS];
if (internals.queueItem) {
// this is still queued. drop it.
const queueItem = internals.queueItem;
internals.queueItem = null;
clearQueueItem(queueItem);
} else if (internals.nativeImmediate) {
internals.nativeImmediate[Symbol.dispose]();
}
}
constructor(){
this[INTERNALS] = {
queueItem: null,
hasRef: true,
nativeImmediate: null
};
}
}
// ==========================================
const debug = process.env.NEXT_DEBUG_IMMEDIATES !== '1' ? undefined : (...args)=>{
if (process.env.NEXT_RUNTIME === 'edge') {
throw Object.defineProperty(new _invarianterror.InvariantError('Fast setImmediate is not available in the edge runtime.'), "__NEXT_ERROR_CODE", {
value: "E963",
enumerable: false,
configurable: true
});
} else {
const { inspect } = require('node:util');
const { writeFileSync } = require('node:fs');
let logLine = args.map((arg)=>typeof arg === 'string' ? arg : inspect(arg, {
colors: true
})).join(' ') + '\n';
logLine = '\x1B[2m' + logLine + '\x1B[22m' // styleText('dim', logLine)
;
writeFileSync(process.stdout.fd, logLine);
}
};
// ==========================================
install();
//# sourceMappingURL=fast-set-immediate.external.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,5 @@
export declare function installGlobalBehaviors(config: {
experimental?: {
hideLogsAfterAbort?: boolean;
};
}): void;

View File

@@ -0,0 +1,33 @@
/**
* Unlike most files in the node-environment-extensions folder this one is not
* an extension itself but it exposes a function to install config based global
* behaviors that should be loaded whenever a Node Server or Node Worker are created.
*/ "use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
Object.defineProperty(exports, "installGlobalBehaviors", {
enumerable: true,
get: function() {
return installGlobalBehaviors;
}
});
const _invarianterror = require("../../shared/lib/invariant-error");
const _consoledimexternal = require("./console-dim.external");
function installGlobalBehaviors(config) {
var _config_experimental;
if (process.env.NEXT_RUNTIME === 'edge') {
throw Object.defineProperty(new _invarianterror.InvariantError('Expected not to install Node.js global behaviors in the edge runtime.'), "__NEXT_ERROR_CODE", {
value: "E874",
enumerable: false,
configurable: true
});
}
if (((_config_experimental = config.experimental) == null ? void 0 : _config_experimental.hideLogsAfterAbort) === true) {
(0, _consoledimexternal.setAbortedLogsStyle)('hidden');
} else {
(0, _consoledimexternal.setAbortedLogsStyle)('dimmed');
}
}
//# sourceMappingURL=global-behaviors.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../src/server/node-environment-extensions/global-behaviors.tsx"],"sourcesContent":["/**\n * Unlike most files in the node-environment-extensions folder this one is not\n * an extension itself but it exposes a function to install config based global\n * behaviors that should be loaded whenever a Node Server or Node Worker are created.\n */\nimport { InvariantError } from '../../shared/lib/invariant-error'\n\nimport { setAbortedLogsStyle } from './console-dim.external'\n\nexport function installGlobalBehaviors(config: {\n experimental?: { hideLogsAfterAbort?: boolean }\n}) {\n if (process.env.NEXT_RUNTIME === 'edge') {\n throw new InvariantError(\n 'Expected not to install Node.js global behaviors in the edge runtime.'\n )\n }\n\n if (config.experimental?.hideLogsAfterAbort === true) {\n setAbortedLogsStyle('hidden')\n } else {\n setAbortedLogsStyle('dimmed')\n }\n}\n"],"names":["installGlobalBehaviors","config","process","env","NEXT_RUNTIME","InvariantError","experimental","hideLogsAfterAbort","setAbortedLogsStyle"],"mappings":"AAAA;;;;CAIC;;;;+BAKeA;;;eAAAA;;;gCAJe;oCAEK;AAE7B,SAASA,uBAAuBC,MAEtC;QAOKA;IANJ,IAAIC,QAAQC,GAAG,CAACC,YAAY,KAAK,QAAQ;QACvC,MAAM,qBAEL,CAFK,IAAIC,8BAAc,CACtB,0EADI,qBAAA;mBAAA;wBAAA;0BAAA;QAEN;IACF;IAEA,IAAIJ,EAAAA,uBAAAA,OAAOK,YAAY,qBAAnBL,qBAAqBM,kBAAkB,MAAK,MAAM;QACpDC,IAAAA,uCAAmB,EAAC;IACtB,OAAO;QACLA,IAAAA,uCAAmB,EAAC;IACtB;AACF","ignoreList":[0]}

View File

@@ -0,0 +1,9 @@
/**
* We extend node:crypto APIs during builds and revalidates to ensure that prerenders don't observe random bytes
* When cacheComponents is enabled. Random bytes are a form of IO even if they resolve synchronously. When cacheComponents is
* enabled we need to ensure that random bytes are excluded from prerenders unless they are cached.
*
*
* The extensions here never error nor alter the underlying return values and thus should be transparent to callers.
*/
export {};

View File

@@ -0,0 +1,103 @@
/**
* We extend node:crypto APIs during builds and revalidates to ensure that prerenders don't observe random bytes
* When cacheComponents is enabled. Random bytes are a form of IO even if they resolve synchronously. When cacheComponents is
* enabled we need to ensure that random bytes are excluded from prerenders unless they are cached.
*
*
* The extensions here never error nor alter the underlying return values and thus should be transparent to callers.
*/ "use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
const _utils = require("./utils");
if (process.env.NEXT_RUNTIME === 'edge') {
// nothing to patch
} else {
const nodeCrypto = require('node:crypto');
// require('node:crypto').getRandomValues is an alias for
// crypto.getRandomValues which is extended in web-crypto.tsx
// require('node:crypto').randomUUID is not an alias for crypto.randomUUID
const randomUUIDExpression = "`require('node:crypto').randomUUID()`";
try {
const _randomUUID = nodeCrypto.randomUUID;
nodeCrypto.randomUUID = function randomUUID() {
(0, _utils.io)(randomUUIDExpression, 'random');
return _randomUUID.apply(this, arguments);
};
} catch {
console.error(`Failed to install ${randomUUIDExpression} extension. When using \`cacheComponents\` calling this function will not correctly trigger dynamic behavior.`);
}
const randomBytesExpression = "`require('node:crypto').randomBytes(size)`";
try {
const _randomBytes = nodeCrypto.randomBytes;
// @ts-expect-error -- TODO: tell TS the overloads are preserved
nodeCrypto.randomBytes = function randomBytes() {
if (typeof arguments[1] !== 'function') {
// randomBytes is sync if the second arg is undefined
(0, _utils.io)(randomBytesExpression, 'random');
}
return _randomBytes.apply(this, arguments);
};
} catch {
console.error(`Failed to install ${randomBytesExpression} extension. When using \`cacheComponents\` calling this function without a callback argument will not correctly trigger dynamic behavior.`);
}
const randomFillSyncExpression = "`require('node:crypto').randomFillSync(...)`";
try {
const _randomFillSync = nodeCrypto.randomFillSync;
// @ts-expect-error -- TODO: tell TS the overloads are preserved
nodeCrypto.randomFillSync = function randomFillSync() {
(0, _utils.io)(randomFillSyncExpression, 'random');
return _randomFillSync.apply(this, arguments);
};
} catch {
console.error(`Failed to install ${randomFillSyncExpression} extension. When using \`cacheComponents\` calling this function will not correctly trigger dynamic behavior.`);
}
const randomIntExpression = "`require('node:crypto').randomInt(min, max)`";
try {
const _randomInt = nodeCrypto.randomInt;
// @ts-expect-error -- TODO: tell TS the overloads are preserved
nodeCrypto.randomInt = function randomInt() {
if (typeof arguments[2] !== 'function') {
// randomInt is sync if the third arg is undefined
(0, _utils.io)(randomIntExpression, 'random');
}
return _randomInt.apply(this, arguments);
};
} catch {
console.error(`Failed to install ${randomBytesExpression} extension. When using \`cacheComponents\` calling this function without a callback argument will not correctly trigger dynamic behavior.`);
}
const generatePrimeSyncExpression = "`require('node:crypto').generatePrimeSync(...)`";
try {
const _generatePrimeSync = nodeCrypto.generatePrimeSync;
// @ts-expect-error -- TODO: tell TS the overloads are preserved
nodeCrypto.generatePrimeSync = function generatePrimeSync() {
(0, _utils.io)(generatePrimeSyncExpression, 'random');
return _generatePrimeSync.apply(this, arguments);
};
} catch {
console.error(`Failed to install ${generatePrimeSyncExpression} extension. When using \`cacheComponents\` calling this function will not correctly trigger dynamic behavior.`);
}
const generateKeyPairSyncExpression = "`require('node:crypto').generateKeyPairSync(...)`";
try {
const _generateKeyPairSync = nodeCrypto.generateKeyPairSync;
// @ts-expect-error -- TODO: tell TS the overloads are preserved
nodeCrypto.generateKeyPairSync = function generateKeyPairSync() {
(0, _utils.io)(generateKeyPairSyncExpression, 'random');
return _generateKeyPairSync.apply(this, arguments);
};
} catch {
console.error(`Failed to install ${generateKeyPairSyncExpression} extension. When using \`cacheComponents\` calling this function will not correctly trigger dynamic behavior.`);
}
const generateKeySyncExpression = "`require('node:crypto').generateKeySync(...)`";
try {
const _generateKeySync = nodeCrypto.generateKeySync;
nodeCrypto.generateKeySync = function generateKeySync() {
(0, _utils.io)(generateKeySyncExpression, 'random');
return _generateKeySync.apply(this, arguments);
};
} catch {
console.error(`Failed to install ${generateKeySyncExpression} extension. When using \`cacheComponents\` calling this function will not correctly trigger dynamic behavior.`);
}
}
//# sourceMappingURL=node-crypto.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,8 @@
/**
* We extend Math.random() during builds and revalidates to ensure that prerenders don't observe randomness
* When cacheComponents is enabled. randomness is a form of IO even though it resolves synchronously. When cacheComponents is
* enabled we need to ensure that randomness is excluded from prerenders.
*
* The extensions here never error nor alter the random generation itself and thus should be transparent to callers.
*/
export {};

View File

@@ -0,0 +1,28 @@
/**
* We extend Math.random() during builds and revalidates to ensure that prerenders don't observe randomness
* When cacheComponents is enabled. randomness is a form of IO even though it resolves synchronously. When cacheComponents is
* enabled we need to ensure that randomness is excluded from prerenders.
*
* The extensions here never error nor alter the random generation itself and thus should be transparent to callers.
*/ "use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
const _utils = require("./utils");
const expression = '`Math.random()`';
try {
const _random = Math.random;
Math.random = (function random() {
(0, _utils.io)(expression, 'random');
return _random.apply(null, arguments);
// We bind here to alter the `toString` printing to match `Math.random`'s native `toString`.
// eslint-disable-next-line no-extra-bind
}).bind(null);
Object.defineProperty(Math.random, 'name', {
value: 'random'
});
} catch {
console.error(`Failed to install ${expression} extension. When using \`cacheComponents\` calling this function will not correctly trigger dynamic behavior.`);
}
//# sourceMappingURL=random.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../src/server/node-environment-extensions/random.tsx"],"sourcesContent":["/**\n * We extend Math.random() during builds and revalidates to ensure that prerenders don't observe randomness\n * When cacheComponents is enabled. randomness is a form of IO even though it resolves synchronously. When cacheComponents is\n * enabled we need to ensure that randomness is excluded from prerenders.\n *\n * The extensions here never error nor alter the random generation itself and thus should be transparent to callers.\n */\n\nimport { io } from './utils'\n\nconst expression = '`Math.random()`'\ntry {\n const _random = Math.random\n Math.random = function random() {\n io(expression, 'random')\n return _random.apply(null, arguments as any)\n\n // We bind here to alter the `toString` printing to match `Math.random`'s native `toString`.\n // eslint-disable-next-line no-extra-bind\n }.bind(null)\n Object.defineProperty(Math.random, 'name', { value: 'random' })\n} catch {\n console.error(\n `Failed to install ${expression} extension. When using \\`cacheComponents\\` calling this function will not correctly trigger dynamic behavior.`\n )\n}\n"],"names":["expression","_random","Math","random","io","apply","arguments","bind","Object","defineProperty","value","console","error"],"mappings":"AAAA;;;;;;CAMC;;;;uBAEkB;AAEnB,MAAMA,aAAa;AACnB,IAAI;IACF,MAAMC,UAAUC,KAAKC,MAAM;IAC3BD,KAAKC,MAAM,GAAG,CAAA,SAASA;QACrBC,IAAAA,SAAE,EAACJ,YAAY;QACf,OAAOC,QAAQI,KAAK,CAAC,MAAMC;IAE3B,4FAA4F;IAC5F,yCAAyC;IAC3C,CAAA,EAAEC,IAAI,CAAC;IACPC,OAAOC,cAAc,CAACP,KAAKC,MAAM,EAAE,QAAQ;QAAEO,OAAO;IAAS;AAC/D,EAAE,OAAM;IACNC,QAAQC,KAAK,CACX,CAAC,kBAAkB,EAAEZ,WAAW,6GAA6G,CAAC;AAElJ","ignoreList":[0]}

View File

@@ -0,0 +1,21 @@
/**
* Manages unhandled rejection listeners to intelligently filter rejections
* from aborted prerenders when cache components are enabled.
*
* THE PROBLEM:
* When we abort prerenders we expect to find numerous unhandled promise rejections due to
* things like awaiting Request data like `headers()`. The rejections are fine and should
* not be construed as problematic so we need to avoid the appearance of a problem by
* omitting them from the logged output.
*
* THE STRATEGY:
* 1. Install a filtering unhandled rejection handler
* 2. Intercept process event methods to capture new handlers in our internal queue
* 3. For each rejection, check if it comes from an aborted prerender context
* 4. If yes, suppress it. If no, delegate to all handlers in our queue
* 5. This provides precise filtering without time-based windows
*
* This ensures we suppress noisy prerender-related rejections while preserving
* normal error logging for genuine unhandled rejections.
*/
export {};

View File

@@ -0,0 +1,489 @@
/**
* Manages unhandled rejection listeners to intelligently filter rejections
* from aborted prerenders when cache components are enabled.
*
* THE PROBLEM:
* When we abort prerenders we expect to find numerous unhandled promise rejections due to
* things like awaiting Request data like `headers()`. The rejections are fine and should
* not be construed as problematic so we need to avoid the appearance of a problem by
* omitting them from the logged output.
*
* THE STRATEGY:
* 1. Install a filtering unhandled rejection handler
* 2. Intercept process event methods to capture new handlers in our internal queue
* 3. For each rejection, check if it comes from an aborted prerender context
* 4. If yes, suppress it. If no, delegate to all handlers in our queue
* 5. This provides precise filtering without time-based windows
*
* This ensures we suppress noisy prerender-related rejections while preserving
* normal error logging for genuine unhandled rejections.
*/ "use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
const _workunitasyncstorageexternal = require("../app-render/work-unit-async-storage.external");
const MODE = process.env.NEXT_UNHANDLED_REJECTION_FILTER;
let ENABLE_UHR_FILTER = true;
let UHR_FILTER_LOG_LEVEL = 'warn';
switch(MODE){
case 'silent':
UHR_FILTER_LOG_LEVEL = 'silent';
break;
case 'debug':
UHR_FILTER_LOG_LEVEL = 'debug';
break;
case 'false':
case 'disabled':
case '0':
ENABLE_UHR_FILTER = false;
break;
case '':
case undefined:
case 'enabled':
case 'true':
case '1':
break;
default:
if (typeof MODE === 'string') {
console.error(`NEXT_UNHANDLED_REJECTION_FILTER has an unrecognized value: ${JSON.stringify(MODE)}. Use "enabled", "disabled", "silent", or "debug", or omit the environment variable altogether`);
}
}
let debug;
let debugWithTrace;
let warn;
let warnWithTrace;
switch(UHR_FILTER_LOG_LEVEL){
case 'debug':
debug = (message)=>console.log('[Next.js Unhandled Rejection Filter]: ' + message);
debugWithTrace = (message)=>{
console.log(new DebugWithStack(message));
};
// Intentional fallthrough
case 'warn':
warn = (message)=>{
console.warn('[Next.js Unhandled Rejection Filter]: ' + message);
};
warnWithTrace = (message)=>{
console.warn(new WarnWithStack(message));
};
break;
case 'silent':
default:
}
class DebugWithStack extends Error {
constructor(message){
super(message);
this.name = '[Next.js Unhandled Rejection Filter]';
}
}
class WarnWithStack extends Error {
constructor(message){
super(message);
this.name = '[Next.js Unhandled Rejection Filter]';
}
}
let didWarnUninstalled = false;
const warnUninstalledOnce = warn ? function warnUninstalledOnce(...args) {
if (!didWarnUninstalled) {
didWarnUninstalled = true;
warn(...args);
}
} : undefined;
let filterInstalled = false;
// We store the proxied listeners for unhandled rejections here.
let underlyingListeners = [];
// We store a unique pointer to each event listener registration to track
// details like whether the listener is a once listener.
let listenerMetadata = [];
// These methods are used to restore the original implementations when uninstalling the patch
let originalProcessAddListener;
let originalProcessRemoveListener;
let originalProcessOn;
let originalProcessOff;
let originalProcessPrependListener;
let originalProcessOnce;
let originalProcessPrependOnceListener;
let originalProcessRemoveAllListeners;
let originalProcessListeners;
// Some of these base methods call others and we don't want them to call the patched version so we
// need a way to synchronously disable the patch temporarily.
let bypassPatch = false;
// This patch ensures that if any patched methods end up calling other methods internally they will
// bypass the patch during their execution. This is important for removeAllListeners in particular
// because it calls removeListener internally and we want to ensure it actually clears the listeners
// from the process queue and not our private queue.
function patchWithoutReentrancy(original, patchedImpl) {
// Produce a function which has the correct name
const patched = {
[original.name]: function(...args) {
if (bypassPatch) {
return Reflect.apply(original, process, args);
}
const previousBypassPatch = bypassPatch;
bypassPatch = true;
try {
return Reflect.apply(patchedImpl, process, args);
} finally{
bypassPatch = previousBypassPatch;
}
}
}[original.name];
// Preserve the original toString behavior
Object.defineProperty(patched, 'toString', {
value: original.toString.bind(original),
writable: true,
configurable: true
});
return patched;
}
const MACGUFFIN_EVENT = 'Next.UnhandledRejectionFilter.MacguffinEvent';
/**
* Installs a filtering unhandled rejection handler that intelligently suppresses
* rejections from aborted prerender contexts.
*
* This should be called once during server startup to install the global filter.
*/ function installUnhandledRejectionFilter() {
if (filterInstalled) {
warnWithTrace == null ? void 0 : warnWithTrace('Unexpected subsequent filter installation. This is a bug in Next.js');
return;
}
debug == null ? void 0 : debug('Installing Filter');
// Capture existing handlers
underlyingListeners = Array.from(process.listeners('unhandledRejection'));
// We assume all existing handlers are not "once"
listenerMetadata = underlyingListeners.map((l)=>({
listener: l,
once: false
}));
// Remove all existing handlers
process.removeAllListeners('unhandledRejection');
// Install our filtering handler
process.addListener('unhandledRejection', filteringUnhandledRejectionHandler);
// Store the original process methods
originalProcessAddListener = process.addListener;
originalProcessRemoveListener = process.removeListener;
originalProcessOn = process.on;
originalProcessOff = process.off;
originalProcessPrependListener = process.prependListener;
originalProcessOnce = process.once;
originalProcessPrependOnceListener = process.prependOnceListener;
originalProcessRemoveAllListeners = process.removeAllListeners;
originalProcessListeners = process.listeners;
process.addListener = patchWithoutReentrancy(originalProcessAddListener, function(event, listener) {
if (event === 'unhandledRejection') {
debugWithTrace == null ? void 0 : debugWithTrace(`Appending 'unhandledRejection' listener with name \`${listener.name}\`.`);
// We add the listener to a dummy event in case it throws. We don't catch it intentionally
try {
originalProcessAddListener.call(process, MACGUFFIN_EVENT, listener);
} finally{
// We clean up the added event
originalProcessRemoveAllListeners.call(process, MACGUFFIN_EVENT);
}
// Add new handlers to our internal queue instead of the process
underlyingListeners.push(listener);
listenerMetadata.push({
listener,
once: false
});
return process;
}
// For other events, use the original method
return originalProcessAddListener.call(process, event, listener);
});
// Intercept process.removeListener (alias for process.off)
process.removeListener = patchWithoutReentrancy(originalProcessRemoveListener, function(event, listener) {
if (event === 'unhandledRejection') {
// Check if they're trying to remove our filtering handler
if (listener === filteringUnhandledRejectionHandler) {
warnUninstalledOnce == null ? void 0 : warnUninstalledOnce(`Uninstalling filter because \`process.removeListener('unhandledRejection', listener)\` was called with the filter listener. Uninstalling this filter is not recommended and will cause you to observe 'unhandledRejection' events related to intentionally aborted prerenders.
You can silence warnings related to this behavior by running Next.js with \`NEXT_UNHANDLED_REJECTION_FILTER=silent\` environment variable.
You can debug event listener operations by running Next.js with \`NEXT_UNHANDLED_REJECTION_FILTER=debug\` environment variable.`);
uninstallUnhandledRejectionFilter();
return process;
}
debugWithTrace == null ? void 0 : debugWithTrace(`Removing 'unhandledRejection' listener with name \`${listener.name}\`.`);
// We remove the listener on a dummy event in case it throws. We don't catch it intentionally
originalProcessRemoveListener.call(process, MACGUFFIN_EVENT, listener);
const index = underlyingListeners.lastIndexOf(listener);
if (index > -1) {
debug == null ? void 0 : debug(`listener found index ${index} and removed.`);
underlyingListeners.splice(index, 1);
listenerMetadata.splice(index, 1);
} else {
debug == null ? void 0 : debug(`listener not found.`);
}
return process;
}
// For other events, use the original method
return originalProcessRemoveListener.call(process, event, listener);
});
// If the process.on is referentially process.addListener then share the patched version as well
if (originalProcessOn === originalProcessAddListener) {
process.on = process.addListener;
} else {
process.on = patchWithoutReentrancy(originalProcessOn, function(event, listener) {
if (event === 'unhandledRejection') {
debugWithTrace == null ? void 0 : debugWithTrace(`Appending 'unhandledRejection' listener with name \`${listener.name}\`.`);
// We add the listener to a dummy event in case it throws. We don't catch it intentionally
try {
originalProcessOn.call(process, MACGUFFIN_EVENT, listener);
} finally{
// We clean up the added event
originalProcessRemoveAllListeners.call(process, MACGUFFIN_EVENT);
}
// Add new handlers to our internal queue instead of the process
underlyingListeners.push(listener);
listenerMetadata.push({
listener,
once: false
});
return process;
}
// For other events, use the original method
return originalProcessOn.call(process, event, listener);
});
}
// If the process.off is referentially process.addListener then share the patched version as well
if (originalProcessOff === originalProcessRemoveListener) {
process.off = process.removeListener;
} else {
process.off = patchWithoutReentrancy(originalProcessOff, function(event, listener) {
if (event === 'unhandledRejection') {
// Check if they're trying to remove our filtering handler
if (listener === filteringUnhandledRejectionHandler) {
warnUninstalledOnce == null ? void 0 : warnUninstalledOnce(`Uninstalling filter because \`process.off('unhandledRejection', listener)\` was called with the filter listener. Uninstalling this filter is not recommended and will cause you to observe 'unhandledRejection' events related to intentionally aborted prerenders.
You can silence warnings related to this behavior by running Next.js with \`NEXT_UNHANDLED_REJECTION_FILTER=silent\` environment variable.
You can debug event listener operations by running Next.js with \`NEXT_UNHANDLED_REJECTION_FILTER=debug\` environment variable.`);
uninstallUnhandledRejectionFilter();
return process;
}
debugWithTrace == null ? void 0 : debugWithTrace(`Removing 'unhandledRejection' listener with name \`${listener.name}\`.`);
// We remove the listener on a dummy event in case it throws. We don't catch it intentionally
originalProcessOff.call(process, MACGUFFIN_EVENT, listener);
const index = underlyingListeners.lastIndexOf(listener);
if (index > -1) {
debug == null ? void 0 : debug(`listener found index ${index} and removed.`);
underlyingListeners.splice(index, 1);
listenerMetadata.splice(index, 1);
} else {
debug == null ? void 0 : debug(`listener not found.`);
}
return process;
}
// For other events, use the original method
return originalProcessOff.call(process, event, listener);
});
}
// Intercept process.prependListener for handlers that should go first
process.prependListener = patchWithoutReentrancy(originalProcessPrependListener, function(event, listener) {
if (event === 'unhandledRejection') {
debugWithTrace == null ? void 0 : debugWithTrace(`(Prepending) Inserting 'unhandledRejection' listener with name \`${listener.name}\` immediately following the Next.js 'unhandledRejection' filter listener.`);
// We add the listener to a dummy event in case it throws. We don't catch it intentionally
try {
originalProcessPrependListener.call(process, MACGUFFIN_EVENT, listener);
} finally{
// We clean up the added event
originalProcessRemoveAllListeners.call(process, MACGUFFIN_EVENT);
}
// Add new handlers to the beginning of our internal queue
underlyingListeners.unshift(listener);
listenerMetadata.unshift({
listener,
once: false
});
return process;
}
// For other events, use the original method
return originalProcessPrependListener.call(process, event, listener);
});
// Intercept process.once for one-time handlers
process.once = patchWithoutReentrancy(originalProcessOnce, function(event, listener) {
if (event === 'unhandledRejection') {
debugWithTrace == null ? void 0 : debugWithTrace(`Appending 'unhandledRejection' once-listener with name \`${listener.name}\`.`);
// We add the listener to a dummy event in case it throws. We don't catch it intentionally
try {
originalProcessOnce.call(process, MACGUFFIN_EVENT, listener);
} finally{
// We clean up the added event
originalProcessRemoveAllListeners.call(process, MACGUFFIN_EVENT);
}
underlyingListeners.push(listener);
listenerMetadata.push({
listener: listener,
once: true
});
return process;
}
// For other events, use the original method
return originalProcessOnce.call(process, event, listener);
});
// Intercept process.prependOnceListener for one-time handlers that should go first
process.prependOnceListener = patchWithoutReentrancy(originalProcessPrependOnceListener, function(event, listener) {
if (event === 'unhandledRejection') {
debugWithTrace == null ? void 0 : debugWithTrace(`(Prepending) Inserting 'unhandledRejection' once-listener with name \`${listener.name}\` immediately following the Next.js 'unhandledRejection' filter listener.`);
// We add the listener to a dummy event in case it throws. We don't catch it intentionally
try {
originalProcessPrependOnceListener.call(process, MACGUFFIN_EVENT, listener);
} finally{
// We clean up the added event
originalProcessRemoveAllListeners.call(process, MACGUFFIN_EVENT);
}
// Add to the beginning of our internal queue
underlyingListeners.unshift(listener);
listenerMetadata.unshift({
listener: listener,
once: true
});
return process;
}
// For other events, use the original method
return originalProcessPrependOnceListener.call(process, event, listener);
});
// Intercept process.removeAllListeners
process.removeAllListeners = patchWithoutReentrancy(originalProcessRemoveAllListeners, function(event) {
if (event === 'unhandledRejection') {
// TODO add warning for this case once we stop importing this in test scopes automatically. Currently
// we pull this file in whenever build/utils.tsx is imported which is not the right layering.
// The extensions should be loaded from entrypoints like build/index or next-server
// warnRemoveAllOnce?.(
// `\`process.removeAllListeners('unhandledRejection')\` was called. Next.js maintains the first 'unhandledRejection' listener to filter out unnecessary rejection warnings caused by aborting prerenders early. It is not recommended that you uninstall this behavior, but if you want to you must you can acquire the listener with \`process.listeners('unhandledRejection')[0]\` and remove it with \`process.removeListener('unhandledRejection', listener)\`.
// You can silence warnings related to this behavior by running Next.js with \`NEXT_UNHANDLED_REJECTION_FILTER=silent\` environment variable.
// You can debug event listener operations by running Next.js with \`NEXT_UNHANDLED_REJECTION_FILTER=debug\` environment variable.`
// )
debugWithTrace == null ? void 0 : debugWithTrace(`Removing all 'unhandledRejection' listeners except for the Next.js filter.`);
underlyingListeners.length = 0;
listenerMetadata.length = 0;
return process;
}
// For other specific events, use the original method
if (event !== undefined) {
return originalProcessRemoveAllListeners.call(process, event);
}
// If no event specified (removeAllListeners()), uninstall our patch completely
warnUninstalledOnce == null ? void 0 : warnUninstalledOnce(`Uninstalling filter because \`process.removeAllListeners()\` was called. Uninstalling this filter is not recommended and will cause you to observe 'unhandledRejection' events related to intentionally aborted prerenders.
You can silence warnings related to this behavior by running Next.js with \`NEXT_UNHANDLED_REJECTION_FILTER=silent\` environment variable.
You can debug event listener operations by running Next.js with \`NEXT_UNHANDLED_REJECTION_FILTER=debug\` environment variable.`);
uninstallUnhandledRejectionFilter();
return originalProcessRemoveAllListeners.call(process);
});
// Intercept process.listeners to return our internal handlers for unhandled rejection
process.listeners = patchWithoutReentrancy(originalProcessListeners, function(event) {
if (event === 'unhandledRejection') {
debugWithTrace == null ? void 0 : debugWithTrace(`Retrieving all 'unhandledRejection' listeners.`);
return [
filteringUnhandledRejectionHandler,
...underlyingListeners
];
}
return originalProcessListeners.call(process, event);
});
filterInstalled = true;
}
/**
* Uninstalls the unhandled rejection filter and restores original process methods.
* This is called when someone explicitly removes our filtering handler.
* @internal
*/ function uninstallUnhandledRejectionFilter() {
if (!filterInstalled) {
warnWithTrace == null ? void 0 : warnWithTrace('Unexpected subsequent filter uninstallation. This is a bug in Next.js');
return;
}
debug == null ? void 0 : debug('Uninstalling Filter');
// Restore original process methods
process.on = originalProcessOn;
process.addListener = originalProcessAddListener;
process.once = originalProcessOnce;
process.prependListener = originalProcessPrependListener;
process.prependOnceListener = originalProcessPrependOnceListener;
process.removeListener = originalProcessRemoveListener;
process.off = originalProcessOff;
process.removeAllListeners = originalProcessRemoveAllListeners;
process.listeners = originalProcessListeners;
// Remove our filtering handler
process.removeListener('unhandledRejection', filteringUnhandledRejectionHandler);
// Re-register all the handlers that were in our internal queue
for (const meta of listenerMetadata){
if (meta.once) {
process.once('unhandledRejection', meta.listener);
} else {
process.addListener('unhandledRejection', meta.listener);
}
}
// Reset state
filterInstalled = false;
underlyingListeners.length = 0;
listenerMetadata.length = 0;
}
/**
* The filtering handler that decides whether to suppress or delegate unhandled rejections.
*/ function filteringUnhandledRejectionHandler(reason, promise) {
const capturedListenerMetadata = Array.from(listenerMetadata);
const workUnitStore = _workunitasyncstorageexternal.workUnitAsyncStorage.getStore();
if (workUnitStore) {
switch(workUnitStore.type){
case 'prerender':
case 'prerender-client':
case 'prerender-runtime':
{
const signal = workUnitStore.renderSignal;
if (signal.aborted) {
// This unhandledRejection is from async work spawned in a now
// aborted prerender. We don't need to report this.
return;
}
break;
}
case 'prerender-ppr':
case 'prerender-legacy':
case 'request':
case 'cache':
case 'private-cache':
case 'unstable-cache':
break;
default:
workUnitStore;
}
}
// Not from an aborted prerender, delegate to original handlers
if (capturedListenerMetadata.length === 0) {
// We need to log something because the default behavior when there is
// no event handler installed is to trigger an Unhandled Exception.
// We don't do that here b/c we don't want to rely on this implicit default
// to kill the process since it can be disabled by installing a userland listener
// and you may also choose to run Next.js with args such that unhandled rejections
// do not automatically terminate the process.
console.error('Unhandled Rejection:', reason);
} else {
try {
for (const meta of capturedListenerMetadata){
if (meta.once) {
// This is a once listener. we remove it from our set before we call it
const index = listenerMetadata.indexOf(meta);
if (index !== -1) {
underlyingListeners.splice(index, 1);
listenerMetadata.splice(index, 1);
}
}
const listener = meta.listener;
listener(reason, promise);
}
} catch (error) {
// If any handlers error we produce an Uncaught Exception
setImmediate(()=>{
throw error;
});
}
}
}
// Install the filter when this module is imported
if (ENABLE_UHR_FILTER) {
installUnhandledRejectionFilter();
}
//# sourceMappingURL=unhandled-rejection.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,3 @@
type ApiType = 'time' | 'random' | 'crypto';
export declare function io(expression: string, type: ApiType): void;
export {};

View File

@@ -0,0 +1,186 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
Object.defineProperty(exports, "io", {
enumerable: true,
get: function() {
return io;
}
});
const _workasyncstorageexternal = require("../app-render/work-async-storage.external");
const _workunitasyncstorageexternal = require("../app-render/work-unit-async-storage.external");
const _dynamicrendering = require("../app-render/dynamic-rendering");
const _invarianterror = require("../../shared/lib/invariant-error");
const _stagedrendering = require("../app-render/staged-rendering");
const _runtimereactsexternal = require("../runtime-reacts.external");
function io(expression, type) {
const workUnitStore = _workunitasyncstorageexternal.workUnitAsyncStorage.getStore();
const workStore = _workasyncstorageexternal.workAsyncStorage.getStore();
if (!workUnitStore || !workStore) {
return;
}
switch(workUnitStore.type){
case 'prerender':
case 'prerender-runtime':
{
const prerenderSignal = workUnitStore.controller.signal;
if (prerenderSignal.aborted === false) {
// If the prerender signal is already aborted we don't need to construct
// any stacks because something else actually terminated the prerender.
let message;
switch(type){
case 'time':
message = `Route "${workStore.route}" used ${expression} before accessing either uncached data (e.g. \`fetch()\`) or Request data (e.g. \`cookies()\`, \`headers()\`, \`connection()\`, and \`searchParams\`). Accessing the current time in a Server Component requires reading one of these data sources first. Alternatively, consider moving this expression into a Client Component or Cache Component. See more info here: https://nextjs.org/docs/messages/next-prerender-current-time`;
break;
case 'random':
message = `Route "${workStore.route}" used ${expression} before accessing either uncached data (e.g. \`fetch()\`) or Request data (e.g. \`cookies()\`, \`headers()\`, \`connection()\`, and \`searchParams\`). Accessing random values synchronously in a Server Component requires reading one of these data sources first. Alternatively, consider moving this expression into a Client Component or Cache Component. See more info here: https://nextjs.org/docs/messages/next-prerender-random`;
break;
case 'crypto':
message = `Route "${workStore.route}" used ${expression} before accessing either uncached data (e.g. \`fetch()\`) or Request data (e.g. \`cookies()\`, \`headers()\`, \`connection()\`, and \`searchParams\`). Accessing random cryptographic values synchronously in a Server Component requires reading one of these data sources first. Alternatively, consider moving this expression into a Client Component or Cache Component. See more info here: https://nextjs.org/docs/messages/next-prerender-crypto`;
break;
default:
throw Object.defineProperty(new _invarianterror.InvariantError('Unknown expression type in abortOnSynchronousPlatformIOAccess.'), "__NEXT_ERROR_CODE", {
value: "E526",
enumerable: false,
configurable: true
});
}
(0, _dynamicrendering.abortOnSynchronousPlatformIOAccess)(workStore.route, expression, applyOwnerStack(Object.defineProperty(new Error(message), "__NEXT_ERROR_CODE", {
value: "E394",
enumerable: false,
configurable: true
})), workUnitStore);
}
break;
}
case 'prerender-client':
{
const prerenderSignal = workUnitStore.controller.signal;
if (prerenderSignal.aborted === false) {
// If the prerender signal is already aborted we don't need to construct
// any stacks because something else actually terminated the prerender.
let message;
switch(type){
case 'time':
message = `Route "${workStore.route}" used ${expression} inside a Client Component without a Suspense boundary above it. See more info here: https://nextjs.org/docs/messages/next-prerender-current-time-client`;
break;
case 'random':
message = `Route "${workStore.route}" used ${expression} inside a Client Component without a Suspense boundary above it. See more info here: https://nextjs.org/docs/messages/next-prerender-random-client`;
break;
case 'crypto':
message = `Route "${workStore.route}" used ${expression} inside a Client Component without a Suspense boundary above it. See more info here: https://nextjs.org/docs/messages/next-prerender-crypto-client`;
break;
default:
throw Object.defineProperty(new _invarianterror.InvariantError('Unknown expression type in abortOnSynchronousPlatformIOAccess.'), "__NEXT_ERROR_CODE", {
value: "E526",
enumerable: false,
configurable: true
});
}
(0, _dynamicrendering.abortOnSynchronousPlatformIOAccess)(workStore.route, expression, applyOwnerStack(Object.defineProperty(new Error(message), "__NEXT_ERROR_CODE", {
value: "E394",
enumerable: false,
configurable: true
})), workUnitStore);
}
break;
}
case 'request':
if (process.env.NODE_ENV === 'development') {
const stageController = workUnitStore.stagedRendering;
if (stageController && stageController.canSyncInterrupt()) {
let message;
if (stageController.currentStage === _stagedrendering.RenderStage.Static) {
switch(type){
case 'time':
message = `Route "${workStore.route}" used ${expression} before accessing either uncached data (e.g. \`fetch()\`) or Request data (e.g. \`cookies()\`, \`headers()\`, \`connection()\`, and \`searchParams\`). Accessing the current time in a Server Component requires reading one of these data sources first. Alternatively, consider moving this expression into a Client Component or Cache Component. See more info here: https://nextjs.org/docs/messages/next-prerender-current-time`;
break;
case 'random':
message = `Route "${workStore.route}" used ${expression} before accessing either uncached data (e.g. \`fetch()\`) or Request data (e.g. \`cookies()\`, \`headers()\`, \`connection()\`, and \`searchParams\`). Accessing random values synchronously in a Server Component requires reading one of these data sources first. Alternatively, consider moving this expression into a Client Component or Cache Component. See more info here: https://nextjs.org/docs/messages/next-prerender-random`;
break;
case 'crypto':
message = `Route "${workStore.route}" used ${expression} before accessing either uncached data (e.g. \`fetch()\`) or Request data (e.g. \`cookies()\`, \`headers()\`, \`connection()\`, and \`searchParams\`). Accessing random cryptographic values synchronously in a Server Component requires reading one of these data sources first. Alternatively, consider moving this expression into a Client Component or Cache Component. See more info here: https://nextjs.org/docs/messages/next-prerender-crypto`;
break;
default:
throw Object.defineProperty(new _invarianterror.InvariantError('Unknown expression type in abortOnSynchronousPlatformIOAccess.'), "__NEXT_ERROR_CODE", {
value: "E526",
enumerable: false,
configurable: true
});
}
} else {
// We're in the Runtime stage.
// We only error for Sync IO in the Runtime stage if the route has a runtime prefetch config.
// This check is implemented in `stageController.canSyncInterrupt()` --
// if runtime prefetching isn't enabled, then we won't get here.
let accessStatement;
let additionalInfoLink;
switch(type){
case 'time':
accessStatement = 'the current time';
additionalInfoLink = 'https://nextjs.org/docs/messages/next-prerender-runtime-current-time';
break;
case 'random':
accessStatement = 'random values synchronously';
additionalInfoLink = 'https://nextjs.org/docs/messages/next-prerender-runtime-random';
break;
case 'crypto':
accessStatement = 'random cryptographic values synchronously';
additionalInfoLink = 'https://nextjs.org/docs/messages/next-prerender-runtime-crypto';
break;
default:
throw Object.defineProperty(new _invarianterror.InvariantError('Unknown expression type in abortOnSynchronousPlatformIOAccess.'), "__NEXT_ERROR_CODE", {
value: "E526",
enumerable: false,
configurable: true
});
}
message = `Route "${workStore.route}" used ${expression} before accessing either uncached data (e.g. \`fetch()\`) or awaiting \`connection()\`. When configured for Runtime prefetching, accessing ${accessStatement} in a Server Component requires reading one of these data sources first. Alternatively, consider moving this expression into a Client Component or Cache Component. See more info here: ${additionalInfoLink}`;
}
const syncIOError = applyOwnerStack(Object.defineProperty(new Error(message), "__NEXT_ERROR_CODE", {
value: "E394",
enumerable: false,
configurable: true
}));
stageController.syncInterruptCurrentStageWithReason(syncIOError);
}
}
break;
case 'prerender-ppr':
case 'prerender-legacy':
case 'cache':
case 'private-cache':
case 'unstable-cache':
break;
default:
workUnitStore;
}
}
function applyOwnerStack(error) {
// TODO: Instead of stitching the stacks here, we should log the original
// error as-is when it occurs, and let `patchErrorInspect` handle adding the
// owner stack, instead of logging it deferred in the `LogSafely` component
// via `throwIfDisallowedDynamic`.
if (process.env.NODE_ENV !== 'production') {
var _getClientReact_captureOwnerStack, _getClientReact, _getServerReact_captureOwnerStack, _getServerReact;
const ownerStack = ((_getClientReact = (0, _runtimereactsexternal.getClientReact)()) == null ? void 0 : (_getClientReact_captureOwnerStack = _getClientReact.captureOwnerStack) == null ? void 0 : _getClientReact_captureOwnerStack.call(_getClientReact)) ?? ((_getServerReact = (0, _runtimereactsexternal.getServerReact)()) == null ? void 0 : (_getServerReact_captureOwnerStack = _getServerReact.captureOwnerStack) == null ? void 0 : _getServerReact_captureOwnerStack.call(_getServerReact));
if (ownerStack) {
let stack = ownerStack;
if (error.stack) {
const frames = [];
for (const frame of error.stack.split('\n').slice(1)){
if (frame.includes('react_stack_bottom_frame')) {
break;
}
frames.push(frame);
}
stack = '\n' + frames.join('\n') + stack;
}
error.stack = error.name + ': ' + error.message + stack;
}
}
return error;
}
//# sourceMappingURL=utils.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,9 @@
/**
* We extend Web Crypto APIs during builds and revalidates to ensure that prerenders don't observe random bytes
* When cacheComponents is enabled. Random bytes are a form of IO even if they resolve synchronously. When cacheComponents is
* enabled we need to ensure that random bytes are excluded from prerenders unless they are cached.
*
*
* The extensions here never error nor alter the underlying return values and thus should be transparent to callers.
*/
export {};

View File

@@ -0,0 +1,45 @@
/**
* We extend Web Crypto APIs during builds and revalidates to ensure that prerenders don't observe random bytes
* When cacheComponents is enabled. Random bytes are a form of IO even if they resolve synchronously. When cacheComponents is
* enabled we need to ensure that random bytes are excluded from prerenders unless they are cached.
*
*
* The extensions here never error nor alter the underlying return values and thus should be transparent to callers.
*/ "use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
const _utils = require("./utils");
let webCrypto;
if (process.env.NEXT_RUNTIME === 'edge') {
webCrypto = crypto;
} else {
if (typeof crypto === 'undefined') {
// @ts-expect-error -- TODO: Is this actually safe?
webCrypto = require('node:crypto').webcrypto;
} else {
webCrypto = crypto;
}
}
const getRandomValuesExpression = '`crypto.getRandomValues()`';
try {
const _getRandomValues = webCrypto.getRandomValues;
webCrypto.getRandomValues = function getRandomValues() {
(0, _utils.io)(getRandomValuesExpression, 'crypto');
return _getRandomValues.apply(webCrypto, arguments);
};
} catch {
console.error(`Failed to install ${getRandomValuesExpression} extension. When using \`cacheComponents\` calling this function will not correctly trigger dynamic behavior.`);
}
const randomUUIDExpression = '`crypto.randomUUID()`';
try {
const _randomUUID = webCrypto.randomUUID;
webCrypto.randomUUID = function randomUUID() {
(0, _utils.io)(randomUUIDExpression, 'crypto');
return _randomUUID.apply(webCrypto, arguments);
};
} catch {
console.error(`Failed to install ${getRandomValuesExpression} extension. When using \`cacheComponents\` calling this function will not correctly trigger dynamic behavior.`);
}
//# sourceMappingURL=web-crypto.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../src/server/node-environment-extensions/web-crypto.tsx"],"sourcesContent":["/**\n * We extend Web Crypto APIs during builds and revalidates to ensure that prerenders don't observe random bytes\n * When cacheComponents is enabled. Random bytes are a form of IO even if they resolve synchronously. When cacheComponents is\n * enabled we need to ensure that random bytes are excluded from prerenders unless they are cached.\n *\n *\n * The extensions here never error nor alter the underlying return values and thus should be transparent to callers.\n */\n\nimport { io } from './utils'\n\nlet webCrypto: typeof crypto\nif (process.env.NEXT_RUNTIME === 'edge') {\n webCrypto = crypto\n} else {\n if (typeof crypto === 'undefined') {\n // @ts-expect-error -- TODO: Is this actually safe?\n webCrypto = (require('node:crypto') as typeof import('node:crypto'))\n .webcrypto\n } else {\n webCrypto = crypto\n }\n}\n\nconst getRandomValuesExpression = '`crypto.getRandomValues()`'\ntry {\n const _getRandomValues = webCrypto.getRandomValues\n webCrypto.getRandomValues = function getRandomValues() {\n io(getRandomValuesExpression, 'crypto')\n return _getRandomValues.apply(webCrypto, arguments as any)\n }\n} catch {\n console.error(\n `Failed to install ${getRandomValuesExpression} extension. When using \\`cacheComponents\\` calling this function will not correctly trigger dynamic behavior.`\n )\n}\n\nconst randomUUIDExpression = '`crypto.randomUUID()`'\ntry {\n const _randomUUID = webCrypto.randomUUID\n webCrypto.randomUUID = function randomUUID() {\n io(randomUUIDExpression, 'crypto')\n return _randomUUID.apply(webCrypto, arguments as any)\n } as typeof _randomUUID\n} catch {\n console.error(\n `Failed to install ${getRandomValuesExpression} extension. When using \\`cacheComponents\\` calling this function will not correctly trigger dynamic behavior.`\n )\n}\n"],"names":["webCrypto","process","env","NEXT_RUNTIME","crypto","require","webcrypto","getRandomValuesExpression","_getRandomValues","getRandomValues","io","apply","arguments","console","error","randomUUIDExpression","_randomUUID","randomUUID"],"mappings":"AAAA;;;;;;;CAOC;;;;uBAEkB;AAEnB,IAAIA;AACJ,IAAIC,QAAQC,GAAG,CAACC,YAAY,KAAK,QAAQ;IACvCH,YAAYI;AACd,OAAO;IACL,IAAI,OAAOA,WAAW,aAAa;QACjC,mDAAmD;QACnDJ,YAAY,AAACK,QAAQ,eAClBC,SAAS;IACd,OAAO;QACLN,YAAYI;IACd;AACF;AAEA,MAAMG,4BAA4B;AAClC,IAAI;IACF,MAAMC,mBAAmBR,UAAUS,eAAe;IAClDT,UAAUS,eAAe,GAAG,SAASA;QACnCC,IAAAA,SAAE,EAACH,2BAA2B;QAC9B,OAAOC,iBAAiBG,KAAK,CAACX,WAAWY;IAC3C;AACF,EAAE,OAAM;IACNC,QAAQC,KAAK,CACX,CAAC,kBAAkB,EAAEP,0BAA0B,6GAA6G,CAAC;AAEjK;AAEA,MAAMQ,uBAAuB;AAC7B,IAAI;IACF,MAAMC,cAAchB,UAAUiB,UAAU;IACxCjB,UAAUiB,UAAU,GAAG,SAASA;QAC9BP,IAAAA,SAAE,EAACK,sBAAsB;QACzB,OAAOC,YAAYL,KAAK,CAACX,WAAWY;IACtC;AACF,EAAE,OAAM;IACNC,QAAQC,KAAK,CACX,CAAC,kBAAkB,EAAEP,0BAA0B,6GAA6G,CAAC;AAEjK","ignoreList":[0]}