1
0
silverbullet/plugos/plug.ts

116 lines
3.5 KiB
TypeScript
Raw Normal View History

import { Manifest, RuntimeEnvironment } from "./types.ts";
import { Sandbox } from "./sandbox.ts";
import { System } from "./system.ts";
2022-10-13 13:16:18 +00:00
import { AssetBundle, AssetJson } from "./asset_bundle/bundle.ts";
2022-11-01 14:00:59 +00:00
import { resolve } from "https://deno.land/std@0.158.0/path/win32.ts";
2022-03-23 14:41:12 +00:00
export class Plug<HookT> {
system: System<HookT>;
sandbox?: Sandbox;
2022-03-23 14:41:12 +00:00
public manifest?: Manifest<HookT>;
2022-10-13 13:16:18 +00:00
public assets?: AssetBundle;
private sandboxFactory: (plug: Plug<HookT>) => Sandbox;
2022-03-23 14:41:12 +00:00
readonly runtimeEnv: RuntimeEnvironment;
2022-03-25 11:03:06 +00:00
grantedPermissions: string[] = [];
name: string;
version: number;
2022-03-23 14:41:12 +00:00
2022-03-25 11:03:06 +00:00
constructor(
system: System<HookT>,
name: string,
2022-10-12 09:47:13 +00:00
sandboxFactory: (plug: Plug<HookT>) => Sandbox,
2022-03-25 11:03:06 +00:00
) {
2022-03-23 14:41:12 +00:00
this.system = system;
2022-03-25 11:03:06 +00:00
this.name = name;
this.sandboxFactory = sandboxFactory;
// this.sandbox = sandboxFactory(this);
2022-03-23 14:41:12 +00:00
this.runtimeEnv = system.runtimeEnv;
this.version = new Date().getTime();
2022-03-23 14:41:12 +00:00
}
2022-11-01 14:00:59 +00:00
private sandboxInitialized: Promise<void> | undefined = undefined;
// Lazy load sandbox, guarantees that the sandbox is loaded
2022-11-01 14:00:59 +00:00
lazyInitSandbox(): Promise<void> {
if (this.sandboxInitialized) {
return this.sandboxInitialized;
}
this.sandboxInitialized = Promise.resolve().then(async () => {
console.log("Now starting sandbox for", this.name);
// Kick off worker
this.sandbox = this.sandboxFactory(this);
// Push in any dependencies
for (
const [dep, code] of Object.entries(this.manifest!.dependencies || {})
) {
await this.sandbox.loadDependency(dep, code);
}
await this.system.emit("sandboxInitialized", this.sandbox, this);
2022-11-01 14:00:59 +00:00
});
return this.sandboxInitialized;
}
load(manifest: Manifest<HookT>) {
2022-03-23 14:41:12 +00:00
this.manifest = manifest;
2022-10-13 13:16:18 +00:00
this.assets = new AssetBundle(
manifest.assets ? manifest.assets as AssetJson : {},
);
2022-03-25 11:03:06 +00:00
// TODO: These need to be explicitly granted, not just taken
this.grantedPermissions = manifest.requiredPermissions || [];
}
syscall(name: string, args: any[]): Promise<any> {
return this.system.syscallWithContext({ plug: this }, name, args);
2022-03-23 14:41:12 +00:00
}
canInvoke(name: string) {
if (!this.manifest) {
return false;
}
const funDef = this.manifest.functions[name];
if (!funDef) {
throw new Error(`Function ${name} not found in manifest`);
}
return !funDef.env || funDef.env === this.runtimeEnv;
}
async invoke(name: string, args: any[]): Promise<any> {
const funDef = this.manifest!.functions[name];
if (!funDef) {
throw new Error(`Function ${name} not found in manifest`);
}
2022-11-01 14:00:59 +00:00
await this.lazyInitSandbox();
const sandbox = this.sandbox!;
if (funDef.redirect) {
// Function redirect, look up
// deno-lint-ignore no-this-alias
let plug: Plug<HookT> | undefined = this;
if (funDef.redirect.indexOf(".") !== -1) {
const [plugName, functionName] = funDef.redirect.split(".");
plug = this.system.loadedPlugs.get(plugName);
if (!plug) {
throw Error(`Plug ${plugName} redirected to not found`);
}
name = functionName;
} else {
name = funDef.redirect;
2022-03-23 14:41:12 +00:00
}
return plug.invoke(name, args);
}
if (!sandbox.isLoaded(name)) {
2022-03-23 14:41:12 +00:00
if (!this.canInvoke(name)) {
throw new Error(
2022-10-12 09:47:13 +00:00
`Function ${name} is not available in ${this.runtimeEnv}`,
2022-03-23 14:41:12 +00:00
);
}
await sandbox.load(name, funDef.code!);
2022-03-23 14:41:12 +00:00
}
return await sandbox.invoke(name, args);
2022-03-23 14:41:12 +00:00
}
2022-10-15 17:02:56 +00:00
stop() {
if (this.sandbox) {
this.sandbox.stop();
}
2022-03-23 14:41:12 +00:00
}
}