import { executeCode, RuntimeError } from "numbl"; import type { RuntimeValue } from "numbl"; import type { RunRequest, RunResult, BackendInfo } from "./types"; import { toPlain, toRuntime } from "./convert"; const OPTIMIZATION = "1" as const; declare const __NUMBL_VERSION__: string | undefined; /** * numbl's package version, injected at build time by the host's Vite config * (see vite.shared.ts); numbl's own `version` builtin reports the MATLAB * release it emulates, which is not what "which backend ran it" means. */ export function numblVersion(): string { return typeof __NUMBL_VERSION__ === "string" ? __NUMBL_VERSION__ : "unknown"; } /** * Run a bundle synchronously in the current thread. Both the Node runner and * the worker call this, so the two hosts execute identically. */ export function executeBundle(req: RunRequest, backend: BackendInfo, onOutput?: (text: string) => void): RunResult { const { bundle } = req; const entry = bundle.files.find((f) => f.name === bundle.entry); const output: string[] = []; const t0 = performance.now(); if (!entry) { return { ok: false, error: `entry file ${bundle.entry} is not in the bundle`, output, wallMs: 0, backend }; } const workspaceFiles = bundle.files.filter((f) => f.name !== bundle.entry).map((f) => ({ name: f.name, source: f.source })); const initialVariableValues: Record = {}; for (const [name, v] of Object.entries(req.inputs ?? {})) initialVariableValues[name] = toRuntime(v); try { const result = executeCode( entry.source, { optimization: OPTIMIZATION, displayResults: false, initialVariableValues, onOutput: (text: string) => { output.push(text); onOutput?.(text); }, }, workspaceFiles, bundle.entry, ); const wallMs = performance.now() - t0; const value = result.variableValues[bundle.resultVariable]; if (value === undefined) { return { ok: false, error: `the script finished but did not define the variable "${bundle.resultVariable}"`, output, wallMs, backend, }; } return { ok: true, value: toPlain(value), output, wallMs, backend }; } catch (e) { const wallMs = performance.now() - t0; return { ok: false, error: formatError(e), output, wallMs, backend }; } } export function formatError(e: unknown): string { if (e instanceof RuntimeError) { const where = [e.file, e.line].filter((x) => x !== undefined && x !== null && x !== "").join(":"); return where ? `${where}: ${e.message}` : e.message; } if (e instanceof Error) return e.message; return String(e); }