UNPKG

mcard-js

Version:

MCard - Content-addressable storage with cryptographic hashing, handle resolution, and vector search for Node.js and browsers

125 lines (124 loc) 4.29 kB
/** * JavaScript runtime executor. * * Executes JavaScript code in VM sandbox or subprocess for module/import support. */ import * as fs from 'fs'; import * as path from 'path'; import * as vm from 'vm'; import * as child_process from 'child_process'; import * as util from 'util'; import { execFile, parseOutput } from './base.js'; import { findProjectRoot } from '../FileSystemUtils.js'; const writeFile = util.promisify(fs.writeFile); const unlink = util.promisify(fs.unlink); export class JavaScriptRuntime { async execute(code, context, config) { // Detect if code needs Node.js features not available in VM sandbox if (this.requiresSubprocess(code)) { return this.executeSubprocess(code, context); } return this.executeInVM(code, context); } /** * Check if code requires subprocess execution (has imports/exports). */ requiresSubprocess(code) { return (code.includes('require(') || code.includes('import(') || code.includes('import ') || code.includes('export ')); } /** * Execute code in VM sandbox (fast, for simple scripts). */ async executeInVM(code, context) { const sandbox = { context, target: context, result: undefined, console: { log: (...args) => console.log('[JS]', ...args), error: (...args) => console.error('[JS]', ...args), warn: (...args) => console.warn('[JS]', ...args), }, Math, JSON, Date, setTimeout, clearTimeout, setInterval, clearInterval, process: process, spawn: child_process.spawn, exec: child_process.exec, // Network capabilities fetch: global.fetch ? global.fetch.bind(global) : undefined, Headers: global.Headers, Request: global.Request, Response: global.Response, URL: global.URL, URLSearchParams: global.URLSearchParams, }; const codeRunnable = code + '\nresult;'; const script = new vm.Script(codeRunnable); const vmContext = vm.createContext(sandbox); const executionResult = script.runInContext(vmContext, { timeout: 5000 }); return sandbox.result !== undefined ? sandbox.result : executionResult; } /** * Execute code in subprocess (for modules/imports). */ async executeSubprocess(code, context) { const contextStr = JSON.stringify(context); const projectRoot = findProjectRoot(); const wrapper = ` const process = require('process'); const context = JSON.parse(process.argv[2]); global.context = context; let result; (async () => { try { ${code} console.log(JSON.stringify(result)); } catch (e) { console.error(JSON.stringify({ error: e.message, stack: e.stack })); process.exit(1); } })(); `; const tmpFile = path.resolve(projectRoot, `tmp_js_${Date.now()}.js`); await writeFile(tmpFile, wrapper); try { const { stdout, stderr } = await execFile('node', [tmpFile, contextStr], { cwd: projectRoot, env: { ...process.env } }); if (stderr && stderr.trim()) { try { const errObj = JSON.parse(stderr.trim()); if (errObj.error) throw new Error(errObj.error); } catch (e) { // Not JSON, ignore warnings } } return parseOutput(stdout); } catch (error) { const stderr = error.stderr ? `\nStderr: ${error.stderr.toString()}` : ''; try { const errObj = JSON.parse(error.stderr?.toString().trim() || ''); if (errObj.error) throw new Error(errObj.error); } catch { } throw new Error(`JavaScript execution failed: ${error.message}${stderr}`); } finally { await unlink(tmpFile); } } } //# sourceMappingURL=javascript.js.map