import { Manifest, RuntimeEnvironment } from "./types"; import { Sandbox } from "./sandbox"; import { System } from "./system"; export class Plug<HookT> { system: System<HookT>; sandbox: Sandbox; public manifest?: Manifest<HookT>; readonly runtimeEnv: RuntimeEnvironment; grantedPermissions: string[] = []; name: string; version: number; constructor( system: System<HookT>, name: string, sandboxFactory: (plug: Plug<HookT>) => Sandbox ) { this.system = system; this.name = name; this.sandbox = sandboxFactory(this); this.runtimeEnv = system.runtimeEnv; this.version = new Date().getTime(); } async load(manifest: Manifest<HookT>) { this.manifest = manifest; // TODO: These need to be explicitly granted, not just taken this.grantedPermissions = manifest.requiredPermissions || []; for (let [dep, code] of Object.entries(manifest.dependencies || {})) { await this.sandbox.loadDependency(dep, code); } } syscall(name: string, args: any[]): Promise<any> { return this.system.syscallWithContext({ plug: this }, name, args); } 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: Array<any>): Promise<any> { if (!this.sandbox.isLoaded(name)) { const funDef = this.manifest!.functions[name]; if (!funDef) { throw new Error(`Function ${name} not found in manifest`); } if (!this.canInvoke(name)) { throw new Error( `Function ${name} is not available in ${this.runtimeEnv}` ); } await this.sandbox.load(name, funDef.code!); } return await this.sandbox.invoke(name, args); } async stop() { this.sandbox.stop(); } }