Files
start-os/libs/js_engine/src/artifacts/loadModule.js
BluJ 9366dbb96e wip: Starting down the bind for the effects
todo: complete a ip todo

chore: Fix the result type on something

todo: Address returning

chore: JS with callbacks

chore: Add in the chown and permissions

chore: Add in the binds and unbinds in
2023-07-06 15:07:53 -06:00

372 lines
9.4 KiB
JavaScript

import Deno from "/deno_global.js";
import * as mainModule from "/embassy.js";
// throw new Error("I'm going crasy")
function requireParam(param) {
throw new Error(`Missing required parameter ${param}`);
}
const callbackName = (() => {
let count = 0;
return () => `callback${count++}${Math.floor(Math.random() * 100000)}`;
})();
const callbackMapping = {};
const registerCallback = (fn) => {
const uuid = callbackName(); // TODO
callbackMapping[uuid] = fn;
return uuid;
};
/**
* This is using the simplified json pointer spec, using no escapes and arrays
* @param {object} obj
* @param {string} pointer
* @returns
*/
function jsonPointerValue(obj, pointer) {
const paths = pointer.substring(1).split("/");
for (const path of paths) {
if (obj == null) {
return null;
}
obj = (obj || {})[path];
}
return obj;
}
function maybeDate(value) {
if (!value) return value;
return new Date(value);
}
const writeFile = (
{
path = requireParam("path"),
volumeId = requireParam("volumeId"),
toWrite = requireParam("toWrite"),
} = requireParam("options"),
) => Deno.core.opAsync("write_file", volumeId, path, toWrite);
const readFile = (
{
volumeId = requireParam("volumeId"),
path = requireParam("path"),
} = requireParam("options"),
) => Deno.core.opAsync("read_file", volumeId, path);
const runDaemon = (
{ command = requireParam("command"), args = [] } = requireParam("options"),
) => {
let id = Deno.core.opAsync("start_command", command, args, "inherit", null);
let processId = id.then((x) => x.processId);
let waitPromise = null;
return {
processId,
async wait() {
waitPromise = waitPromise ||
Deno.core.opAsync("wait_command", await processId);
return waitPromise;
},
async term(signal = 15) {
return Deno.core.opAsync("send_signal", await processId, 15);
},
};
};
const runCommand = async (
{
command = requireParam("command"),
args = [],
timeoutMillis = 30000,
} = requireParam("options"),
) => {
let id = Deno.core.opAsync(
"start_command",
command,
args,
"collect",
timeoutMillis,
);
let pid = id.then((x) => x.processId);
return Deno.core.opAsync("wait_command", await pid);
};
const bindLocal = async (
{
internalPort = requireParam("internalPort"),
name = requireParam("name"),
externalPort = requireParam("externalPort"),
} = requireParam("options"),
) => {
return Deno.core.opAsync("bind_local", internalPort, { name, externalPort });
};
const bindTor = async (
{
internalPort = requireParam("internalPort"),
name = requireParam("name"),
externalPort = requireParam("externalPort"),
} = requireParam("options"),
) => {
return Deno.core.opAsync("bind_onion", internalPort, { name, externalPort });
};
const signalGroup = async (
{ gid = requireParam("gid"), signal = requireParam("signal") } = requireParam(
"gid and signal",
),
) => {
return Deno.core.opAsync("signal_group", gid, signal);
};
const sleep = (timeMs = requireParam("timeMs")) =>
Deno.core.opAsync("sleep", timeMs);
const rename = (
{
srcVolume = requireParam("srcVolume"),
dstVolume = requirePapram("dstVolume"),
srcPath = requireParam("srcPath"),
dstPath = requireParam("dstPath"),
} = requireParam("options"),
) => Deno.core.opAsync("rename", srcVolume, srcPath, dstVolume, dstPath);
const metadata = async (
{
volumeId = requireParam("volumeId"),
path = requireParam("path"),
} = requireParam("options"),
) => {
const data = await Deno.core.opAsync("metadata", volumeId, path);
return {
...data,
modified: maybeDate(data.modified),
created: maybeDate(data.created),
accessed: maybeDate(data.accessed),
};
};
const removeFile = (
{
volumeId = requireParam("volumeId"),
path = requireParam("path"),
} = requireParam("options"),
) => Deno.core.opAsync("remove_file", volumeId, path);
const isSandboxed = () => Deno.core.opSync("is_sandboxed");
const writeJsonFile = (
{
volumeId = requireParam("volumeId"),
path = requireParam("path"),
toWrite = requireParam("toWrite"),
} = requireParam("options"),
) =>
writeFile({
volumeId,
path,
toWrite: JSON.stringify(toWrite),
});
const chown = async (
{
volumeId = requireParam("volumeId"),
path = requireParam("path"),
uid = requireParam("uid"),
} = requireParam("options"),
) => {
return await Deno.core.opAsync("chown", volumeId, path, uid);
};
const chmod = async (
{
volumeId = requireParam("volumeId"),
path = requireParam("path"),
mode = requireParam("mode"),
} = requireParam("options"),
) => {
return await Deno.core.opAsync("chmod", volumeId, path, mode);
};
const readJsonFile = async (
{
volumeId = requireParam("volumeId"),
path = requireParam("path"),
} = requireParam("options"),
) => JSON.parse(await readFile({ volumeId, path }));
const createDir = (
{
volumeId = requireParam("volumeId"),
path = requireParam("path"),
} = requireParam("options"),
) => Deno.core.opAsync("create_dir", volumeId, path);
const readDir = (
{ volumeId = requireParam("volumeId"), path = requireParam("path") } = requireParam("options"),
) => Deno.core.opAsync("read_dir", volumeId, path);
const removeDir = (
{
volumeId = requireParam("volumeId"),
path = requireParam("path"),
} = requireParam("options"),
) => Deno.core.opAsync("remove_dir", volumeId, path);
const trace = (whatToTrace = requireParam("whatToTrace")) =>
Deno.core.opAsync("log_trace", whatToTrace);
const warn = (whatToTrace = requireParam("whatToTrace")) =>
Deno.core.opAsync("log_warn", whatToTrace);
const error = (whatToTrace = requireParam("whatToTrace")) =>
Deno.core.opAsync("log_error", whatToTrace);
const debug = (whatToTrace = requireParam("whatToTrace")) =>
Deno.core.opAsync("log_debug", whatToTrace);
const info = (whatToTrace = requireParam("whatToTrace")) =>
Deno.core.opAsync("log_info", whatToTrace);
const fetch = async (url = requireParam("url"), options = null) => {
const { body, ...response } = await Deno.core.opAsync("fetch", url, options);
const textValue = Promise.resolve(body);
return {
...response,
text() {
return textValue;
},
json() {
return textValue.then((x) => JSON.parse(x));
},
};
};
const runRsync = (
{
srcVolume = requireParam("srcVolume"),
dstVolume = requireParam("dstVolume"),
srcPath = requireParam("srcPath"),
dstPath = requireParam("dstPath"),
options = requireParam("options"),
} = requireParam("options"),
) => {
let id = Deno.core.opAsync(
"rsync",
srcVolume,
srcPath,
dstVolume,
dstPath,
options,
);
let waitPromise = null;
return {
async id() {
return id;
},
async wait() {
waitPromise = waitPromise || Deno.core.opAsync("rsync_wait", await id);
return waitPromise;
},
async progress() {
return Deno.core.opAsync("rsync_progress", await id);
},
};
};
const diskUsage = async ({
volumeId = requireParam("volumeId"),
path = requireParam("path"),
} = { volumeId: null, path: null }) => {
const [used, total] = await Deno.core.opAsync("disk_usage", volumeId, path);
return { used, total }
}
globalThis.runCallback = (uuid, data) => callbackMapping[uuid](data);
// window.runCallback = runCallback;
// Deno.runCallback = runCallback;
const getServiceConfig = async (
{
serviceId = requireParam("serviceId"),
configPath = requireParam("configPath"),
onChange = requireParam("onChange"),
} = requireParam("options"),
) => {
return await Deno.core.opAsync(
"get_service_config",
serviceId,
configPath,
registerCallback(onChange),
);
};
const setPermissions = async (
{
volumeId = requireParam("volumeId"),
path = requireParam("path"),
readonly = requireParam("readonly"),
} = requireParam("options"),
) => {
return await Deno.core.opAsync("set_permissions", volumeId, path, readonly);
};
const currentFunction = Deno.core.opSync("current_function");
const input = Deno.core.opSync("get_input");
const variable_args = Deno.core.opSync("get_variable_args");
const setState = (x) => Deno.core.opAsync("set_value", x);
const effects = {
bindLocal,
bindTor,
chmod,
chown,
createDir,
debug,
diskUsage,
error,
fetch,
getServiceConfig,
getServiceConfig,
info,
isSandboxed,
metadata,
readDir,
readFile,
readJsonFile,
removeDir,
removeFile,
rename,
runCommand,
runDaemon,
runRsync,
setPermissions,
signalGroup,
sleep,
trace,
warn,
writeFile,
writeJsonFile,
};
const defaults = {
handleSignal: (effects, { gid, signal }) => {
return effects.signalGroup({ gid, signal });
},
};
function safeToString(fn, orValue = "") {
try {
return fn();
} catch (e) {
return orValue;
}
}
const runFunction = jsonPointerValue(mainModule, currentFunction) ||
jsonPointerValue(defaults, currentFunction);
(async () => {
const answer = await (async () => {
if (typeof runFunction !== "function") {
error(`Expecting ${currentFunction} to be a function`);
throw new Error(`Expecting ${currentFunction} to be a function`);
}
})()
.then(() => runFunction(effects, input, ...variable_args))
.catch((e) => {
if ("error" in e) return e;
if ("error-code" in e) return e;
return {
error: safeToString(
() => e.toString(),
"Error Not able to be stringified",
),
};
});
await setState(answer);
})();