Plugbox cleanup

This commit is contained in:
Zef Hemel
2022-03-04 11:21:11 +01:00
parent 24ceaea9d5
commit a97bff60d9
26 changed files with 1266 additions and 365 deletions
+5 -4
View File
@@ -22,7 +22,7 @@ import {
import React, { useEffect, useReducer } from "react";
import ReactDOM from "react-dom";
import coreManifest from "./generated/core.plugin.json";
import coreManifest from "./generated/core.plug.json";
// @ts-ignore
window.coreManifest = coreManifest;
import { AppEvent, AppEventDispatcher, ClickEvent } from "./app_event";
@@ -36,9 +36,10 @@ import { lineWrapper } from "./lineWrapper";
import { markdown } from "./markdown";
import { IPageNavigator, PathPageNavigator } from "./navigator";
import customMarkDown from "./parser";
import { BrowserSystem } from "./plugins/browser_system";
import { Plugin } from "./plugins/runtime";
import { slashCommandRegexp } from "./plugins/types";
import { BrowserSystem } from "./plugbox_browser/browser_system";
import { Plugin } from "../../plugbox/src/runtime";
import { slashCommandRegexp } from "../../plugbox/src/types";
import reducer from "./reducer";
import { smartQuoteKeymap } from "./smart_quotes";
import { Space } from "./space";
File diff suppressed because one or more lines are too long
@@ -1,5 +1,5 @@
import { PluginLoader, System } from "./runtime";
import { Manifest } from "./types";
import { PluginLoader, System } from "../../../plugbox/src/runtime";
import { Manifest } from "../../../plugbox/src/types";
import { sleep } from "../util";
export class BrowserLoader implements PluginLoader {
@@ -43,7 +43,7 @@ export class BrowserSystem extends System {
async bootServiceWorker() {
// @ts-ignore
let reg = navigator.serviceWorker.register(
new URL("../plugin_sw.ts", import.meta.url),
new URL("../plugbox_sw.ts", import.meta.url),
{
type: "module",
scope: "/",
@@ -1,12 +1,10 @@
import { Manifest } from "./plugins/types";
import { Manifest } from "../../plugbox/src/types";
import { openDB, wrap, unwrap } from "idb";
import { openDB } from "idb";
const rootUrl = location.origin + "/plugin";
// Storing manifests in IndexedDB, y'all
let manifestCache = caches.open("manifests");
const db = openDB("manifests-store", undefined, {
upgrade(db) {
db.createObjectStore("manifests");
-187
View File
@@ -1,187 +0,0 @@
import { Manifest } from "./types";
interface SysCallMapping {
// TODO: Better typing
[key: string]: any;
}
export class FunctionWorker {
private worker: Worker;
private inited: Promise<any>;
private initCallback: any;
private invokeResolve?: (result?: any) => void;
private invokeReject?: (reason?: any) => void;
private plugin: Plugin;
constructor(plugin: Plugin, pathPrefix: string, name: string) {
// this.worker = new Worker(new URL("function_worker.ts", import.meta.url), {
// type: "classic",
// });
let worker = window.Worker;
this.worker = new worker("/function_worker.js");
// console.log("Starting worker", this.worker);
this.worker.onmessage = this.onmessage.bind(this);
this.worker.postMessage({
type: "boot",
prefix: pathPrefix,
name: name,
// @ts-ignore
userAgent: navigator.userAgent,
});
this.inited = new Promise((resolve) => {
this.initCallback = resolve;
});
this.plugin = plugin;
}
async onmessage(evt: MessageEvent) {
let data = evt.data;
if (!data) return;
switch (data.type) {
case "inited":
this.initCallback();
break;
case "syscall":
let result = await this.plugin.system.syscall(data.name, data.args);
this.worker.postMessage({
type: "syscall-response",
id: data.id,
data: result,
});
break;
case "result":
this.invokeResolve!(data.result);
break;
case "error":
this.invokeReject!(data.reason);
break;
default:
console.error("Unknown message type", data);
}
}
async invoke(args: Array<any>): Promise<any> {
await this.inited;
this.worker.postMessage({
type: "invoke",
args: args,
});
return new Promise((resolve, reject) => {
this.invokeResolve = resolve;
this.invokeReject = reject;
});
}
stop() {
this.worker.terminate();
}
}
export interface PluginLoader {
load(name: string, manifest: Manifest): Promise<void>;
}
export class Plugin {
pathPrefix: string;
system: System;
private runningFunctions: Map<string, FunctionWorker>;
public manifest?: Manifest;
private name: string;
constructor(system: System, pathPrefix: string, name: string) {
this.name = name;
this.pathPrefix = `${pathPrefix}/${name}`;
this.system = system;
this.runningFunctions = new Map<string, FunctionWorker>();
}
async load(manifest: Manifest) {
this.manifest = manifest;
await this.system.pluginLoader.load(this.name, manifest);
await this.dispatchEvent("load");
}
async invoke(name: string, args: Array<any>): Promise<any> {
if (!this.runningFunctions.has(name)) {
this.runningFunctions.set(
name,
new FunctionWorker(this, this.pathPrefix, name)
);
}
return await this.runningFunctions.get(name)!.invoke(args);
}
async dispatchEvent(name: string, data?: any): Promise<any[]> {
let functionsToSpawn = this.manifest!.events[name];
if (functionsToSpawn) {
return await Promise.all(
functionsToSpawn.map(
async (functionToSpawn: string) =>
await this.invoke(functionToSpawn, [data])
)
);
} else {
return [];
}
}
async stop() {
for (const [functionname, worker] of Object.entries(
this.runningFunctions
)) {
console.log(`Stopping ${functionname}`);
worker.stop();
}
this.runningFunctions = new Map<string, FunctionWorker>();
}
}
export class System {
protected plugins: Map<string, Plugin>;
protected pathPrefix: string;
registeredSyscalls: SysCallMapping;
pluginLoader: PluginLoader;
constructor(PluginLoader: PluginLoader, pathPrefix: string) {
this.pluginLoader = PluginLoader;
this.pathPrefix = pathPrefix;
this.plugins = new Map<string, Plugin>();
this.registeredSyscalls = {};
}
registerSyscalls(...registrationObjects: Array<SysCallMapping>) {
for (const registrationObject of registrationObjects) {
for (let p in registrationObject) {
this.registeredSyscalls[p] = registrationObject[p];
}
}
}
async syscall(name: string, args: Array<any>): Promise<any> {
const callback = this.registeredSyscalls[name];
if (!name) {
throw Error(`Unregistered syscall ${name}`);
}
if (!callback) {
throw Error(`Registered but not implemented syscall ${name}`);
}
return Promise.resolve(callback(...args));
}
async load(name: string, manifest: Manifest): Promise<Plugin> {
const plugin = new Plugin(this, this.pathPrefix, name);
await plugin.load(manifest);
this.plugins.set(name, plugin);
return plugin;
}
async stop(): Promise<void[]> {
return Promise.all(
Array.from(this.plugins.values()).map((plugin) => plugin.stop())
);
}
}
console.log("Starting");
-30
View File
@@ -1,30 +0,0 @@
export interface Manifest {
events: { [key: string]: string[] };
commands: {
[key: string]: CommandDef;
};
functions: {
[key: string]: FunctionDef;
};
}
export const slashCommandRegexp = /\/[\w\-]*/;
export interface CommandDef {
// Function name to invoke
invoke: string;
// Bind to keyboard shortcut
key?: string;
mac?: string;
// If to show in slash invoked menu and if so, with what label
// should match slashCommandRegexp
slashCommand?: string;
}
export interface FunctionDef {
path: string;
functionName?: string;
code?: string;
}
+1 -1
View File
@@ -1,4 +1,4 @@
import { CommandDef } from "./plugins/types";
import { CommandDef } from "../../plugbox/src/types";
export type PageMeta = {
name: string;