Work
This commit is contained in:
@@ -20,6 +20,10 @@ async function bundle(
|
||||
(await readFile(manifestPath)).toString()
|
||||
) as Manifest<any>;
|
||||
|
||||
if (!manifest.name) {
|
||||
throw new Error(`Missing 'name' in ${manifestPath}`);
|
||||
}
|
||||
|
||||
for (let [name, def] of Object.entries(manifest.functions)) {
|
||||
let jsFunctionName = "default",
|
||||
filePath = path.join(rootPath, def.path!);
|
||||
|
||||
@@ -9,8 +9,8 @@ import { System } from "../system";
|
||||
test("Run a plugos endpoint server", async () => {
|
||||
let system = new System<EndpointHookT>("server");
|
||||
let plug = await system.load(
|
||||
"test",
|
||||
{
|
||||
name: "test",
|
||||
functions: {
|
||||
testhandler: {
|
||||
http: {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Hook, Manifest } from "../types";
|
||||
import { System } from "../system";
|
||||
import { safeRun } from "../util";
|
||||
import { EventEmitter } from "events";
|
||||
|
||||
// System events:
|
||||
// - plug:load (plugName: string)
|
||||
@@ -11,6 +12,14 @@ export type EventHookT = {
|
||||
|
||||
export class EventHook implements Hook<EventHookT> {
|
||||
private system?: System<EventHookT>;
|
||||
public localListeners: Map<string, ((data: any) => any)[]> = new Map();
|
||||
|
||||
addLocalListener(eventName: string, callback: (data: any) => any) {
|
||||
if (!this.localListeners.has(eventName)) {
|
||||
this.localListeners.set(eventName, []);
|
||||
}
|
||||
this.localListeners.get(eventName)!.push(callback);
|
||||
}
|
||||
|
||||
async dispatchEvent(eventName: string, data?: any): Promise<any[]> {
|
||||
if (!this.system) {
|
||||
@@ -32,15 +41,25 @@ export class EventHook implements Hook<EventHookT> {
|
||||
}
|
||||
}
|
||||
}
|
||||
let localListeners = this.localListeners.get(eventName);
|
||||
if (localListeners) {
|
||||
for (let localListener of localListeners) {
|
||||
let result = await Promise.resolve(localListener(data));
|
||||
if (result) {
|
||||
responses.push(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return responses;
|
||||
}
|
||||
|
||||
apply(system: System<EventHookT>): void {
|
||||
this.system = system;
|
||||
this.system.on({
|
||||
plugLoaded: (name) => {
|
||||
plugLoaded: (plug) => {
|
||||
safeRun(async () => {
|
||||
await this.dispatchEvent("plug:load", name);
|
||||
await this.dispatchEvent("plug:load", plug.name);
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
@@ -11,10 +11,10 @@ export class NodeCronHook implements Hook<CronHookT> {
|
||||
apply(system: System<CronHookT>): void {
|
||||
let tasks: ScheduledTask[] = [];
|
||||
system.on({
|
||||
plugLoaded: (name, plug) => {
|
||||
plugLoaded: () => {
|
||||
reloadCrons();
|
||||
},
|
||||
plugUnloaded(name, plug) {
|
||||
plugUnloaded() {
|
||||
reloadCrons();
|
||||
},
|
||||
});
|
||||
|
||||
@@ -3,11 +3,7 @@ import watch from "node-watch";
|
||||
import path from "path";
|
||||
import { createSandbox } from "./environments/node_sandbox";
|
||||
import { System } from "./system";
|
||||
|
||||
function extractPlugName(localPath: string): string {
|
||||
const baseName = path.basename(localPath);
|
||||
return baseName.substring(0, baseName.length - ".plug.json".length);
|
||||
}
|
||||
import { Manifest } from "./types";
|
||||
|
||||
export class DiskPlugLoader<HookT> {
|
||||
private system: System<HookT>;
|
||||
@@ -27,13 +23,13 @@ export class DiskPlugLoader<HookT> {
|
||||
.then(async () => {
|
||||
try {
|
||||
// let localPath = path.join(this.plugPath, filename);
|
||||
const plugName = extractPlugName(localPath);
|
||||
console.log("Change detected for", plugName);
|
||||
console.log("Change detected for", localPath);
|
||||
try {
|
||||
await fs.stat(localPath);
|
||||
} catch (e) {
|
||||
// Likely removed
|
||||
await this.system.unload(plugName);
|
||||
console.log("Plug removed, TODO: Unload");
|
||||
return;
|
||||
}
|
||||
const plugDef = await this.loadPlugFromFile(localPath);
|
||||
} catch (e) {
|
||||
@@ -47,12 +43,11 @@ export class DiskPlugLoader<HookT> {
|
||||
|
||||
private async loadPlugFromFile(localPath: string) {
|
||||
const plug = await fs.readFile(localPath, "utf8");
|
||||
const plugName = extractPlugName(localPath);
|
||||
|
||||
console.log("Now loading plug", plugName);
|
||||
try {
|
||||
const plugDef = JSON.parse(plug);
|
||||
await this.system.load(plugName, plugDef, createSandbox);
|
||||
const plugDef: Manifest<HookT> = JSON.parse(plug);
|
||||
console.log("Now loading plug", plugDef.name);
|
||||
await this.system.load(plugDef, createSandbox);
|
||||
return plugDef;
|
||||
} catch (e) {
|
||||
console.error("Could not parse plugin file", e);
|
||||
|
||||
@@ -23,8 +23,8 @@ test("Run a Node sandbox", async () => {
|
||||
},
|
||||
});
|
||||
let plug = await system.load(
|
||||
"test",
|
||||
{
|
||||
name: "test",
|
||||
requiredPermissions: ["dangerous"],
|
||||
functions: {
|
||||
addTen: {
|
||||
|
||||
@@ -34,9 +34,7 @@ export function esbuildSyscalls(): SysCallMapping {
|
||||
}
|
||||
|
||||
await writeFile(`${tmpDir}/${filename}`, code);
|
||||
console.log("Dir", tmpDir);
|
||||
let jsCode = await compile(`${tmpDir}/${filename}`, "", false, ["yaml"]);
|
||||
// console.log("JS code", jsCode);
|
||||
await rm(tmpDir, { recursive: true });
|
||||
return jsCode;
|
||||
},
|
||||
|
||||
@@ -10,8 +10,8 @@ test("Test store", async () => {
|
||||
let system = new System("server");
|
||||
system.registerSyscalls([], storeSyscalls("test", "test"));
|
||||
let plug = await system.load(
|
||||
"test",
|
||||
{
|
||||
name: "test",
|
||||
functions: {
|
||||
test1: {
|
||||
code: `(() => {
|
||||
|
||||
@@ -17,8 +17,8 @@ test("Test store", async () => {
|
||||
let system = new System("server");
|
||||
system.registerSyscalls([], storeSyscalls(db, "test_table"));
|
||||
let plug = await system.load(
|
||||
"test",
|
||||
{
|
||||
name: "test",
|
||||
functions: {
|
||||
test1: {
|
||||
code: `(() => {
|
||||
|
||||
+13
-11
@@ -7,11 +7,11 @@ export interface SysCallMapping {
|
||||
[key: string]: (ctx: SyscallContext, ...args: any) => Promise<any> | any;
|
||||
}
|
||||
|
||||
export type SystemJSON<HookT> = { [key: string]: Manifest<HookT> };
|
||||
export type SystemJSON<HookT> = Manifest<HookT>[];
|
||||
|
||||
export type SystemEvents<HookT> = {
|
||||
plugLoaded: (name: string, plug: Plug<HookT>) => void;
|
||||
plugUnloaded: (name: string, plug: Plug<HookT>) => void;
|
||||
plugLoaded: (plug: Plug<HookT>) => void;
|
||||
plugUnloaded: (name: string) => void;
|
||||
};
|
||||
|
||||
export type SyscallContext = {
|
||||
@@ -83,10 +83,10 @@ export class System<HookT> extends EventEmitter<SystemEvents<HookT>> {
|
||||
}
|
||||
|
||||
async load(
|
||||
name: string,
|
||||
manifest: Manifest<HookT>,
|
||||
sandboxFactory: SandboxFactory<HookT>
|
||||
): Promise<Plug<HookT>> {
|
||||
const name = manifest.name;
|
||||
if (this.plugs.has(name)) {
|
||||
await this.unload(name);
|
||||
}
|
||||
@@ -100,29 +100,31 @@ export class System<HookT> extends EventEmitter<SystemEvents<HookT>> {
|
||||
}
|
||||
// Ok, let's load this thing!
|
||||
const plug = new Plug(this, name, sandboxFactory);
|
||||
console.log("Loading", name);
|
||||
await plug.load(manifest);
|
||||
this.plugs.set(name, plug);
|
||||
this.emit("plugLoaded", name, plug);
|
||||
this.emit("plugLoaded", plug);
|
||||
return plug;
|
||||
}
|
||||
|
||||
async unload(name: string) {
|
||||
console.log("Unloading", name);
|
||||
const plug = this.plugs.get(name);
|
||||
if (!plug) {
|
||||
throw Error(`Plug ${name} not found`);
|
||||
}
|
||||
await plug.stop();
|
||||
this.emit("plugUnloaded", name, plug);
|
||||
this.emit("plugUnloaded", name);
|
||||
this.plugs.delete(name);
|
||||
}
|
||||
|
||||
toJSON(): SystemJSON<HookT> {
|
||||
let plugJSON: { [key: string]: Manifest<HookT> } = {};
|
||||
let plugJSON: Manifest<HookT>[] = [];
|
||||
for (let [name, plug] of this.plugs) {
|
||||
if (!plug.manifest) {
|
||||
continue;
|
||||
}
|
||||
plugJSON[name] = plug.manifest;
|
||||
plugJSON.push(plug.manifest);
|
||||
}
|
||||
return plugJSON;
|
||||
}
|
||||
@@ -132,9 +134,9 @@ export class System<HookT> extends EventEmitter<SystemEvents<HookT>> {
|
||||
sandboxFactory: SandboxFactory<HookT>
|
||||
) {
|
||||
await this.unloadAll();
|
||||
for (let [name, manifest] of Object.entries(json)) {
|
||||
console.log("Loading plug", name);
|
||||
await this.load(name, manifest, sandboxFactory);
|
||||
for (let manifest of json) {
|
||||
console.log("Loading plug", manifest.name);
|
||||
await this.load(manifest, sandboxFactory);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { System } from "./system";
|
||||
|
||||
export interface Manifest<HookT> {
|
||||
name: string;
|
||||
requiredPermissions?: string[];
|
||||
functions: {
|
||||
[key: string]: FunctionDef<HookT>;
|
||||
|
||||
Reference in New Issue
Block a user