UNPKG

@sebastianwessel/quickjs

Version:

A typescript package to execute JavaScript and TypeScript code in a WebAssembly QuickJS sandbox

81 lines (80 loc) 3.88 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.createEvalCodeFunction = void 0; const createTimeInterval_js_1 = require("../../createTimeInterval.js"); const pendingHostPromises_js_1 = require("../expose/pendingHostPromises.js"); const getMaxIntervalAmount_js_1 = require("../getMaxIntervalAmount.js"); const getMaxTimeoutAmount_js_1 = require("../getMaxTimeoutAmount.js"); const handleEvalError_js_1 = require("../handleEvalError.js"); const handleToNative_js_1 = require("../handleToNative/handleToNative.js"); const provideTimingFunctions_js_1 = require("../provide/provideTimingFunctions.js"); const createEvalCodeFunction = (input, scope) => { const { ctx, sandboxOptions, transpileFile } = input; return async (code, filename = '/src/index.js', evalOptions) => { const eventLoopinterval = (0, createTimeInterval_js_1.createTimeInterval)(() => ctx.runtime.executePendingJobs(), 0); const timeoutMs = sandboxOptions.executionTimeout ?? 0; let timeoutId; let nativePromise; let timedOut = false; const { dispose: disposeTimer } = (0, provideTimingFunctions_js_1.provideTimingFunctions)(ctx, { maxTimeoutCount: (0, getMaxTimeoutAmount_js_1.getMaxTimeoutAmount)(sandboxOptions), maxIntervalCount: (0, getMaxIntervalAmount_js_1.getMaxIntervalAmount)(sandboxOptions), }); const disposeStep = () => { if (timeoutId) { clearTimeout(timeoutId); } eventLoopinterval?.clear(); disposeTimer(); }; try { const jsCode = transpileFile(code); const evalResult = ctx.evalCode(jsCode, filename, { strict: true, strip: false, backtraceBarrier: true, ...evalOptions, type: 'module', }); const handle = scope.manage(ctx.unwrapResult(evalResult)); const native = (0, handleToNative_js_1.handleToNative)(ctx, handle, scope); nativePromise = (async () => { const res = await native; return res.default; })(); const result = await Promise.race([ nativePromise, new Promise((_resolve, reject) => { if (timeoutMs > 0) { timeoutId = setTimeout(() => { timedOut = true; const err = new Error('The script execution has exceeded the maximum allowed time limit.'); err.name = 'ExecutionTimeout'; reject(err); }, timeoutMs); } }), ]); return { ok: true, data: result }; } catch (err) { return (0, handleEvalError_js_1.handleEvalError)((0, handleEvalError_js_1.normalizeInterruptedExecution)(err, timeoutMs)); } finally { if (timedOut && nativePromise) { // Reject unresolved host-injected promises to avoid leaving QuickJS promises // pending while disposing the runtime after timeout. await (0, pendingHostPromises_js_1.rejectAndFlushPendingHostPromises)(ctx); // Force-fast interrupt handling and give pending guest jobs a short window to settle. ctx.runtime.setInterruptHandler(() => true); const cleanupWait = Math.min(Math.max(timeoutMs, 50), 1000); await Promise.race([ nativePromise.catch(() => undefined), new Promise(resolve => setTimeout(resolve, cleanupWait)), ]); } disposeStep(); } }; }; exports.createEvalCodeFunction = createEvalCodeFunction;