2022-10-10 12:50:21 +00:00
|
|
|
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-03-23 14:41:12 +00:00
|
|
|
|
|
|
|
export class Plug<HookT> {
|
|
|
|
system: System<HookT>;
|
|
|
|
sandbox: Sandbox;
|
|
|
|
public manifest?: Manifest<HookT>;
|
2022-10-13 13:16:18 +00:00
|
|
|
public assets?: AssetBundle;
|
2022-03-23 14:41:12 +00:00
|
|
|
readonly runtimeEnv: RuntimeEnvironment;
|
2022-03-25 11:03:06 +00:00
|
|
|
grantedPermissions: string[] = [];
|
|
|
|
name: string;
|
2022-03-31 12:28:07 +00:00
|
|
|
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.sandbox = sandboxFactory(this);
|
2022-03-23 14:41:12 +00:00
|
|
|
this.runtimeEnv = system.runtimeEnv;
|
2022-03-31 12:28:07 +00:00
|
|
|
this.version = new Date().getTime();
|
2022-03-23 14:41:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
async load(manifest: Manifest<HookT>) {
|
|
|
|
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 || [];
|
2022-10-10 12:50:21 +00:00
|
|
|
for (const [dep, code] of Object.entries(manifest.dependencies || {})) {
|
2022-05-13 12:36:26 +00:00
|
|
|
await this.sandbox.loadDependency(dep, code);
|
|
|
|
}
|
2022-03-25 11:03:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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: 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(
|
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 this.sandbox.load(name, funDef.code!);
|
|
|
|
}
|
|
|
|
return await this.sandbox.invoke(name, args);
|
|
|
|
}
|
|
|
|
|
2022-10-15 17:02:56 +00:00
|
|
|
stop() {
|
2022-03-23 14:41:12 +00:00
|
|
|
this.sandbox.stop();
|
|
|
|
}
|
|
|
|
}
|