Something went wrong. Try again.
Monorepo for the nibling.run platform
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120// node:vm. Script/runInThisContext/compileFunction run through the same indirect-eval// compile path the CJS loader uses (modules.ts) — the kernel's CSP already allows// unsafe-eval for that. A separate V8 context is the part that cannot work: a worker// has no iframe, and ShadowRealm — the one web primitive shaped like a new realm — is// not available (checked below; Node itself doesn't have it either). createContext and// anything that runs code *in* one refuse by name instead of silently running in this// realm, which would be a correctness lie (globals, prototypes, instanceof would all// leak across the "context" boundary a guest asked to be isolated from).import type { RequireContext } from "./modules";import { unsupported } from "./refuse";
const NO_REALM = "a worker has no iframe, and ShadowRealm (the one web API shaped like a " + "new realm) is not available here; without a second realm there is nowhere to run this";
interface ScriptOptions { filename?: string; lineOffset?: number; columnOffset?: number; timeout?: number }interface RunOptions { timeout?: number; displayErrors?: boolean; breakOnSigint?: boolean }
export function makeVm(_ctx: RequireContext) { class Script { code: string; filename: string; cachedDataRejected = undefined;
constructor(code: string, options: ScriptOptions = {}) { this.code = String(code); this.filename = options.filename ?? "evalmachine.<anonymous>"; // lineOffset/columnOffset shift stack traces to match a caller's own file; // without a real position-mapped eval there is nothing to shift them against, // so a non-zero offset would just be wrong rather than ignored silently. if (options.lineOffset || options.columnOffset) { throw unsupported("vm", "new Script() with lineOffset/columnOffset", "stack traces here are the compiled function's own, not remapped"); } }
runInThisContext(options: RunOptions = {}): unknown { if (options.timeout !== undefined) { throw unsupported("vm", "Script#runInThisContext({ timeout })", "nothing here can interrupt a running script — no isolate control from inside a worker"); } // A script's result is its last statement's completion value, exactly as a // top-level eval() gives it — unlike compileFunction below, this must not be // wrapped in a function body (that would need an explicit `return` the // caller's source never wrote). const indirectEval = eval; return indirectEval(`${this.code}\n//# sourceURL=${this.filename}`); }
runInContext(): never { throw unsupported("vm", "Script#runInContext()", NO_REALM); }
runInNewContext(): never { throw unsupported("vm", "Script#runInNewContext()", NO_REALM); }
createCachedData(): never { throw unsupported("vm", "Script#createCachedData()", "no V8 code-cache API is exposed to a worker"); } }
function createScript(code: string, options?: ScriptOptions): Script { return new Script(code, options); }
function runInThisContext(code: string, options?: ScriptOptions & RunOptions): unknown { return new Script(code, options).runInThisContext(options); }
function createContext(): never { throw unsupported("vm", "createContext()", NO_REALM); } function runInContext(): never { throw unsupported("vm", "runInContext()", NO_REALM); } function runInNewContext(): never { throw unsupported("vm", "runInNewContext()", NO_REALM); } function isContext(_object: unknown): boolean { return false; // nothing produced here is ever a contextified object }
function compileFunction(code: string, params: string[] = [], options: { filename?: string; parsingContext?: unknown; contextExtensions?: unknown[] } = {}): (...args: unknown[]) => unknown { if (options.parsingContext !== undefined) throw unsupported("vm", "compileFunction({ parsingContext })", NO_REALM); if (options.contextExtensions?.length) throw unsupported("vm", "compileFunction({ contextExtensions })", NO_REALM); const indirectEval = eval; const filename = options.filename ?? ""; return indirectEval(`(function anonymous(${params.join(", ")}\n) {\n${code}\n})\n//# sourceURL=${filename}`); }
// Node's vm.measureMemory samples V8's per-context heap, which only exists because // each vm context is a real V8 context; here it would just be this worker's own // heap. The web has an honest equivalent — performance.measureUserAgentSpecificMemory // — gated behind cross-origin isolation (COOP+COEP), so it's used when present // instead of inventing a number. async function measureMemory(): Promise<unknown> { const measure = (globalThis.performance as { measureUserAgentSpecificMemory?: () => Promise<{ bytes: number; breakdown: unknown[] }> }) .measureUserAgentSpecificMemory; if (!measure) { throw unsupported("vm", "measureMemory()", "performance.measureUserAgentSpecificMemory is unavailable (needs cross-origin isolation: COOP+COEP)"); } const sample = await measure.call(globalThis.performance); return { total: { jsMemoryEstimate: sample.bytes, jsMemoryRange: [sample.bytes, sample.bytes] } }; }
return { Script, createScript, runInThisContext, createContext, runInContext, runInNewContext, isContext, compileFunction, measureMemory, constants: { USE_MAIN_CONTEXT_DEFAULT_LOADER: Symbol("vm_dynamic_import_main_context_default"), DONT_CONTEXTIFY: Symbol("vm_context_no_contextify"), }, };}