export interface JavaScriptResult {
value: string;
logs: string[];
durationMs: number;
}
const MAX_SOURCE_BYTES = 64 * 1024;
const MAX_RESULT_CHARACTERS = 16 * 1024;
const OPAQUE_WORKER_SOURCE = `
(() => {
const send = self.postMessage.bind(self);
const render = (value) => {
if (typeof value === 'string') return value;
try { return JSON.stringify(value); } catch { return String(value); }
};
const blocked = () => Promise.reject(new Error('Network access is disabled in js_eval'));
const hide = (target, key, value) => {
try { Object.defineProperty(target, key, { value, writable: false, configurable: false }); } catch {}
};
hide(self, 'fetch', blocked);
hide(self, 'XMLHttpRequest', undefined);
hide(self, 'WebSocket', undefined);
hide(self, 'EventSource', undefined);
hide(self, 'SharedWorker', undefined);
hide(self, 'Worker', undefined);
hide(self, 'BroadcastChannel', undefined);
hide(self, 'importScripts', undefined);
hide(self, 'indexedDB', undefined);
hide(self, 'caches', undefined);
hide(self.navigator, 'storage', undefined);
hide(self, 'postMessage', undefined);
hide(self, 'close', undefined);
self.onmessage = async ({ data }) => {
const logs = [];
const capture = (...values) => logs.push(values.map(render).join(' '));
const console = { log: capture, info: capture, warn: capture, error: capture };
try {
const AsyncFunction = Object.getPrototypeOf(async function () {}).constructor;
const execute = new AsyncFunction('console', '"use strict";\\n' + data.source);
const value = await execute(console);
send({ ok: true, value: render(value), logs });
} catch (error) {
send({ ok: false, error: error?.message ?? String(error), logs });
}
};
})();
`;
function frameSource(channel: string): string {
const policy = [
"default-src 'none'",
"base-uri 'none'",
"connect-src 'none'",
"form-action 'none'",
"frame-src 'none'",
"img-src 'none'",
"media-src 'none'",
"object-src 'none'",
"style-src 'none'",
"script-src 'unsafe-inline' 'unsafe-eval' blob:",
'worker-src blob:',
].join('; ');
return `