@sebastianwessel/quickjs
Version:
A typescript package to execute JavaScript and TypeScript code in a WebAssembly QuickJS sandbox
81 lines (80 loc) • 3.88 kB
JavaScript
;
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;