1
0
silverbullet/packages/plugos/environments/node_worker.ts

151 lines
4.0 KiB
TypeScript
Raw Normal View History

2022-05-09 12:59:12 +00:00
import { ConsoleLogger } from "./custom_logger";
2022-04-25 08:33:38 +00:00
const {
parentPort,
2022-05-13 12:36:26 +00:00
workerData: { nodeModulesPath },
2022-04-25 08:33:38 +00:00
} = require("worker_threads");
2022-04-25 08:33:38 +00:00
const { VM, VMScript } = require(`${nodeModulesPath}/vm2`);
let loadedFunctions = new Map<string, Function>();
let pendingRequests = new Map<
2022-03-21 14:21:34 +00:00
number,
{
resolve: (result: unknown) => void;
reject: (e: any) => void;
}
>();
2022-03-24 09:48:56 +00:00
let syscallReqId = 0;
2022-05-09 12:59:12 +00:00
let consoleLogger = new ConsoleLogger((level, message) => {
parentPort.postMessage({
type: "log",
level,
message,
});
}, false);
2022-05-13 12:36:26 +00:00
let loadedModules = new Map<string, any>();
let vm = new VM({
sandbox: {
// Exposing some "safe" APIs
2022-05-09 12:59:12 +00:00
console: consoleLogger,
setTimeout,
clearTimeout,
setInterval,
clearInterval,
2022-04-25 08:33:38 +00:00
fetch: require(`${nodeModulesPath}/node-fetch`),
WebSocket: require(`${nodeModulesPath}/ws`),
// This is only going to be called for pre-bundled modules, we won't allow
// arbitrary requiring of modules
require: (moduleName: string): any => {
2022-05-13 12:36:26 +00:00
// console.log("Loading module", moduleName);
// if (preloadedModules.includes(moduleName)) {
// return require(`${nodeModulesPath}/${moduleName}`);
// } else
if (loadedModules.has(moduleName)) {
let mod = loadedModules.get(moduleName);
// console.log("And it has the value", mod);
return mod;
} else {
2022-04-25 09:24:13 +00:00
throw Error(`Cannot import arbitrary modules like ${moduleName}`);
}
},
self: {
2022-03-24 09:48:56 +00:00
syscall: (name: string, ...args: any[]) => {
return new Promise((resolve, reject) => {
2022-03-24 09:48:56 +00:00
syscallReqId++;
pendingRequests.set(syscallReqId, { resolve, reject });
parentPort.postMessage({
type: "syscall",
2022-03-24 09:48:56 +00:00
id: syscallReqId,
name,
// TODO: Figure out why this is necessary (to avoide a CloneError)
args: JSON.parse(JSON.stringify(args)),
});
});
},
},
},
});
2022-03-21 14:21:34 +00:00
function wrapScript(code: string) {
return `(${code})["default"]`;
}
2022-03-21 14:21:34 +00:00
function safeRun(fn: any) {
fn().catch((e: any) => {
console.error(e);
});
}
2022-03-21 14:21:34 +00:00
parentPort.on("message", (data: any) => {
safeRun(async () => {
switch (data.type) {
case "load":
loadedFunctions.set(data.name, new VMScript(wrapScript(data.code)));
parentPort.postMessage({
type: "inited",
name: data.name,
});
break;
2022-05-13 12:36:26 +00:00
case "load-dependency":
// console.log("Asked to load dep", data.name);
try {
let r = vm.run(data.code);
// console.log("Loaded dependency", r);
loadedModules.set(data.name, r);
parentPort.postMessage({
type: "dependency-inited",
name: data.name,
});
} catch (e: any) {
console.error("Could not load dependency", e.message);
}
break;
case "invoke":
let fn = loadedFunctions.get(data.name);
if (!fn) {
throw new Error(`Function not loaded: ${data.name}`);
}
try {
let r = vm.run(fn);
let result = await Promise.resolve(r(...data.args));
parentPort.postMessage({
type: "result",
id: data.id,
2022-03-21 14:21:34 +00:00
// TOOD: Figure out if this is necessary, because it's expensive
result: result && JSON.parse(JSON.stringify(result)),
});
2022-03-21 14:21:34 +00:00
} catch (e: any) {
parentPort.postMessage({
type: "result",
id: data.id,
error: e.message,
});
}
break;
case "syscall-response":
let syscallId = data.id;
const lookup = pendingRequests.get(syscallId);
if (!lookup) {
throw Error("Invalid request id");
}
pendingRequests.delete(syscallId);
if (data.error) {
2022-03-28 13:25:05 +00:00
console.log("Got rejection", data.error);
lookup.reject(new Error(data.error));
} else {
lookup.resolve(data.result);
}
break;
}
});
});
2022-05-11 18:10:45 +00:00
process.on("uncaughtException", (e) => {
console.error("Uncaught error", e);
});