This commit is contained in:
Kismet Hasanaj
2026-05-02 20:07:02 +02:00
parent ce8672e283
commit 34dc9aec52
9428 changed files with 1733330 additions and 0 deletions
+41
View File
@@ -0,0 +1,41 @@
import type { EdgeFunctionDefinition } from '../../../build/webpack/plugins/middleware-plugin';
import { AsyncLocalStorage } from 'async_hooks';
import { EdgeRuntime } from 'next/dist/compiled/edge-runtime';
/**
* Same as clearModuleContext but for all module contexts.
*/
export declare function clearAllModuleContexts(): Promise<void>;
/**
* For a given path a context, this function checks if there is any module
* context that contains the path with an older content and, if that's the
* case, removes the context from the cache.
*
* This function also clears all intervals and timeouts created by the
* module context.
*/
export declare function clearModuleContext(path: string): Promise<void>;
export declare const requestStore: AsyncLocalStorage<{
headers: Headers;
}>;
export declare const edgeSandboxNextRequestContext: import("../../after/builtin-request-context").RunnableBuiltinRequestContext;
interface ModuleContextOptions {
moduleName: string;
onError: (err: unknown) => void;
onWarning: (warn: Error) => void;
useCache: boolean;
distDir: string;
edgeFunctionEntry: Pick<EdgeFunctionDefinition, 'assets' | 'wasm' | 'env'>;
}
/**
* For a given module name this function will get a cached module
* context or create it. It will return the module context along
* with a function that allows to run some code from a given
* filepath within the context.
*/
export declare function getModuleContext(options: ModuleContextOptions): Promise<{
evaluateInContext: (filepath: string) => void;
runtime: EdgeRuntime;
paths: Map<string, string>;
warnedEvals: Set<string>;
}>;
export {};
+462
View File
@@ -0,0 +1,462 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
0 && (module.exports = {
clearAllModuleContexts: null,
clearModuleContext: null,
edgeSandboxNextRequestContext: null,
getModuleContext: null,
requestStore: null
});
function _export(target, all) {
for(var name in all)Object.defineProperty(target, name, {
enumerable: true,
get: all[name]
});
}
_export(exports, {
clearAllModuleContexts: function() {
return clearAllModuleContexts;
},
clearModuleContext: function() {
return clearModuleContext;
},
edgeSandboxNextRequestContext: function() {
return edgeSandboxNextRequestContext;
},
getModuleContext: function() {
return getModuleContext;
},
requestStore: function() {
return requestStore;
}
});
const _async_hooks = require("async_hooks");
const _constants = require("../../../shared/lib/constants");
const _edgeruntime = require("next/dist/compiled/edge-runtime");
const _fs = require("fs");
const _utils = require("../utils");
const _pick = require("../../../lib/pick");
const _fetchinlineassets = require("./fetch-inline-assets");
const _vm = require("vm");
const _nodebuffer = /*#__PURE__*/ _interop_require_default(require("node:buffer"));
const _nodeevents = /*#__PURE__*/ _interop_require_default(require("node:events"));
const _nodeassert = /*#__PURE__*/ _interop_require_default(require("node:assert"));
const _nodeutil = /*#__PURE__*/ _interop_require_default(require("node:util"));
const _nodeasync_hooks = /*#__PURE__*/ _interop_require_default(require("node:async_hooks"));
const _resourcemanagers = require("./resource-managers");
const _builtinrequestcontext = require("../../after/builtin-request-context");
const _patcherrorinspect = require("../../patch-error-inspect");
function _interop_require_default(obj) {
return obj && obj.__esModule ? obj : {
default: obj
};
}
let getServerError;
let decorateServerError;
if (process.env.NODE_ENV === 'development') {
getServerError = require('../../dev/node-stack-frames').getServerError;
decorateServerError = require('../../../shared/lib/error-source').decorateServerError;
} else {
getServerError = (error)=>error;
decorateServerError = ()=>{};
}
/**
* A Map of cached module contexts indexed by the module name. It allows
* to have a different cache scoped per module name or depending on the
* provided module key on creation.
*/ const moduleContexts = new Map();
const pendingModuleCaches = new Map();
async function clearAllModuleContexts() {
_resourcemanagers.intervalsManager.removeAll();
_resourcemanagers.timeoutsManager.removeAll();
moduleContexts.clear();
pendingModuleCaches.clear();
}
async function clearModuleContext(path) {
_resourcemanagers.intervalsManager.removeAll();
_resourcemanagers.timeoutsManager.removeAll();
const handleContext = (key, cache, context)=>{
if (cache == null ? void 0 : cache.paths.has(path)) {
context.delete(key);
}
};
for (const [key, cache] of moduleContexts){
handleContext(key, cache, moduleContexts);
}
for (const [key, cache] of pendingModuleCaches){
handleContext(key, await cache, pendingModuleCaches);
}
}
async function loadWasm(wasm) {
const modules = {};
await Promise.all(wasm.map(async (binding)=>{
const module1 = await WebAssembly.compile(// @ts-expect-error - Argument of type 'Buffer<ArrayBufferLike>' is not assignable to parameter of type 'BufferSource'.
await _fs.promises.readFile(binding.filePath));
modules[binding.name] = module1;
}));
return modules;
}
function buildEnvironmentVariablesFrom(injectedEnvironments) {
let env = Object.fromEntries([
...Object.entries(process.env),
...Object.entries(injectedEnvironments),
[
'NEXT_RUNTIME',
'edge'
]
]);
return env;
}
function throwUnsupportedAPIError(name) {
const error = Object.defineProperty(new Error(`A Node.js API is used (${name}) which is not supported in the Edge Runtime.
Learn more: https://nextjs.org/docs/api-reference/edge-runtime`), "__NEXT_ERROR_CODE", {
value: "E97",
enumerable: false,
configurable: true
});
decorateServerError(error, _constants.COMPILER_NAMES.edgeServer);
throw error;
}
function createProcessPolyfill(env) {
const processPolyfill = {
env: buildEnvironmentVariablesFrom(env)
};
const overriddenValue = {};
for (const key of Object.keys(process)){
if (key === 'env') continue;
Object.defineProperty(processPolyfill, key, {
get () {
if (overriddenValue[key] !== undefined) {
return overriddenValue[key];
}
if (typeof process[key] === 'function') {
return ()=>throwUnsupportedAPIError(`process.${key}`);
}
return undefined;
},
set (value) {
overriddenValue[key] = value;
},
enumerable: false
});
}
return processPolyfill;
}
function addStub(context, name) {
Object.defineProperty(context, name, {
get () {
return function() {
throwUnsupportedAPIError(name);
};
},
enumerable: false
});
}
function getDecorateUnhandledError(runtime) {
const EdgeRuntimeError = runtime.evaluate(`Error`);
return (error)=>{
if (error instanceof EdgeRuntimeError) {
decorateServerError(error, _constants.COMPILER_NAMES.edgeServer);
}
};
}
function getDecorateUnhandledRejection(runtime) {
const EdgeRuntimeError = runtime.evaluate(`Error`);
return (rejected)=>{
if (rejected.reason instanceof EdgeRuntimeError) {
decorateServerError(rejected.reason, _constants.COMPILER_NAMES.edgeServer);
}
};
}
const NativeModuleMap = (()=>{
const mods = {
'node:buffer': (0, _pick.pick)(_nodebuffer.default, [
'constants',
'kMaxLength',
'kStringMaxLength',
'Buffer',
'SlowBuffer'
]),
'node:events': (0, _pick.pick)(_nodeevents.default, [
'EventEmitter',
'captureRejectionSymbol',
'defaultMaxListeners',
'errorMonitor',
'listenerCount',
'on',
'once'
]),
'node:async_hooks': (0, _pick.pick)(_nodeasync_hooks.default, [
'AsyncLocalStorage',
'AsyncResource'
]),
'node:assert': (0, _pick.pick)(_nodeassert.default, [
'AssertionError',
'deepEqual',
'deepStrictEqual',
'doesNotMatch',
'doesNotReject',
'doesNotThrow',
'equal',
'fail',
'ifError',
'match',
'notDeepEqual',
'notDeepStrictEqual',
'notEqual',
'notStrictEqual',
'ok',
'rejects',
'strict',
'strictEqual',
'throws'
]),
'node:util': (0, _pick.pick)(_nodeutil.default, [
'_extend',
'callbackify',
'format',
'inherits',
'promisify',
'types'
])
};
return new Map(Object.entries(mods));
})();
const requestStore = new _async_hooks.AsyncLocalStorage();
const edgeSandboxNextRequestContext = (0, _builtinrequestcontext.createLocalRequestContext)();
/**
* Create a module cache specific for the provided parameters. It includes
* a runtime context, require cache and paths cache.
*/ async function createModuleContext(options) {
const warnedEvals = new Set();
const warnedWasmCodegens = new Set();
const { edgeFunctionEntry } = options;
const wasm = await loadWasm(edgeFunctionEntry.wasm ?? []);
const runtime = new _edgeruntime.EdgeRuntime({
codeGeneration: process.env.NODE_ENV !== 'production' ? {
strings: true,
wasm: true
} : undefined,
extend: (context)=>{
context.process = createProcessPolyfill(edgeFunctionEntry.env);
Object.defineProperty(context, 'require', {
enumerable: false,
value: (id)=>{
const value = NativeModuleMap.get(id);
if (!value) {
throw Object.defineProperty(new TypeError('Native module not found: ' + id), "__NEXT_ERROR_CODE", {
value: "E546",
enumerable: false,
configurable: true
});
}
return value;
}
});
if (process.env.NODE_ENV !== 'production') {
context.__next_log_error__ = function(err) {
options.onError(err);
};
}
context.__next_eval__ = function __next_eval__(fn) {
const key = fn.toString();
if (!warnedEvals.has(key)) {
const warning = getServerError(Object.defineProperty(new Error(`Dynamic Code Evaluation (e. g. 'eval', 'new Function') not allowed in Edge Runtime
Learn More: https://nextjs.org/docs/messages/edge-dynamic-code-evaluation`), "__NEXT_ERROR_CODE", {
value: "E149",
enumerable: false,
configurable: true
}), _constants.COMPILER_NAMES.edgeServer);
warning.name = 'DynamicCodeEvaluationWarning';
Error.captureStackTrace(warning, __next_eval__);
warnedEvals.add(key);
options.onWarning(warning);
}
return fn();
};
context.__next_webassembly_compile__ = function __next_webassembly_compile__(fn) {
const key = fn.toString();
if (!warnedWasmCodegens.has(key)) {
const warning = getServerError(Object.defineProperty(new Error(`Dynamic WASM code generation (e. g. 'WebAssembly.compile') not allowed in Edge Runtime.
Learn More: https://nextjs.org/docs/messages/edge-dynamic-code-evaluation`), "__NEXT_ERROR_CODE", {
value: "E184",
enumerable: false,
configurable: true
}), _constants.COMPILER_NAMES.edgeServer);
warning.name = 'DynamicWasmCodeGenerationWarning';
Error.captureStackTrace(warning, __next_webassembly_compile__);
warnedWasmCodegens.add(key);
options.onWarning(warning);
}
return fn();
};
context.__next_webassembly_instantiate__ = async function __next_webassembly_instantiate__(fn) {
const result = await fn();
// If a buffer is given, WebAssembly.instantiate returns an object
// containing both a module and an instance while it returns only an
// instance if a WASM module is given. Utilize the fact to determine
// if the WASM code generation happens.
//
// https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/instantiate#primary_overload_%E2%80%94_taking_wasm_binary_code
const instantiatedFromBuffer = result.hasOwnProperty('module');
const key = fn.toString();
if (instantiatedFromBuffer && !warnedWasmCodegens.has(key)) {
const warning = getServerError(Object.defineProperty(new Error(`Dynamic WASM code generation ('WebAssembly.instantiate' with a buffer parameter) not allowed in Edge Runtime.
Learn More: https://nextjs.org/docs/messages/edge-dynamic-code-evaluation`), "__NEXT_ERROR_CODE", {
value: "E40",
enumerable: false,
configurable: true
}), _constants.COMPILER_NAMES.edgeServer);
warning.name = 'DynamicWasmCodeGenerationWarning';
Error.captureStackTrace(warning, __next_webassembly_instantiate__);
warnedWasmCodegens.add(key);
options.onWarning(warning);
}
return result;
};
const __fetch = context.fetch;
context.fetch = async (input, init = {})=>{
const callingError = Object.defineProperty(new Error('[internal]'), "__NEXT_ERROR_CODE", {
value: "E5",
enumerable: false,
configurable: true
});
const assetResponse = await (0, _fetchinlineassets.fetchInlineAsset)({
input,
assets: options.edgeFunctionEntry.assets,
distDir: options.distDir,
context
});
if (assetResponse) {
return assetResponse;
}
init.headers = new Headers(init.headers ?? {});
if (!init.headers.has('user-agent')) {
init.headers.set(`user-agent`, `Next.js Middleware`);
}
const response = typeof input === 'object' && 'url' in input ? __fetch(input.url, {
...(0, _pick.pick)(input, [
'method',
'body',
'cache',
'credentials',
'integrity',
'keepalive',
'mode',
'redirect',
'referrer',
'referrerPolicy',
'signal'
]),
...init,
headers: {
...Object.fromEntries(input.headers),
...Object.fromEntries(init.headers)
}
}) : __fetch(String(input), init);
return await response.catch((err)=>{
callingError.message = err.message;
err.stack = callingError.stack;
throw err;
});
};
const __Request = context.Request;
context.Request = class extends __Request {
constructor(input, init){
const url = typeof input !== 'string' && 'url' in input ? input.url : String(input);
if (typeof input === 'string') {
(0, _utils.validateURL)(url);
super(input, init);
} else {
super(input, init);
(0, _utils.validateURL)(url);
}
this.next = init == null ? void 0 : init.next;
}
};
const __redirect = context.Response.redirect.bind(context.Response);
context.Response.redirect = (...args)=>{
(0, _utils.validateURL)(args[0]);
return __redirect(...args);
};
for (const name of _constants.EDGE_UNSUPPORTED_NODE_APIS){
addStub(context, name);
}
Object.assign(context, wasm);
context.performance = performance;
context.AsyncLocalStorage = _async_hooks.AsyncLocalStorage;
// @ts-ignore the timeouts have weird types in the edge runtime
context.setInterval = (...args)=>_resourcemanagers.intervalsManager.add(args);
// @ts-ignore the timeouts have weird types in the edge runtime
context.clearInterval = (interval)=>_resourcemanagers.intervalsManager.remove(interval);
// @ts-ignore the timeouts have weird types in the edge runtime
context.setTimeout = (...args)=>_resourcemanagers.timeoutsManager.add(args);
// @ts-ignore the timeouts have weird types in the edge runtime
context.clearTimeout = (timeout)=>_resourcemanagers.timeoutsManager.remove(timeout);
// Duplicated from packages/next/src/server/after/builtin-request-context.ts
// because we need to use the sandboxed `Symbol.for`, not the one from the outside
const NEXT_REQUEST_CONTEXT_SYMBOL = context.Symbol.for('@next/request-context');
Object.defineProperty(context, NEXT_REQUEST_CONTEXT_SYMBOL, {
enumerable: false,
value: edgeSandboxNextRequestContext
});
return context;
}
});
const decorateUnhandledError = getDecorateUnhandledError(runtime);
runtime.context.addEventListener('error', decorateUnhandledError);
const decorateUnhandledRejection = getDecorateUnhandledRejection(runtime);
runtime.context.addEventListener('unhandledrejection', decorateUnhandledRejection);
(0, _patcherrorinspect.patchErrorInspectEdgeLite)(runtime.context.Error);
// An Error from within the Edge Runtime could also bubble up into the Node.js process.
// For example, uncaught errors are handled in the Node.js runtime.
(0, _patcherrorinspect.patchErrorInspectNodeJS)(runtime.context.Error);
return {
runtime,
paths: new Map(),
warnedEvals: new Set()
};
}
function getModuleContextShared(options) {
let deferredModuleContext = pendingModuleCaches.get(options.moduleName);
if (!deferredModuleContext) {
deferredModuleContext = createModuleContext(options);
pendingModuleCaches.set(options.moduleName, deferredModuleContext);
}
return deferredModuleContext;
}
async function getModuleContext(options) {
let lazyModuleContext;
if (options.useCache) {
lazyModuleContext = moduleContexts.get(options.moduleName) || await getModuleContextShared(options);
}
if (!lazyModuleContext) {
lazyModuleContext = await createModuleContext(options);
moduleContexts.set(options.moduleName, lazyModuleContext);
}
const moduleContext = lazyModuleContext;
const evaluateInContext = (filepath)=>{
if (!moduleContext.paths.has(filepath)) {
const content = (0, _fs.readFileSync)(filepath, 'utf-8');
try {
(0, _vm.runInContext)(content, moduleContext.runtime.context, {
filename: filepath
});
moduleContext.paths.set(filepath, content);
} catch (error) {
if (options.useCache) {
moduleContext == null ? void 0 : moduleContext.paths.delete(filepath);
}
throw error;
}
}
};
return {
...moduleContext,
evaluateInContext
};
}
//# sourceMappingURL=context.js.map
File diff suppressed because one or more lines are too long
+15
View File
@@ -0,0 +1,15 @@
import type { EdgeFunctionDefinition } from '../../../build/webpack/plugins/middleware-plugin';
/**
* Short-circuits the `fetch` function
* to return a stream for a given asset, if a user used `new URL("file", import.meta.url)`.
* This allows to embed assets in Edge Runtime.
*/
export declare function fetchInlineAsset(options: {
input: RequestInfo | URL;
distDir: string;
assets: EdgeFunctionDefinition['assets'];
context: {
Response: typeof Response;
ReadableStream: typeof ReadableStream;
};
}): Promise<Response | undefined>;
+35
View File
@@ -0,0 +1,35 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
Object.defineProperty(exports, "fetchInlineAsset", {
enumerable: true,
get: function() {
return fetchInlineAsset;
}
});
const _fs = require("fs");
const _bodystreams = require("../../body-streams");
const _path = require("path");
async function fetchInlineAsset(options) {
const inputString = String(options.input);
if (!inputString.startsWith('blob:')) {
return;
}
const name = inputString.replace('blob:', '');
const asset = options.assets ? options.assets.find((x)=>x.name === name) : {
name,
filePath: name
};
if (!asset) {
return;
}
const filePath = (0, _path.resolve)(options.distDir, asset.filePath);
const fileIsReadable = await _fs.promises.access(filePath).then(()=>true, ()=>false);
if (fileIsReadable) {
const readStream = (0, _fs.createReadStream)(filePath);
return new options.context.Response((0, _bodystreams.requestToBodyStream)(options.context, Uint8Array, readStream));
}
}
//# sourceMappingURL=fetch-inline-assets.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"sources":["../../../../src/server/web/sandbox/fetch-inline-assets.ts"],"sourcesContent":["import type { EdgeFunctionDefinition } from '../../../build/webpack/plugins/middleware-plugin'\nimport { createReadStream, promises as fs } from 'fs'\nimport { requestToBodyStream } from '../../body-streams'\nimport { resolve } from 'path'\n\n/**\n * Short-circuits the `fetch` function\n * to return a stream for a given asset, if a user used `new URL(\"file\", import.meta.url)`.\n * This allows to embed assets in Edge Runtime.\n */\nexport async function fetchInlineAsset(options: {\n input: RequestInfo | URL\n distDir: string\n assets: EdgeFunctionDefinition['assets']\n context: { Response: typeof Response; ReadableStream: typeof ReadableStream }\n}): Promise<Response | undefined> {\n const inputString = String(options.input)\n if (!inputString.startsWith('blob:')) {\n return\n }\n\n const name = inputString.replace('blob:', '')\n const asset = options.assets\n ? options.assets.find((x) => x.name === name)\n : {\n name,\n filePath: name,\n }\n if (!asset) {\n return\n }\n\n const filePath = resolve(options.distDir, asset.filePath)\n const fileIsReadable = await fs.access(filePath).then(\n () => true,\n () => false\n )\n\n if (fileIsReadable) {\n const readStream = createReadStream(filePath)\n return new options.context.Response(\n requestToBodyStream(options.context, Uint8Array, readStream)\n )\n }\n}\n"],"names":["fetchInlineAsset","options","inputString","String","input","startsWith","name","replace","asset","assets","find","x","filePath","resolve","distDir","fileIsReadable","fs","access","then","readStream","createReadStream","context","Response","requestToBodyStream","Uint8Array"],"mappings":";;;;+BAUsBA;;;eAAAA;;;oBAT2B;6BACb;sBACZ;AAOjB,eAAeA,iBAAiBC,OAKtC;IACC,MAAMC,cAAcC,OAAOF,QAAQG,KAAK;IACxC,IAAI,CAACF,YAAYG,UAAU,CAAC,UAAU;QACpC;IACF;IAEA,MAAMC,OAAOJ,YAAYK,OAAO,CAAC,SAAS;IAC1C,MAAMC,QAAQP,QAAQQ,MAAM,GACxBR,QAAQQ,MAAM,CAACC,IAAI,CAAC,CAACC,IAAMA,EAAEL,IAAI,KAAKA,QACtC;QACEA;QACAM,UAAUN;IACZ;IACJ,IAAI,CAACE,OAAO;QACV;IACF;IAEA,MAAMI,WAAWC,IAAAA,aAAO,EAACZ,QAAQa,OAAO,EAAEN,MAAMI,QAAQ;IACxD,MAAMG,iBAAiB,MAAMC,YAAE,CAACC,MAAM,CAACL,UAAUM,IAAI,CACnD,IAAM,MACN,IAAM;IAGR,IAAIH,gBAAgB;QAClB,MAAMI,aAAaC,IAAAA,oBAAgB,EAACR;QACpC,OAAO,IAAIX,QAAQoB,OAAO,CAACC,QAAQ,CACjCC,IAAAA,gCAAmB,EAACtB,QAAQoB,OAAO,EAAEG,YAAYL;IAErD;AACF","ignoreList":[0]}
+2
View File
@@ -0,0 +1,2 @@
export * from './sandbox';
export { clearModuleContext } from './context';
+28
View File
@@ -0,0 +1,28 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
Object.defineProperty(exports, "clearModuleContext", {
enumerable: true,
get: function() {
return _context.clearModuleContext;
}
});
0 && __export(require("./sandbox"));
_export_star(require("./sandbox"), exports);
const _context = require("./context");
function _export_star(from, to) {
Object.keys(from).forEach(function(k) {
if (k !== "default" && !Object.prototype.hasOwnProperty.call(to, k)) {
Object.defineProperty(to, k, {
enumerable: true,
get: function() {
return from[k];
}
});
}
});
return from;
}
//# sourceMappingURL=index.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"sources":["../../../../src/server/web/sandbox/index.ts"],"sourcesContent":["export * from './sandbox'\nexport { clearModuleContext } from './context'\n"],"names":["clearModuleContext"],"mappings":";;;;+BACSA;;;eAAAA,2BAAkB;;;;qBADb;yBACqB","ignoreList":[0]}
+19
View File
@@ -0,0 +1,19 @@
declare abstract class ResourceManager<T, Args> {
private resources;
abstract create(resourceArgs: Args): T;
abstract destroy(resource: T): void;
add(resourceArgs: Args): T;
remove(resource: T): void;
removeAll(): void;
}
declare class IntervalsManager extends ResourceManager<number, Parameters<typeof setInterval>> {
create(args: Parameters<typeof setInterval>): number;
destroy(interval: number): void;
}
declare class TimeoutsManager extends ResourceManager<number, Parameters<typeof setTimeout>> {
create(args: Parameters<typeof setTimeout>): number;
destroy(timeout: number): void;
}
export declare const intervalsManager: IntervalsManager;
export declare const timeoutsManager: TimeoutsManager;
export {};
+88
View File
@@ -0,0 +1,88 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
0 && (module.exports = {
intervalsManager: null,
timeoutsManager: null
});
function _export(target, all) {
for(var name in all)Object.defineProperty(target, name, {
enumerable: true,
get: all[name]
});
}
_export(exports, {
intervalsManager: function() {
return intervalsManager;
},
timeoutsManager: function() {
return timeoutsManager;
}
});
class ResourceManager {
add(resourceArgs) {
const resource = this.create(resourceArgs);
this.resources.push(resource);
return resource;
}
remove(resource) {
this.resources = this.resources.filter((r)=>r !== resource);
this.destroy(resource);
}
removeAll() {
this.resources.forEach(this.destroy);
this.resources = [];
}
constructor(){
this.resources = [];
}
}
class IntervalsManager extends ResourceManager {
create(args) {
// TODO: use the edge runtime provided `setInterval` instead
return webSetIntervalPolyfill(...args);
}
destroy(interval) {
clearInterval(interval);
}
}
class TimeoutsManager extends ResourceManager {
create(args) {
// TODO: use the edge runtime provided `setTimeout` instead
return webSetTimeoutPolyfill(...args);
}
destroy(timeout) {
clearTimeout(timeout);
}
}
function webSetIntervalPolyfill(callback, ms, ...args) {
return setInterval(()=>{
// node's `setInterval` sets `this` to the `Timeout` instance it returned,
// but web `setInterval` always sets `this` to `window`
// see: https://developer.mozilla.org/en-US/docs/Web/API/Window/setInterval#the_this_problem
return callback.apply(globalThis, args);
}, ms)[Symbol.toPrimitive]();
}
function webSetTimeoutPolyfill(callback, ms, ...args) {
const wrappedCallback = ()=>{
try {
// node's `setTimeout` sets `this` to the `Timeout` instance it returned,
// but web `setTimeout` always sets `this` to `window`
// see: https://developer.mozilla.org/en-US/docs/Web/API/Window/setTimeout#the_this_problem
return callback.apply(globalThis, args);
} finally{
// On certain older node versions (<20.16.0, <22.4.0),
// a `setTimeout` whose Timeout was converted to a primitive will leak.
// See: https://github.com/nodejs/node/issues/53335
// We can work around this by explicitly calling `clearTimeout` after the callback runs.
clearTimeout(timeout);
}
};
const timeout = setTimeout(wrappedCallback, ms);
return timeout[Symbol.toPrimitive]();
}
const intervalsManager = new IntervalsManager();
const timeoutsManager = new TimeoutsManager();
//# sourceMappingURL=resource-managers.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"sources":["../../../../src/server/web/sandbox/resource-managers.ts"],"sourcesContent":["abstract class ResourceManager<T, Args> {\n private resources: T[] = []\n\n abstract create(resourceArgs: Args): T\n abstract destroy(resource: T): void\n\n add(resourceArgs: Args) {\n const resource = this.create(resourceArgs)\n this.resources.push(resource)\n return resource\n }\n\n remove(resource: T) {\n this.resources = this.resources.filter((r) => r !== resource)\n this.destroy(resource)\n }\n\n removeAll() {\n this.resources.forEach(this.destroy)\n this.resources = []\n }\n}\n\nclass IntervalsManager extends ResourceManager<\n number,\n Parameters<typeof setInterval>\n> {\n create(args: Parameters<typeof setInterval>) {\n // TODO: use the edge runtime provided `setInterval` instead\n return webSetIntervalPolyfill(...args)\n }\n\n destroy(interval: number) {\n clearInterval(interval)\n }\n}\n\nclass TimeoutsManager extends ResourceManager<\n number,\n Parameters<typeof setTimeout>\n> {\n create(args: Parameters<typeof setTimeout>) {\n // TODO: use the edge runtime provided `setTimeout` instead\n return webSetTimeoutPolyfill(...args)\n }\n\n destroy(timeout: number) {\n clearTimeout(timeout)\n }\n}\n\nfunction webSetIntervalPolyfill<TArgs extends any[]>(\n callback: (...args: TArgs) => void,\n ms?: number,\n ...args: TArgs\n): number {\n return setInterval(() => {\n // node's `setInterval` sets `this` to the `Timeout` instance it returned,\n // but web `setInterval` always sets `this` to `window`\n // see: https://developer.mozilla.org/en-US/docs/Web/API/Window/setInterval#the_this_problem\n return callback.apply(globalThis, args)\n }, ms)[Symbol.toPrimitive]()\n}\n\nfunction webSetTimeoutPolyfill<TArgs extends any[]>(\n callback: (...args: TArgs) => void,\n ms?: number,\n ...args: TArgs\n): number {\n const wrappedCallback = () => {\n try {\n // node's `setTimeout` sets `this` to the `Timeout` instance it returned,\n // but web `setTimeout` always sets `this` to `window`\n // see: https://developer.mozilla.org/en-US/docs/Web/API/Window/setTimeout#the_this_problem\n return callback.apply(globalThis, args)\n } finally {\n // On certain older node versions (<20.16.0, <22.4.0),\n // a `setTimeout` whose Timeout was converted to a primitive will leak.\n // See: https://github.com/nodejs/node/issues/53335\n // We can work around this by explicitly calling `clearTimeout` after the callback runs.\n clearTimeout(timeout)\n }\n }\n const timeout = setTimeout(wrappedCallback, ms)\n return timeout[Symbol.toPrimitive]()\n}\n\nexport const intervalsManager = new IntervalsManager()\nexport const timeoutsManager = new TimeoutsManager()\n"],"names":["intervalsManager","timeoutsManager","ResourceManager","add","resourceArgs","resource","create","resources","push","remove","filter","r","destroy","removeAll","forEach","IntervalsManager","args","webSetIntervalPolyfill","interval","clearInterval","TimeoutsManager","webSetTimeoutPolyfill","timeout","clearTimeout","callback","ms","setInterval","apply","globalThis","Symbol","toPrimitive","wrappedCallback","setTimeout"],"mappings":";;;;;;;;;;;;;;;IAuFaA,gBAAgB;eAAhBA;;IACAC,eAAe;eAAfA;;;AAxFb,MAAeC;IAMbC,IAAIC,YAAkB,EAAE;QACtB,MAAMC,WAAW,IAAI,CAACC,MAAM,CAACF;QAC7B,IAAI,CAACG,SAAS,CAACC,IAAI,CAACH;QACpB,OAAOA;IACT;IAEAI,OAAOJ,QAAW,EAAE;QAClB,IAAI,CAACE,SAAS,GAAG,IAAI,CAACA,SAAS,CAACG,MAAM,CAAC,CAACC,IAAMA,MAAMN;QACpD,IAAI,CAACO,OAAO,CAACP;IACf;IAEAQ,YAAY;QACV,IAAI,CAACN,SAAS,CAACO,OAAO,CAAC,IAAI,CAACF,OAAO;QACnC,IAAI,CAACL,SAAS,GAAG,EAAE;IACrB;;aAnBQA,YAAiB,EAAE;;AAoB7B;AAEA,MAAMQ,yBAAyBb;IAI7BI,OAAOU,IAAoC,EAAE;QAC3C,4DAA4D;QAC5D,OAAOC,0BAA0BD;IACnC;IAEAJ,QAAQM,QAAgB,EAAE;QACxBC,cAAcD;IAChB;AACF;AAEA,MAAME,wBAAwBlB;IAI5BI,OAAOU,IAAmC,EAAE;QAC1C,2DAA2D;QAC3D,OAAOK,yBAAyBL;IAClC;IAEAJ,QAAQU,OAAe,EAAE;QACvBC,aAAaD;IACf;AACF;AAEA,SAASL,uBACPO,QAAkC,EAClCC,EAAW,EACX,GAAGT,IAAW;IAEd,OAAOU,YAAY;QACjB,0EAA0E;QAC1E,uDAAuD;QACvD,4FAA4F;QAC5F,OAAOF,SAASG,KAAK,CAACC,YAAYZ;IACpC,GAAGS,GAAG,CAACI,OAAOC,WAAW,CAAC;AAC5B;AAEA,SAAST,sBACPG,QAAkC,EAClCC,EAAW,EACX,GAAGT,IAAW;IAEd,MAAMe,kBAAkB;QACtB,IAAI;YACF,yEAAyE;YACzE,sDAAsD;YACtD,2FAA2F;YAC3F,OAAOP,SAASG,KAAK,CAACC,YAAYZ;QACpC,SAAU;YACR,sDAAsD;YACtD,uEAAuE;YACvE,mDAAmD;YACnD,wFAAwF;YACxFO,aAAaD;QACf;IACF;IACA,MAAMA,UAAUU,WAAWD,iBAAiBN;IAC5C,OAAOH,OAAO,CAACO,OAAOC,WAAW,CAAC;AACpC;AAEO,MAAM9B,mBAAmB,IAAIe;AAC7B,MAAMd,kBAAkB,IAAImB","ignoreList":[0]}
+22
View File
@@ -0,0 +1,22 @@
import type { NodejsRequestData, FetchEventResult } from '../types';
import type { EdgeFunctionDefinition } from '../../../build/webpack/plugins/middleware-plugin';
import type { EdgeRuntime } from 'next/dist/compiled/edge-runtime';
import type { ServerComponentsHmrCache } from '../../response-cache';
export declare const ErrorSource: unique symbol;
interface RunnerFnParams {
name: string;
onError?: (err: unknown) => void;
onWarning?: (warn: Error) => void;
paths: string[];
request: NodejsRequestData;
useCache: boolean;
edgeFunctionEntry: Pick<EdgeFunctionDefinition, 'assets' | 'wasm' | 'env'>;
distDir: string;
incrementalCache?: any;
serverComponentsHmrCache?: ServerComponentsHmrCache;
clientAssetToken: string;
}
type RunnerFn = (params: RunnerFnParams) => Promise<FetchEventResult>;
export declare function getRuntimeContext(params: Omit<RunnerFnParams, 'request'>): Promise<EdgeRuntime<any>>;
export declare const run: RunnerFn;
export {};
+137
View File
@@ -0,0 +1,137 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
0 && (module.exports = {
ErrorSource: null,
getRuntimeContext: null,
run: null
});
function _export(target, all) {
for(var name in all)Object.defineProperty(target, name, {
enumerable: true,
get: all[name]
});
}
_export(exports, {
ErrorSource: function() {
return ErrorSource;
},
getRuntimeContext: function() {
return getRuntimeContext;
},
run: function() {
return run;
}
});
const _context = require("./context");
const _bodystreams = require("../../body-streams");
const _builtinrequestcontext = require("../../after/builtin-request-context");
const _routerservercontext = require("../../lib/router-utils/router-server-context");
const ErrorSource = Symbol('SandboxError');
const FORBIDDEN_HEADERS = [
'content-length',
'content-encoding',
'transfer-encoding'
];
/**
* Decorates the runner function making sure all errors it can produce are
* tagged with `edge-server` so they can properly be rendered in dev.
*/ function withTaggedErrors(fn) {
if (process.env.NODE_ENV === 'development') {
const { getServerError } = require('../../dev/node-stack-frames');
return (params)=>fn(params).then((result)=>{
var _result_waitUntil;
return {
...result,
waitUntil: result == null ? void 0 : (_result_waitUntil = result.waitUntil) == null ? void 0 : _result_waitUntil.catch((error)=>{
// TODO: used COMPILER_NAMES.edgeServer instead. Verify that it does not increase the runtime size.
throw getServerError(error, 'edge-server');
})
};
}).catch((error)=>{
// TODO: used COMPILER_NAMES.edgeServer instead
throw getServerError(error, 'edge-server');
});
}
return fn;
}
async function getRuntimeContext(params) {
const { runtime, evaluateInContext } = await (0, _context.getModuleContext)({
moduleName: params.name,
onWarning: params.onWarning ?? (()=>{}),
onError: params.onError ?? (()=>{}),
useCache: params.useCache !== false,
edgeFunctionEntry: params.edgeFunctionEntry,
distDir: params.distDir
});
if (params.incrementalCache) {
runtime.context.globalThis.__incrementalCacheShared = true;
runtime.context.globalThis.__incrementalCache = params.incrementalCache;
}
// expose router server context for access to dev handlers like
// logErrorWithOriginalStack
;
runtime.context.globalThis[_routerservercontext.RouterServerContextSymbol] = _routerservercontext.routerServerGlobal[_routerservercontext.RouterServerContextSymbol];
if (params.serverComponentsHmrCache) {
runtime.context.globalThis.__serverComponentsHmrCache = params.serverComponentsHmrCache;
}
if (params.clientAssetToken) {
runtime.context.globalThis.NEXT_CLIENT_ASSET_SUFFIX = params.clientAssetToken ? `?dpl=${params.clientAssetToken}` : '';
}
for (const paramPath of params.paths){
evaluateInContext(paramPath);
}
return runtime;
}
const run = withTaggedErrors(async function runWithTaggedErrors(params) {
var _params_request_body;
const runtime = await getRuntimeContext(params);
const edgeFunction = (await runtime.context._ENTRIES[`middleware_${params.name}`]).default;
const cloned = ![
'HEAD',
'GET'
].includes(params.request.method) ? (_params_request_body = params.request.body) == null ? void 0 : _params_request_body.cloneBodyStream() : undefined;
const KUint8Array = runtime.evaluate('Uint8Array');
const urlInstance = new URL(params.request.url);
params.request.url = urlInstance.toString();
const headers = new Headers();
for (const [key, value] of Object.entries(params.request.headers)){
headers.set(key, (value == null ? void 0 : value.toString()) ?? '');
}
try {
let result = undefined;
const builtinRequestCtx = {
...(0, _builtinrequestcontext.getBuiltinRequestContext)(),
// FIXME(after):
// arguably, this is an abuse of "@next/request-context" --
// it'd make more sense to simply forward its existing value into the sandbox (in `createModuleContext`)
// but here we're using it to just pass in `waitUntil` regardless if we were running in this context or not.
waitUntil: params.request.waitUntil
};
await _context.edgeSandboxNextRequestContext.run(builtinRequestCtx, ()=>_context.requestStore.run({
headers
}, async ()=>{
result = await edgeFunction({
request: {
...params.request,
body: cloned && (0, _bodystreams.requestToBodyStream)(runtime.context, KUint8Array, cloned)
}
});
for (const headerName of FORBIDDEN_HEADERS){
result.response.headers.delete(headerName);
}
}));
if (!result) throw Object.defineProperty(new Error('Edge function did not return a response'), "__NEXT_ERROR_CODE", {
value: "E332",
enumerable: false,
configurable: true
});
return result;
} finally{
var _params_request_body1;
await ((_params_request_body1 = params.request.body) == null ? void 0 : _params_request_body1.finalize());
}
});
//# sourceMappingURL=sandbox.js.map
File diff suppressed because one or more lines are too long