Files
bruno/packages/bruno-js/src/sandbox/quickjs/index.js
2024-08-19 11:59:43 +05:30

123 lines
3.7 KiB
JavaScript

const addBruShimToContext = require('./shims/bru');
const addBrunoRequestShimToContext = require('./shims/bruno-request');
const addConsoleShimToContext = require('./shims/console');
const addBrunoResponseShimToContext = require('./shims/bruno-response');
const addTestShimToContext = require('./shims/test');
const addLibraryShimsToContext = require('./shims/lib');
const { newQuickJSWASMModule, memoizePromiseFactory } = require('quickjs-emscripten');
// execute `npm run sandbox:bundle-libraries` if the below file doesn't exist
const getBundledCode = require('../bundle-browser-rollup');
const addSleepShimToContext = require('./shims/sleep');
let QuickJSSyncContext;
const loader = memoizePromiseFactory(() => newQuickJSWASMModule());
const getContext = (opts) => loader().then((mod) => (QuickJSSyncContext = mod.newContext(opts)));
getContext();
const toNumber = (value) => {
const num = Number(value);
return Number.isInteger(num) ? parseInt(value, 10) : parseFloat(value);
};
const executeQuickJsVm = ({ script: externalScript, context: externalContext, scriptType = 'template-literal' }) => {
if (!isNaN(Number(externalScript))) {
return Number(externalScript);
}
const vm = QuickJSSyncContext;
try {
const { bru, req, res } = externalContext;
bru && addBruShimToContext(vm, bru);
req && addBrunoRequestShimToContext(vm, req);
res && addBrunoResponseShimToContext(vm, res);
const templateLiteralText = `\`${externalScript}\`;`;
const jsExpressionText = `${externalScript};`;
let scriptText = scriptType === 'template-literal' ? templateLiteralText : jsExpressionText;
const result = vm.evalCode(scriptText);
if (result.error) {
let e = vm.dump(result.error);
result.error.dispose();
return e;
} else {
let v = vm.dump(result.value);
let vString = v.toString();
result.value.dispose();
return v;
}
} catch (error) {
console.error('Error executing the script!', error);
}
};
const executeQuickJsVmAsync = async ({ script: externalScript, context: externalContext }) => {
if (!isNaN(Number(externalScript))) {
return toNumber(externalScript);
}
try {
const module = await newQuickJSWASMModule();
const vm = module.newContext();
const bundledCode = getBundledCode?.toString() || '';
vm.evalCode(
`
(${bundledCode})()
globalThis.require = (module) => {
return globalThis.requireObject[module];
}
`
);
const { bru, req, res, test, __brunoTestResults, console: consoleFn } = externalContext;
bru && addBruShimToContext(vm, bru);
req && addBrunoRequestShimToContext(vm, req);
res && addBrunoResponseShimToContext(vm, res);
consoleFn && addConsoleShimToContext(vm, consoleFn);
addSleepShimToContext(vm);
await addLibraryShimsToContext(vm);
test && __brunoTestResults && addTestShimToContext(vm, __brunoTestResults);
const script = `
(async () => {
const setTimeout = async(fn, timer) => {
v = await sleep(timer);
fn.apply();
}
await sleep(0);
try {
${externalScript}
}
catch(error) {
console?.debug?.('quick-js:execution-end:with-error', error?.message);
}
return 'done';
})()
`;
const result = vm.evalCode(script);
const promiseHandle = vm.unwrapResult(result);
const resolvedResult = await vm.resolvePromise(promiseHandle);
promiseHandle.dispose();
const resolvedHandle = vm.unwrapResult(resolvedResult);
resolvedHandle.dispose();
// vm.dispose();
return;
} catch (error) {
console.error('Error executing the script!', error);
}
};
module.exports = {
executeQuickJsVm,
executeQuickJsVmAsync
};