Cleanup and progress
parent
7e591c6f44
commit
a916088215
|
@ -0,0 +1,23 @@
|
|||
import * as plugbox from "../plugbox/types";
|
||||
import { EndpointHook } from "../plugbox/types";
|
||||
|
||||
export type 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 type SilverBulletHooks = {
|
||||
commands?: {
|
||||
[key: string]: CommandDef;
|
||||
};
|
||||
} & plugbox.EndpointHook;
|
||||
|
||||
export type Manifest = plugbox.Manifest<SilverBulletHooks>;
|
|
@ -6,14 +6,14 @@
|
|||
<style>
|
||||
@import "../../webapp/src/styles/main.scss";
|
||||
</style>
|
||||
<script type="module" src="boot.ts"></script>
|
||||
<script type="module" src="server.ts"></script>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module">
|
||||
import "./boot";
|
||||
import "./server";
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -12,8 +12,8 @@
|
|||
"watch": "parcel watch",
|
||||
"build": "parcel build",
|
||||
"clean": "rm -rf dist",
|
||||
"plugs": "node dist/bundler/plugbox-bundle.js plugs/core/core.plug.json webapp/generated/core.plug.json",
|
||||
"server": "nodemon dist/server/server.js pages",
|
||||
"plugs": "node dist/bundler/plugbox-bundle.js plugs/core/core.plug.json plugs/dist/core.plug.json",
|
||||
"server": "nodemon -w dist/server dist/server/server.js pages",
|
||||
"test": "jest"
|
||||
},
|
||||
"targets": {
|
||||
|
@ -40,6 +40,7 @@
|
|||
"test": {
|
||||
"source": [
|
||||
"plugbox/runtime.test.ts",
|
||||
"plugbox/endpoint.test.ts",
|
||||
"server/api.test.ts"
|
||||
],
|
||||
"outputFormat": "commonjs",
|
||||
|
@ -75,12 +76,14 @@
|
|||
"jest": "^27.5.1",
|
||||
"knex": "^1.0.4",
|
||||
"lodash": "^4.17.21",
|
||||
"node-cron": "^3.0.0",
|
||||
"nodemon": "^2.0.15",
|
||||
"parcel": "^2.3.2",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"socket.io": "^4.4.1",
|
||||
"socket.io-client": "^4.4.1",
|
||||
"supertest": "^6.2.2",
|
||||
"ts-jest": "^27.1.3",
|
||||
"vm2": "^3.9.9",
|
||||
"yargs": "^17.3.1"
|
||||
|
@ -95,8 +98,10 @@
|
|||
"@types/events": "^3.0.0",
|
||||
"@types/jest": "^27.4.1",
|
||||
"@types/node": "^17.0.21",
|
||||
"@types/node-cron": "^3.0.1",
|
||||
"@types/react": "^17.0.39",
|
||||
"@types/react-dom": "^17.0.11",
|
||||
"@types/supertest": "^2.0.11",
|
||||
"@vscode/sqlite3": "^5.0.7",
|
||||
"assert": "^2.0.0",
|
||||
"crypto-browserify": "^3.12.0",
|
||||
|
|
|
@ -6,9 +6,9 @@ import path from "path";
|
|||
|
||||
import yargs from "yargs";
|
||||
import { hideBin } from "yargs/helpers";
|
||||
import {Manifest} from "../types";
|
||||
import { Manifest } from "../types";
|
||||
|
||||
async function compile(filePath : string, functionName : string, debug: boolean) {
|
||||
async function compile(filePath: string, functionName: string, debug: boolean) {
|
||||
let outFile = "out.js";
|
||||
|
||||
let inFile = filePath;
|
||||
|
@ -47,7 +47,9 @@ export default ${functionName};`
|
|||
|
||||
async function bundle(manifestPath: string, sourceMaps: boolean) {
|
||||
const rootPath = path.dirname(manifestPath);
|
||||
const manifest = JSON.parse((await readFile(manifestPath)).toString()) as Manifest<any>;
|
||||
const manifest = JSON.parse(
|
||||
(await readFile(manifestPath)).toString()
|
||||
) as Manifest<any>;
|
||||
|
||||
for (let [name, def] of Object.entries(manifest.functions)) {
|
||||
let jsFunctionName = "default",
|
||||
|
@ -69,7 +71,10 @@ async function run() {
|
|||
.parse();
|
||||
|
||||
let generatedManifest = await bundle(args._[0] as string, !!args.debug);
|
||||
await writeFile(args._[1] as string, JSON.stringify(generatedManifest, null, 2));
|
||||
await writeFile(
|
||||
args._[1] as string,
|
||||
JSON.stringify(generatedManifest, null, 2)
|
||||
);
|
||||
}
|
||||
|
||||
run().catch((e) => {
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
import { System } from "./runtime";
|
||||
import { CronHook } from "./types";
|
||||
import cron from "node-cron";
|
||||
|
||||
export function cronSystem(system: System<CronHook>) {
|
||||
let task = cron.schedule("* * * * *", () => {
|
||||
|
||||
});
|
||||
// @ts-ignore
|
||||
task.destroy();
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
import { createSandbox } from "./node_sandbox";
|
||||
import { System } from "./runtime";
|
||||
import { test, expect } from "@jest/globals";
|
||||
import { EndPointDef, EndpointHook, Manifest } from "./types";
|
||||
import express from "express";
|
||||
import request from "supertest";
|
||||
import { exposeSystem } from "./endpoints";
|
||||
|
||||
test("Run a plugbox endpoint server", async () => {
|
||||
let system = new System<EndpointHook>();
|
||||
let plug = await system.load(
|
||||
"test",
|
||||
{
|
||||
functions: {
|
||||
testhandler: {
|
||||
code: `(() => {
|
||||
return {
|
||||
default: (req) => {
|
||||
console.log("Req", req);
|
||||
return {status: 200, body: [1, 2, 3], headers: {"Content-type": "application/json"}};
|
||||
}
|
||||
};
|
||||
})()`,
|
||||
},
|
||||
},
|
||||
hooks: {
|
||||
endpoints: [{ method: "GET", path: "/", handler: "testhandler" }],
|
||||
},
|
||||
} as Manifest<EndpointHook>,
|
||||
createSandbox(system)
|
||||
);
|
||||
|
||||
const app = express();
|
||||
const port = 3123;
|
||||
app.use(exposeSystem(system));
|
||||
let server = app.listen(port, () => {
|
||||
console.log(`Listening on port ${port}`);
|
||||
});
|
||||
let resp = await request(app)
|
||||
.get("/_/test/?name=Pete")
|
||||
.expect((resp) => {
|
||||
expect(resp.status).toBe(200);
|
||||
expect(resp.header["content-type"]).toContain("application/json");
|
||||
expect(resp.text).toBe(JSON.stringify([1, 2, 3]));
|
||||
});
|
||||
server.close();
|
||||
});
|
|
@ -0,0 +1,85 @@
|
|||
import { System } from "./runtime";
|
||||
import { EndpointHook } from "./types";
|
||||
import express from "express";
|
||||
|
||||
export type EndpointRequest = {
|
||||
method: string;
|
||||
path: string;
|
||||
query: { [key: string]: string };
|
||||
headers: { [key: string]: string };
|
||||
body: any;
|
||||
};
|
||||
|
||||
export type EndpointResponse = {
|
||||
status: number;
|
||||
headers?: { [key: string]: string };
|
||||
body: any;
|
||||
};
|
||||
|
||||
const endPointPrefix = "/_";
|
||||
|
||||
export function exposeSystem(system: System<EndpointHook>) {
|
||||
return (
|
||||
req: express.Request,
|
||||
res: express.Response,
|
||||
next: express.NextFunction
|
||||
) => {
|
||||
if (!req.path.startsWith(endPointPrefix)) {
|
||||
return next();
|
||||
}
|
||||
Promise.resolve()
|
||||
.then(async () => {
|
||||
for (const [plugName, plug] of system.loadedPlugs.entries()) {
|
||||
const manifest = plug.manifest;
|
||||
if (!manifest) {
|
||||
continue;
|
||||
}
|
||||
const endpoints = manifest.hooks?.endpoints;
|
||||
if (endpoints) {
|
||||
let prefix = `${endPointPrefix}/${plugName}`;
|
||||
if (!req.path.startsWith(prefix)) {
|
||||
continue;
|
||||
}
|
||||
for (const { path, method, handler } of endpoints) {
|
||||
let prefixedPath = `${prefix}${path}`;
|
||||
if (prefixedPath === req.path && method === req.method) {
|
||||
try {
|
||||
const response: EndpointResponse = await plug.invoke(
|
||||
handler,
|
||||
[
|
||||
{
|
||||
path: req.path,
|
||||
method: req.method,
|
||||
body: req.body,
|
||||
query: req.query,
|
||||
headers: req.headers,
|
||||
} as EndpointRequest,
|
||||
]
|
||||
);
|
||||
let resp = res.status(response.status);
|
||||
if (response.headers) {
|
||||
for (const [key, value] of Object.entries(
|
||||
response.headers
|
||||
)) {
|
||||
resp = resp.header(key, value);
|
||||
}
|
||||
}
|
||||
resp.send(response.body);
|
||||
return;
|
||||
} catch (e: any) {
|
||||
console.error("Error executing function", e);
|
||||
res.status(500).send(e.message);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
next();
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error(e);
|
||||
next(e);
|
||||
});
|
||||
};
|
||||
}
|
|
@ -8,6 +8,7 @@ import sandboxHtml from "bundle-text:./iframe_sandbox.html";
|
|||
class IFrameWrapper implements WorkerLike {
|
||||
private iframe: HTMLIFrameElement;
|
||||
onMessage?: (message: any) => Promise<void>;
|
||||
ready: Promise<void>;
|
||||
|
||||
constructor() {
|
||||
const iframe = document.createElement("iframe", {});
|
||||
|
@ -27,6 +28,12 @@ class IFrameWrapper implements WorkerLike {
|
|||
});
|
||||
});
|
||||
document.body.appendChild(iframe);
|
||||
this.ready = new Promise((resolve) => {
|
||||
iframe.onload = () => {
|
||||
resolve();
|
||||
iframe.onload = null;
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
postMessage(message: any): void {
|
||||
|
|
|
@ -6,14 +6,12 @@ import * as fs from "fs";
|
|||
import { safeRun } from "./util";
|
||||
|
||||
// @ts-ignore
|
||||
import workerCode from "bundle-text:./node_worker.ts"
|
||||
|
||||
// ParcelJS will simply inline this into the bundle.
|
||||
// const workerCode = fs.readFileSync(__dirname + "/node_worker.ts", "utf-8");
|
||||
import workerCode from "bundle-text:./node_worker.ts";
|
||||
|
||||
class NodeWorkerWrapper implements WorkerLike {
|
||||
onMessage?: (message: any) => Promise<void>;
|
||||
private worker: Worker;
|
||||
ready: Promise<void>;
|
||||
|
||||
constructor(worker: Worker) {
|
||||
this.worker = worker;
|
||||
|
@ -22,6 +20,9 @@ class NodeWorkerWrapper implements WorkerLike {
|
|||
await this.onMessage!(message);
|
||||
});
|
||||
});
|
||||
this.ready = new Promise((resolve) => {
|
||||
worker.once("online", resolve);
|
||||
});
|
||||
}
|
||||
|
||||
postMessage(message: any): void {
|
||||
|
@ -34,6 +35,9 @@ class NodeWorkerWrapper implements WorkerLike {
|
|||
}
|
||||
|
||||
export function createSandbox(system: System<any>) {
|
||||
let worker = new Worker(workerCode, {
|
||||
eval: true,
|
||||
});
|
||||
return new Sandbox(
|
||||
system,
|
||||
new NodeWorkerWrapper(
|
||||
|
|
|
@ -3,18 +3,18 @@ const { parentPort } = require("worker_threads");
|
|||
|
||||
let loadedFunctions = new Map<string, Function>();
|
||||
let pendingRequests = new Map<
|
||||
number,
|
||||
{
|
||||
resolve: (result: unknown) => void;
|
||||
reject: (e: any) => void;
|
||||
}
|
||||
>();
|
||||
number,
|
||||
{
|
||||
resolve: (result: unknown) => void;
|
||||
reject: (e: any) => void;
|
||||
}
|
||||
>();
|
||||
|
||||
let vm = new VM({
|
||||
sandbox: {
|
||||
console: console,
|
||||
self: {
|
||||
syscall: (reqId : number, name : string, args: any[]) => {
|
||||
syscall: (reqId: number, name: string, args: any[]) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
pendingRequests.set(reqId, { resolve, reject });
|
||||
parentPort.postMessage({
|
||||
|
@ -30,17 +30,17 @@ let vm = new VM({
|
|||
},
|
||||
});
|
||||
|
||||
function wrapScript(code : string) {
|
||||
function wrapScript(code: string) {
|
||||
return `(${code})["default"]`;
|
||||
}
|
||||
|
||||
function safeRun(fn : any) {
|
||||
fn().catch((e : any) => {
|
||||
function safeRun(fn: any) {
|
||||
fn().catch((e: any) => {
|
||||
console.error(e);
|
||||
});
|
||||
}
|
||||
|
||||
parentPort.on("message", (data : any) => {
|
||||
parentPort.on("message", (data: any) => {
|
||||
safeRun(async () => {
|
||||
switch (data.type) {
|
||||
case "load":
|
||||
|
@ -62,9 +62,10 @@ parentPort.on("message", (data : any) => {
|
|||
parentPort.postMessage({
|
||||
type: "result",
|
||||
id: data.id,
|
||||
result: result,
|
||||
// TOOD: Figure out if this is necessary, because it's expensive
|
||||
result: result && JSON.parse(JSON.stringify(result)),
|
||||
});
|
||||
} catch (e : any) {
|
||||
} catch (e: any) {
|
||||
// console.log("ERROR", e);
|
||||
parentPort.postMessage({
|
||||
type: "result",
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
import fs, { stat, watch } from "fs/promises";
|
||||
import path from "path";
|
||||
import { createSandbox } from "./node_sandbox";
|
||||
import { System } from "./runtime";
|
||||
import { safeRun } from "../server/util";
|
||||
|
||||
function extractPlugName(localPath: string): string {
|
||||
const baseName = path.basename(localPath);
|
||||
return baseName.substring(0, baseName.length - ".plug.json".length);
|
||||
}
|
||||
|
||||
export class DiskPlugLoader<HookT> {
|
||||
private system: System<HookT>;
|
||||
private plugPath: string;
|
||||
|
||||
constructor(system: System<HookT>, plugPath: string) {
|
||||
this.system = system;
|
||||
this.plugPath = plugPath;
|
||||
}
|
||||
|
||||
watcher() {
|
||||
safeRun(async () => {
|
||||
for await (const { filename, eventType } of watch(this.plugPath, {
|
||||
recursive: true,
|
||||
})) {
|
||||
if (!filename.endsWith(".plug.json")) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
let localPath = path.join(this.plugPath, filename);
|
||||
const plugName = extractPlugName(localPath);
|
||||
try {
|
||||
await fs.stat(localPath);
|
||||
} catch (e) {
|
||||
// Likely removed
|
||||
await this.system.unload(plugName);
|
||||
this.system.emit("plugRemoved", plugName);
|
||||
}
|
||||
const plugDef = await this.loadPlugFromFile(localPath);
|
||||
this.system.emit("plugUpdated", plugName, plugDef);
|
||||
} catch {
|
||||
// ignore, error handled by loadPlug
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
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(this.system));
|
||||
return plugDef;
|
||||
} catch (e) {
|
||||
console.error("Could not parse plugin file", e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
async loadPlugs() {
|
||||
for (let filename of await fs.readdir(this.plugPath)) {
|
||||
if (filename.endsWith(".plug.json")) {
|
||||
let localPath = path.join(this.plugPath, filename);
|
||||
await this.loadPlugFromFile(localPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -75,5 +75,5 @@ test("Run a Node sandbox", async () => {
|
|||
} catch (e: any) {
|
||||
expect(e.message).toBe("#fail");
|
||||
}
|
||||
await system.stop();
|
||||
await system.unloadAll();
|
||||
});
|
||||
|
|
|
@ -4,6 +4,7 @@ import {
|
|||
WorkerLike,
|
||||
WorkerMessage,
|
||||
} from "./types";
|
||||
import { EventEmitter } from "../common/event";
|
||||
|
||||
interface SysCallMapping {
|
||||
[key: string]: (...args: any) => Promise<any> | any;
|
||||
|
@ -31,6 +32,7 @@ export class Sandbox {
|
|||
}
|
||||
|
||||
async load(name: string, code: string): Promise<void> {
|
||||
await this.worker.ready;
|
||||
this.worker.postMessage({
|
||||
type: "load",
|
||||
name: name,
|
||||
|
@ -119,18 +121,20 @@ export class Plug<HookT> {
|
|||
if (!funDef) {
|
||||
throw new Error(`Function ${name} not found in manifest`);
|
||||
}
|
||||
await this.sandbox.load(name, this.manifest!.functions[name].code!);
|
||||
await this.sandbox.load(name, funDef.code!);
|
||||
}
|
||||
return await this.sandbox.invoke(name, args);
|
||||
}
|
||||
|
||||
async dispatchEvent(name: string, data?: any): Promise<any[]> {
|
||||
if (!this.manifest!.hooks?.events) {
|
||||
return [];
|
||||
}
|
||||
let functionsToSpawn = this.manifest!.hooks.events[name];
|
||||
if (functionsToSpawn) {
|
||||
return await Promise.all(
|
||||
functionsToSpawn.map(
|
||||
async (functionToSpawn: string) =>
|
||||
await this.invoke(functionToSpawn, [data])
|
||||
functionsToSpawn.map((functionToSpawn: string) =>
|
||||
this.invoke(functionToSpawn, [data])
|
||||
)
|
||||
);
|
||||
} else {
|
||||
|
@ -143,10 +147,21 @@ export class Plug<HookT> {
|
|||
}
|
||||
}
|
||||
|
||||
export class System<HookT> {
|
||||
export type SystemJSON<HookT> = { [key: string]: Manifest<HookT> };
|
||||
|
||||
export type SystemEvents<HookT> = {
|
||||
plugUpdated: (name: string, plug: Plug<HookT>) => void;
|
||||
plugRemoved: (name: string) => void;
|
||||
};
|
||||
|
||||
export class System<HookT> extends EventEmitter<SystemEvents<HookT>> {
|
||||
protected plugs = new Map<string, Plug<HookT>>();
|
||||
registeredSyscalls: SysCallMapping = {};
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
registerSyscalls(...registrationObjects: SysCallMapping[]) {
|
||||
for (const registrationObject of registrationObjects) {
|
||||
for (let p in registrationObject) {
|
||||
|
@ -171,23 +186,63 @@ export class System<HookT> {
|
|||
manifest: Manifest<HookT>,
|
||||
sandbox: Sandbox
|
||||
): Promise<Plug<HookT>> {
|
||||
if (this.plugs.has(name)) {
|
||||
await this.unload(name);
|
||||
}
|
||||
const plug = new Plug(this, name, sandbox);
|
||||
await plug.load(manifest);
|
||||
this.plugs.set(name, plug);
|
||||
return plug;
|
||||
}
|
||||
|
||||
async unload(name: string) {
|
||||
const plug = this.plugs.get(name);
|
||||
if (!plug) {
|
||||
throw Error(`Plug ${name} not found`);
|
||||
}
|
||||
await plug.stop();
|
||||
this.plugs.delete(name);
|
||||
}
|
||||
|
||||
async dispatchEvent(name: string, data?: any): Promise<any[]> {
|
||||
let promises = [];
|
||||
for (let plug of this.plugs.values()) {
|
||||
promises.push(plug.dispatchEvent(name, data));
|
||||
for (let result of await plug.dispatchEvent(name, data)) {
|
||||
promises.push(result);
|
||||
}
|
||||
}
|
||||
return await Promise.all(promises);
|
||||
}
|
||||
|
||||
async stop(): Promise<void[]> {
|
||||
get loadedPlugs(): Map<string, Plug<HookT>> {
|
||||
return this.plugs;
|
||||
}
|
||||
|
||||
toJSON(): SystemJSON<HookT> {
|
||||
let plugJSON: { [key: string]: Manifest<HookT> } = {};
|
||||
for (let [name, plug] of this.plugs) {
|
||||
if (!plug.manifest) {
|
||||
continue;
|
||||
}
|
||||
plugJSON[name] = plug.manifest;
|
||||
}
|
||||
return plugJSON;
|
||||
}
|
||||
|
||||
async replaceAllFromJSON(
|
||||
json: SystemJSON<HookT>,
|
||||
sandboxFactory: () => Sandbox
|
||||
) {
|
||||
await this.unloadAll();
|
||||
for (let [name, manifest] of Object.entries(json)) {
|
||||
console.log("Loading plug", name);
|
||||
await this.load(name, manifest, sandboxFactory());
|
||||
}
|
||||
}
|
||||
|
||||
async unloadAll(): Promise<void[]> {
|
||||
return Promise.all(
|
||||
Array.from(this.plugs.values()).map((plug) => plug.stop())
|
||||
Array.from(this.plugs.keys()).map(this.unload.bind(this))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,9 +17,7 @@ declare global {
|
|||
let postMessage = self.postMessage.bind(self);
|
||||
|
||||
if (window.parent !== window) {
|
||||
console.log("running in an iframe");
|
||||
postMessage = window.parent.postMessage.bind(window.parent);
|
||||
// postMessage({ type: "test" }, "*");
|
||||
}
|
||||
|
||||
self.syscall = async (id: number, name: string, args: any[]) => {
|
||||
|
@ -43,7 +41,6 @@ return fn["default"].apply(null, arguments);`;
|
|||
}
|
||||
|
||||
self.addEventListener("message", (event: { data: WorkerMessage }) => {
|
||||
// console.log("Got a message", event.data);
|
||||
safeRun(async () => {
|
||||
let messageEvent = event;
|
||||
let data = messageEvent.data;
|
||||
|
|
|
@ -1,7 +1,3 @@
|
|||
export type EventHook = {
|
||||
events: { [key: string]: string[] };
|
||||
};
|
||||
|
||||
export type WorkerMessageType = "load" | "invoke" | "syscall-response";
|
||||
|
||||
export type WorkerMessage = {
|
||||
|
@ -37,7 +33,30 @@ export interface FunctionDef {
|
|||
code?: string;
|
||||
}
|
||||
|
||||
export type EventHook = {
|
||||
events?: { [key: string]: string[] };
|
||||
};
|
||||
|
||||
export type EndpointHook = {
|
||||
endpoints?: EndPointDef[];
|
||||
};
|
||||
export type EndPointDef = {
|
||||
method: "GET" | "POST" | "PUT" | "DELETE" | "HEAD" | "OPTIONS";
|
||||
path: string;
|
||||
handler: string; // function name
|
||||
};
|
||||
|
||||
export type CronHook = {
|
||||
crons?: CronDef[];
|
||||
};
|
||||
|
||||
export type CronDef = {
|
||||
cron: string;
|
||||
handler: string; // function name
|
||||
};
|
||||
|
||||
export interface WorkerLike {
|
||||
ready: Promise<void>;
|
||||
onMessage?: (message: any) => Promise<void>;
|
||||
postMessage(message: any): void;
|
||||
terminate(): void;
|
||||
|
|
|
@ -5,6 +5,7 @@ import { safeRun } from "./util";
|
|||
class WebWorkerWrapper implements WorkerLike {
|
||||
private worker: Worker;
|
||||
onMessage?: (message: any) => Promise<void>;
|
||||
ready: Promise<void>;
|
||||
|
||||
constructor(worker: Worker) {
|
||||
this.worker = worker;
|
||||
|
@ -15,6 +16,7 @@ class WebWorkerWrapper implements WorkerLike {
|
|||
await this.onMessage!(data);
|
||||
});
|
||||
});
|
||||
this.ready = Promise.resolve();
|
||||
}
|
||||
postMessage(message: any): void {
|
||||
this.worker.postMessage(message);
|
||||
|
|
|
@ -36,8 +36,16 @@
|
|||
"events": {
|
||||
"page:click": ["taskToggle", "clickNavigate"],
|
||||
"editor:complete": ["pageComplete"],
|
||||
"page:index": ["indexLinks"]
|
||||
}
|
||||
"page:index": ["indexLinks"],
|
||||
"load": ["welcome"]
|
||||
},
|
||||
"endpoints": [
|
||||
{
|
||||
"method": "GET",
|
||||
"path": "/",
|
||||
"handler": "endpointTest"
|
||||
}
|
||||
]
|
||||
},
|
||||
"functions": {
|
||||
"indexLinks": {
|
||||
|
@ -76,8 +84,11 @@
|
|||
"toggle_h2": {
|
||||
"path": "./markup.ts:toggleH2"
|
||||
},
|
||||
"server_test": {
|
||||
"path": "./server.ts:test"
|
||||
"endpointTest": {
|
||||
"path": "./server.ts:endpointTest"
|
||||
},
|
||||
"welcome": {
|
||||
"path": "./server.ts:welcome"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,15 @@
|
|||
import { syscall } from "./lib/syscall";
|
||||
export function test() {
|
||||
console.log("I'm running on the server!");
|
||||
return 5;
|
||||
import { EndpointRequest, EndpointResponse } from "../../plugbox/endpoints";
|
||||
|
||||
export function endpointTest(req: EndpointRequest): EndpointResponse {
|
||||
console.log("I'm running on the server!", req);
|
||||
return {
|
||||
status: 200,
|
||||
body: "Hello world!",
|
||||
};
|
||||
}
|
||||
|
||||
export function welcome() {
|
||||
for (var i = 0; i < 10; i++) {
|
||||
console.log("Welcome to you all!!!");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,8 @@ import { Server } from "socket.io";
|
|||
import { SocketServer } from "./api_server";
|
||||
import * as path from "path";
|
||||
import * as fs from "fs";
|
||||
import { SilverBulletHooks } from "../common/manifest";
|
||||
import { System } from "../plugbox/runtime";
|
||||
|
||||
describe("Server test", () => {
|
||||
let io: Server,
|
||||
|
@ -38,7 +40,11 @@ describe("Server test", () => {
|
|||
const port = httpServer.address().port;
|
||||
// @ts-ignore
|
||||
clientSocket = new Client(`http://localhost:${port}`);
|
||||
socketServer = new SocketServer(tmpDir, io);
|
||||
socketServer = new SocketServer(
|
||||
tmpDir,
|
||||
io,
|
||||
new System<SilverBulletHooks>()
|
||||
);
|
||||
clientSocket.on("connect", done);
|
||||
await socketServer.init();
|
||||
});
|
||||
|
|
|
@ -4,9 +4,7 @@ import * as path from "path";
|
|||
import { IndexApi } from "./index_api";
|
||||
import { PageApi } from "./page_api";
|
||||
import { System } from "../plugbox/runtime";
|
||||
import { createSandbox } from "../plugbox/node_sandbox";
|
||||
import { NuggetHook } from "../webapp/types";
|
||||
import corePlug from "../webapp/generated/core.plug.json";
|
||||
import { SilverBulletHooks } from "../common/manifest";
|
||||
import pageIndexSyscalls from "./syscalls/page_index";
|
||||
|
||||
export class ClientConnection {
|
||||
|
@ -25,12 +23,16 @@ export class SocketServer {
|
|||
private apis = new Map<string, ApiProvider>();
|
||||
readonly rootPath: string;
|
||||
private serverSocket: Server;
|
||||
system: System<NuggetHook>;
|
||||
system: System<SilverBulletHooks>;
|
||||
|
||||
constructor(rootPath: string, serverSocket: Server) {
|
||||
constructor(
|
||||
rootPath: string,
|
||||
serverSocket: Server,
|
||||
system: System<SilverBulletHooks>
|
||||
) {
|
||||
this.rootPath = path.resolve(rootPath);
|
||||
this.serverSocket = serverSocket;
|
||||
this.system = new System<NuggetHook>();
|
||||
this.system = system;
|
||||
}
|
||||
|
||||
async registerApi(name: string, apiProvider: ApiProvider) {
|
||||
|
@ -52,12 +54,6 @@ export class SocketServer {
|
|||
)
|
||||
);
|
||||
|
||||
let plug = await this.system.load(
|
||||
"core",
|
||||
corePlug,
|
||||
createSandbox(this.system)
|
||||
);
|
||||
|
||||
this.serverSocket.on("connection", (socket) => {
|
||||
const clientConn = new ClientConnection(socket);
|
||||
|
||||
|
@ -112,6 +108,9 @@ export class SocketServer {
|
|||
});
|
||||
});
|
||||
}
|
||||
|
||||
console.log("Sending the sytem to the client");
|
||||
socket.emit("loadSystem", this.system.toJSON());
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
import { Express } from "express";
|
||||
import { System } from "../plugbox/runtime";
|
||||
import { SilverBulletHooks } from "../common/manifest";
|
||||
import { exposeSystem } from "../plugbox/endpoints";
|
||||
import { readFile } from "fs/promises";
|
||||
|
||||
export class ExpressServer {
|
||||
app: Express;
|
||||
system: System<SilverBulletHooks>;
|
||||
private rootPath: string;
|
||||
|
||||
constructor(
|
||||
app: Express,
|
||||
rootPath: string,
|
||||
distDir: string,
|
||||
system: System<SilverBulletHooks>
|
||||
) {
|
||||
this.app = app;
|
||||
this.rootPath = rootPath;
|
||||
this.system = system;
|
||||
|
||||
app.use(exposeSystem(this.system));
|
||||
|
||||
// Fallback, serve index.html
|
||||
let cachedIndex: string | undefined = undefined;
|
||||
app.get("/*", async (req, res) => {
|
||||
if (!cachedIndex) {
|
||||
cachedIndex = await readFile(`${distDir}/index.html`, "utf8");
|
||||
}
|
||||
res.status(200).header("Content-Type", "text/html").send(cachedIndex);
|
||||
});
|
||||
}
|
||||
|
||||
async init() {}
|
||||
}
|
|
@ -10,20 +10,20 @@ import path from "path";
|
|||
import { stat } from "fs/promises";
|
||||
import { Cursor, cursorEffect } from "../webapp/cursorEffect";
|
||||
import { System } from "../plugbox/runtime";
|
||||
import { NuggetHook } from "../webapp/types";
|
||||
import { SilverBulletHooks } from "../common/manifest";
|
||||
|
||||
export class PageApi implements ApiProvider {
|
||||
openPages: Map<string, Page>;
|
||||
pageStore: DiskStorage;
|
||||
rootPath: string;
|
||||
connectedSockets: Set<Socket>;
|
||||
private system: System<NuggetHook>;
|
||||
private system: System<SilverBulletHooks>;
|
||||
|
||||
constructor(
|
||||
rootPath: string,
|
||||
connectedSockets: Set<Socket>,
|
||||
openPages: Map<string, Page>,
|
||||
system: System<NuggetHook>
|
||||
system: System<SilverBulletHooks>
|
||||
) {
|
||||
this.pageStore = new DiskStorage(rootPath);
|
||||
this.rootPath = rootPath;
|
||||
|
@ -34,6 +34,20 @@ export class PageApi implements ApiProvider {
|
|||
|
||||
async init(): Promise<void> {
|
||||
this.fileWatcher();
|
||||
this.system.on({
|
||||
plugUpdated: (plugName, plugDef) => {
|
||||
console.log("Plug updated on disk, broadcasting to all clients");
|
||||
this.connectedSockets.forEach((socket) => {
|
||||
socket.emit("plugUpdated", plugName, plugDef);
|
||||
});
|
||||
},
|
||||
plugRemoved: (plugName) => {
|
||||
console.log("Plug removed on disk, broadcasting to all clients");
|
||||
this.connectedSockets.forEach((socket) => {
|
||||
socket.emit("plugRemoved", plugName);
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
broadcastCursors(page: Page) {
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
import express from "express";
|
||||
import { readFile } from "fs/promises";
|
||||
import http from "http";
|
||||
import { Server } from "socket.io";
|
||||
import { SocketServer } from "./api_server";
|
||||
import yargs from "yargs";
|
||||
import { hideBin } from "yargs/helpers";
|
||||
import { System } from "../plugbox/runtime";
|
||||
import { SilverBulletHooks } from "../common/manifest";
|
||||
import { ExpressServer } from "./express_server";
|
||||
import { DiskPlugLoader } from "../plugbox/plug_loader";
|
||||
|
||||
let args = yargs(hideBin(process.argv))
|
||||
.option("debug", {
|
||||
|
@ -16,8 +19,12 @@ let args = yargs(hideBin(process.argv))
|
|||
})
|
||||
.parse();
|
||||
|
||||
const pagesPath = args._[0] as string;
|
||||
|
||||
const app = express();
|
||||
const server = http.createServer(app);
|
||||
const system = new System<SilverBulletHooks>();
|
||||
|
||||
const io = new Server(server, {
|
||||
cors: {
|
||||
methods: "GET,HEAD,PUT,OPTIONS,POST,DELETE",
|
||||
|
@ -29,18 +36,26 @@ const port = args.port;
|
|||
const distDir = `${__dirname}/../webapp`;
|
||||
|
||||
app.use("/", express.static(distDir));
|
||||
let socketServer = new SocketServer(args._[0] as string, io);
|
||||
socketServer.init();
|
||||
|
||||
// Fallback, serve index.html
|
||||
let cachedIndex: string | undefined = undefined;
|
||||
app.get("/*", async (req, res) => {
|
||||
if (!cachedIndex) {
|
||||
cachedIndex = await readFile(`${distDir}/index.html`, "utf8");
|
||||
}
|
||||
res.status(200).header("Content-Type", "text/html").send(cachedIndex);
|
||||
let socketServer = new SocketServer(pagesPath, io, system);
|
||||
socketServer.init().catch((e) => {
|
||||
console.error(e);
|
||||
});
|
||||
|
||||
server.listen(port, () => {
|
||||
console.log(`Server listening on port ${port}`);
|
||||
});
|
||||
const expressServer = new ExpressServer(app, pagesPath, distDir, system);
|
||||
expressServer
|
||||
.init()
|
||||
.then(async () => {
|
||||
let plugLoader = new DiskPlugLoader(
|
||||
system,
|
||||
`${__dirname}/../../plugs/dist`
|
||||
);
|
||||
await plugLoader.loadPlugs();
|
||||
plugLoader.watcher();
|
||||
server.listen(port, () => {
|
||||
console.log(`Server listening on port ${port}`);
|
||||
});
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error(e);
|
||||
});
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
import { promisify } from "util";
|
||||
import { execFile } from "child_process";
|
||||
|
||||
const execFilePromise = promisify(execFile);
|
||||
|
||||
export default function (cwd: string) {
|
||||
return {
|
||||
"shell.run": async (cmd: string, args: string[]) => {
|
||||
let { stdout, stderr } = await execFilePromise(cmd, args, {
|
||||
cwd: cwd,
|
||||
});
|
||||
return { stdout, stderr };
|
||||
},
|
||||
};
|
||||
}
|
|
@ -17,5 +17,5 @@ window.editor = editor;
|
|||
navigator.serviceWorker
|
||||
.register(new URL("service_worker.ts", import.meta.url), { type: "module" })
|
||||
.then((r) => {
|
||||
console.log("Service worker registered", r);
|
||||
// console.log("Service worker registered", r);
|
||||
});
|
||||
|
|
|
@ -17,7 +17,7 @@ import {
|
|||
} from "@codemirror/view";
|
||||
import { throttle } from "./util";
|
||||
import { Cursor, cursorEffect } from "./cursorEffect";
|
||||
import { EventEmitter } from "./event";
|
||||
import { EventEmitter } from "../common/event";
|
||||
|
||||
const throttleInterval = 250;
|
||||
|
||||
|
|
|
@ -19,7 +19,6 @@ import {
|
|||
KeyBinding,
|
||||
keymap,
|
||||
} from "@codemirror/view";
|
||||
// import { debounce } from "lodash";
|
||||
import React, { useEffect, useReducer } from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
import { Plug, System } from "../plugbox/runtime";
|
||||
|
@ -31,7 +30,6 @@ import { CommandPalette } from "./components/command_palette";
|
|||
import { PageNavigator } from "./components/page_navigator";
|
||||
import { TopBar } from "./components/top_bar";
|
||||
import { Cursor } from "./cursorEffect";
|
||||
import coreManifest from "./generated/core.plug.json";
|
||||
import { lineWrapper } from "./line_wrapper";
|
||||
import { markdown } from "./markdown";
|
||||
import { IPageNavigator, PathPageNavigator } from "./navigator";
|
||||
|
@ -49,9 +47,9 @@ import {
|
|||
AppCommand,
|
||||
AppViewState,
|
||||
initialViewState,
|
||||
NuggetHook,
|
||||
slashCommandRegexp,
|
||||
} from "./types";
|
||||
import { SilverBulletHooks } from "../common/manifest";
|
||||
import { safeRun } from "./util";
|
||||
|
||||
class PageState {
|
||||
|
@ -65,20 +63,19 @@ class PageState {
|
|||
}
|
||||
|
||||
export class Editor implements AppEventDispatcher {
|
||||
private system = new System<SilverBulletHooks>();
|
||||
editorView?: EditorView;
|
||||
viewState: AppViewState;
|
||||
viewDispatch: React.Dispatch<Action>;
|
||||
openPages: Map<string, PageState>;
|
||||
space: Space;
|
||||
editorCommands: Map<string, AppCommand>;
|
||||
plugs: Plug<NuggetHook>[];
|
||||
navigationResolve?: (val: undefined) => void;
|
||||
pageNavigator: IPageNavigator;
|
||||
|
||||
constructor(space: Space, parent: Element) {
|
||||
this.editorCommands = new Map();
|
||||
this.openPages = new Map();
|
||||
this.plugs = [];
|
||||
this.space = space;
|
||||
this.viewState = initialViewState;
|
||||
this.viewDispatch = () => {};
|
||||
|
@ -91,6 +88,13 @@ export class Editor implements AppEventDispatcher {
|
|||
parent: document.getElementById("editor")!,
|
||||
});
|
||||
this.pageNavigator = new PathPageNavigator();
|
||||
|
||||
this.system.registerSyscalls(
|
||||
dbSyscalls,
|
||||
editorSyscalls(this),
|
||||
spaceSyscalls(this),
|
||||
indexerSyscalls(this.space)
|
||||
);
|
||||
}
|
||||
|
||||
async init() {
|
||||
|
@ -128,6 +132,39 @@ export class Editor implements AppEventDispatcher {
|
|||
pages: pages,
|
||||
});
|
||||
},
|
||||
loadSystem: (systemJSON) => {
|
||||
safeRun(async () => {
|
||||
console.log("Received SYSTEM", systemJSON);
|
||||
await this.system.replaceAllFromJSON(systemJSON, () =>
|
||||
createIFrameSandbox(this.system)
|
||||
);
|
||||
console.log("Loaded plugs, now updating editor comands");
|
||||
this.editorCommands = new Map<string, AppCommand>();
|
||||
for (let plug of this.system.loadedPlugs.values()) {
|
||||
this.buildCommands(plug);
|
||||
}
|
||||
this.viewDispatch({
|
||||
type: "update-commands",
|
||||
commands: this.editorCommands,
|
||||
});
|
||||
});
|
||||
},
|
||||
plugUpdated: (plugName, plug) => {
|
||||
safeRun(async () => {
|
||||
console.log("Plug updated", plugName);
|
||||
await this.system.load(
|
||||
plugName,
|
||||
plug,
|
||||
createIFrameSandbox(this.system)
|
||||
);
|
||||
});
|
||||
},
|
||||
plugRemoved: (plugName) => {
|
||||
safeRun(async () => {
|
||||
console.log("Plug removed", plugName);
|
||||
await this.system.unload(plugName);
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
if (this.pageNavigator.getCurrentPage() === "") {
|
||||
|
@ -154,32 +191,16 @@ export class Editor implements AppEventDispatcher {
|
|||
}
|
||||
|
||||
async loadPlugs() {
|
||||
const system = new System<NuggetHook>();
|
||||
const system = new System<SilverBulletHooks>();
|
||||
system.registerSyscalls(
|
||||
dbSyscalls,
|
||||
editorSyscalls(this),
|
||||
spaceSyscalls(this),
|
||||
indexerSyscalls(this.space)
|
||||
);
|
||||
|
||||
console.log("Now loading core plug");
|
||||
let mainPlug = await system.load(
|
||||
"core",
|
||||
coreManifest,
|
||||
createIFrameSandbox(system)
|
||||
);
|
||||
this.plugs.push(mainPlug);
|
||||
this.editorCommands = new Map<string, AppCommand>();
|
||||
for (let plug of this.plugs) {
|
||||
this.buildCommands(plug);
|
||||
}
|
||||
this.viewDispatch({
|
||||
type: "update-commands",
|
||||
commands: this.editorCommands,
|
||||
});
|
||||
}
|
||||
|
||||
private buildCommands(plug: Plug<NuggetHook>) {
|
||||
private buildCommands(plug: Plug<SilverBulletHooks>) {
|
||||
const cmds = plug.manifest!.hooks.commands;
|
||||
for (let name in cmds) {
|
||||
let cmd = cmds[name];
|
||||
|
@ -192,18 +213,8 @@ export class Editor implements AppEventDispatcher {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: Parallelize?
|
||||
async dispatchAppEvent(name: AppEvent, data?: any): Promise<any[]> {
|
||||
let results: any[] = [];
|
||||
for (let plug of this.plugs) {
|
||||
let plugResults = await plug.dispatchEvent(name, data);
|
||||
if (plugResults) {
|
||||
for (let result of plugResults) {
|
||||
results.push(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
return results;
|
||||
return this.system.dispatchEvent(name, data);
|
||||
}
|
||||
|
||||
get currentPage(): string | undefined {
|
||||
|
@ -338,6 +349,7 @@ export class Editor implements AppEventDispatcher {
|
|||
|
||||
async plugCompleter(): Promise<CompletionResult | null> {
|
||||
let allCompletionResults = await this.dispatchAppEvent("editor:complete");
|
||||
console.log("Completion results", allCompletionResults);
|
||||
if (allCompletionResults.length === 1) {
|
||||
return allCompletionResults[0];
|
||||
} else if (allCompletionResults.length > 1) {
|
||||
|
|
|
@ -5,7 +5,9 @@ import { ChangeSet, Text, Transaction } from "@codemirror/state";
|
|||
|
||||
import { CollabDocument, CollabEvents } from "./collab";
|
||||
import { cursorEffect } from "./cursorEffect";
|
||||
import { EventEmitter } from "./event";
|
||||
import { EventEmitter } from "../common/event";
|
||||
import { SystemJSON } from "../plugbox/runtime";
|
||||
import { Manifest } from "../common/manifest";
|
||||
|
||||
export type SpaceEvents = {
|
||||
connect: () => void;
|
||||
|
@ -13,6 +15,9 @@ export type SpaceEvents = {
|
|||
pageChanged: (meta: PageMeta) => void;
|
||||
pageDeleted: (name: string) => void;
|
||||
pageListUpdated: (pages: Set<PageMeta>) => void;
|
||||
loadSystem: (systemJSON: SystemJSON<any>) => void;
|
||||
plugUpdated: (plugName: string, plug: Manifest) => void;
|
||||
plugRemoved: (plugName: string) => void;
|
||||
} & CollabEvents;
|
||||
|
||||
export type KV = {
|
||||
|
@ -35,6 +40,9 @@ export class Space extends EventEmitter<SpaceEvents> {
|
|||
"pageCreated",
|
||||
"pageChanged",
|
||||
"pageDeleted",
|
||||
"loadSystem",
|
||||
"plugUpdated",
|
||||
"plugRemoved",
|
||||
].forEach((eventName) => {
|
||||
socket.on(eventName, (...args) => {
|
||||
this.emit(eventName as keyof SpaceEvents, ...args);
|
||||
|
@ -54,7 +62,7 @@ export class Space extends EventEmitter<SpaceEvents> {
|
|||
break;
|
||||
}
|
||||
}
|
||||
if(!found) {
|
||||
if (!found) {
|
||||
this.allPages.add(meta);
|
||||
console.log("New page created", meta);
|
||||
this.emit("pageListUpdated", this.allPages);
|
||||
|
|
|
@ -1,12 +1,4 @@
|
|||
import * as plugbox from "../plugbox/types";
|
||||
|
||||
export type NuggetHook = {
|
||||
commands: {
|
||||
[key: string]: CommandDef;
|
||||
};
|
||||
};
|
||||
|
||||
export type Manifest = plugbox.Manifest<NuggetHook>;
|
||||
import { CommandDef } from "../common/manifest";
|
||||
|
||||
export type PageMeta = {
|
||||
name: string;
|
||||
|
@ -22,19 +14,6 @@ export type AppCommand = {
|
|||
|
||||
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 type Notification = {
|
||||
id: number;
|
||||
message: string;
|
||||
|
|
138
yarn.lock
138
yarn.lock
|
@ -1553,6 +1553,11 @@
|
|||
resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.4.1.tgz#bfd02c1f2224567676c1545199f87c3a861d878d"
|
||||
integrity sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==
|
||||
|
||||
"@types/cookiejar@*":
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/cookiejar/-/cookiejar-2.1.2.tgz#66ad9331f63fe8a3d3d9d8c6e3906dd10f6446e8"
|
||||
integrity sha512-t73xJJrvdTjXrn4jLS9VSGRbz0nUY3cl2DMGDU48lKl+HR9dbbjW2A9r3g40VA++mQpy6uuHg33gy7du2BKpog==
|
||||
|
||||
"@types/cors@^2.8.12":
|
||||
version "2.8.12"
|
||||
resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.12.tgz#6b2c510a7ad7039e98e7b8d3d6598f4359e5c080"
|
||||
|
@ -1621,6 +1626,11 @@
|
|||
resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a"
|
||||
integrity sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==
|
||||
|
||||
"@types/node-cron@^3.0.1":
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/node-cron/-/node-cron-3.0.1.tgz#e01a874d4c2aa1a02ebc64cfd1cd8ebdbad7a996"
|
||||
integrity sha512-BkMHHonDT8NJUE/pQ3kr5v2GLDKm5or9btLBoBx4F2MB2cuqYC748LYMDC55VlrLI5qZZv+Qgc3m4P3dBPcmeg==
|
||||
|
||||
"@types/node@*", "@types/node@>=10.0.0", "@types/node@^17.0.21":
|
||||
version "17.0.21"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.21.tgz#864b987c0c68d07b4345845c3e63b75edd143644"
|
||||
|
@ -1685,6 +1695,21 @@
|
|||
resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c"
|
||||
integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==
|
||||
|
||||
"@types/superagent@*":
|
||||
version "4.1.15"
|
||||
resolved "https://registry.yarnpkg.com/@types/superagent/-/superagent-4.1.15.tgz#63297de457eba5e2bc502a7609426c4cceab434a"
|
||||
integrity sha512-mu/N4uvfDN2zVQQ5AYJI/g4qxn2bHB6521t1UuH09ShNWjebTqN0ZFuYK9uYjcgmI0dTQEs+Owi1EO6U0OkOZQ==
|
||||
dependencies:
|
||||
"@types/cookiejar" "*"
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/supertest@^2.0.11":
|
||||
version "2.0.11"
|
||||
resolved "https://registry.yarnpkg.com/@types/supertest/-/supertest-2.0.11.tgz#2e70f69f220bc77b4f660d72c2e1a4231f44a77d"
|
||||
integrity sha512-uci4Esokrw9qGb9bvhhSVEjd6rkny/dk5PK/Qz4yxKiyppEI+dOPlNrZBahE3i+PoKFYyDxChVXZ/ysS/nrm1Q==
|
||||
dependencies:
|
||||
"@types/superagent" "*"
|
||||
|
||||
"@types/yargs-parser@*":
|
||||
version "21.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.0.tgz#0c60e537fa790f5f9472ed2776c2b71ec117351b"
|
||||
|
@ -1838,6 +1863,11 @@ array-flatten@1.1.1:
|
|||
resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2"
|
||||
integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=
|
||||
|
||||
asap@^2.0.0:
|
||||
version "2.0.6"
|
||||
resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46"
|
||||
integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=
|
||||
|
||||
asn1.js@^5.2.0:
|
||||
version "5.4.1"
|
||||
resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-5.4.1.tgz#11a980b84ebb91781ce35b0fdc2ee294e3783f07"
|
||||
|
@ -2395,7 +2425,7 @@ commander@^8.3.0:
|
|||
resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66"
|
||||
integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==
|
||||
|
||||
component-emitter@~1.3.0:
|
||||
component-emitter@^1.3.0, component-emitter@~1.3.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0"
|
||||
integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==
|
||||
|
@ -2451,6 +2481,11 @@ cookie@0.4.2, cookie@~0.4.1:
|
|||
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432"
|
||||
integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==
|
||||
|
||||
cookiejar@^2.1.3:
|
||||
version "2.1.3"
|
||||
resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.3.tgz#fc7a6216e408e74414b90230050842dacda75acc"
|
||||
integrity sha512-JxbCBUdrfr6AQjOXrxoTvAMJO4HBTUIlBzslcJPAz+/KT8yk53fXun51u+RenNYvad/+Vc2DIz5o9UxlCDymFQ==
|
||||
|
||||
core-util-is@~1.0.0:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85"
|
||||
|
@ -2672,7 +2707,7 @@ debug@2.6.9:
|
|||
dependencies:
|
||||
ms "2.0.0"
|
||||
|
||||
debug@4, debug@^4.1.0, debug@^4.1.1, debug@~4.3.1, debug@~4.3.2:
|
||||
debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.3, debug@~4.3.1, debug@~4.3.2:
|
||||
version "4.3.4"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
|
||||
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
|
||||
|
@ -2792,6 +2827,14 @@ dexie@^3.2.1:
|
|||
resolved "https://registry.yarnpkg.com/dexie/-/dexie-3.2.1.tgz#ef21456d725e700c1ab7ac4307896e4fdabaf753"
|
||||
integrity sha512-Y8oz3t2XC9hvjkP35B5I8rUkKKwM36GGRjWQCMjzIYScg7W+GHKDXobSYswkisW7CxL1/tKQtggMDsiWqDUc1g==
|
||||
|
||||
dezalgo@1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/dezalgo/-/dezalgo-1.0.3.tgz#7f742de066fc748bc8db820569dddce49bf0d456"
|
||||
integrity sha1-f3Qt4Gb8dIvI24IFad3c5Jvw1FY=
|
||||
dependencies:
|
||||
asap "^2.0.0"
|
||||
wrappy "1"
|
||||
|
||||
diff-sequences@^27.5.1:
|
||||
version "27.5.1"
|
||||
resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.5.1.tgz#eaecc0d327fd68c8d9672a1e64ab8dccb2ef5327"
|
||||
|
@ -3287,6 +3330,11 @@ fast-levenshtein@~2.0.6:
|
|||
resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
|
||||
integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=
|
||||
|
||||
fast-safe-stringify@^2.1.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz#c406a83b6e70d9e35ce3b30a81141df30aeba884"
|
||||
integrity sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==
|
||||
|
||||
fb-watchman@^2.0.0:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.1.tgz#fc84fb39d2709cf3ff6d743706157bb5708a8a85"
|
||||
|
@ -3341,6 +3389,25 @@ form-data@^3.0.0:
|
|||
combined-stream "^1.0.8"
|
||||
mime-types "^2.1.12"
|
||||
|
||||
form-data@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452"
|
||||
integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==
|
||||
dependencies:
|
||||
asynckit "^0.4.0"
|
||||
combined-stream "^1.0.8"
|
||||
mime-types "^2.1.12"
|
||||
|
||||
formidable@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/formidable/-/formidable-2.0.1.tgz#4310bc7965d185536f9565184dee74fbb75557ff"
|
||||
integrity sha512-rjTMNbp2BpfQShhFbR3Ruk3qk2y9jKpvMW78nJgx8QKtxjDVrwbZG+wvDOmVbifHyOUOQJXxqEy6r0faRrPzTQ==
|
||||
dependencies:
|
||||
dezalgo "1.0.3"
|
||||
hexoid "1.0.0"
|
||||
once "1.4.0"
|
||||
qs "6.9.3"
|
||||
|
||||
forwarded@0.2.0:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811"
|
||||
|
@ -3577,6 +3644,11 @@ hash.js@^1.0.0, hash.js@^1.0.3:
|
|||
inherits "^2.0.3"
|
||||
minimalistic-assert "^1.0.1"
|
||||
|
||||
hexoid@1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/hexoid/-/hexoid-1.0.0.tgz#ad10c6573fb907de23d9ec63a711267d9dc9bc18"
|
||||
integrity sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==
|
||||
|
||||
hmac-drbg@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1"
|
||||
|
@ -4666,7 +4738,7 @@ merge-stream@^2.0.0:
|
|||
resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60"
|
||||
integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==
|
||||
|
||||
methods@~1.1.2:
|
||||
methods@^1.1.2, methods@~1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
|
||||
integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=
|
||||
|
@ -4704,7 +4776,7 @@ mime@1.6.0:
|
|||
resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
|
||||
integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
|
||||
|
||||
mime@^2.4.4:
|
||||
mime@^2.4.4, mime@^2.5.0:
|
||||
version "2.6.0"
|
||||
resolved "https://registry.yarnpkg.com/mime/-/mime-2.6.0.tgz#a2a682a95cd4d0cb1d6257e28f83da7e35800367"
|
||||
integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==
|
||||
|
@ -4751,6 +4823,18 @@ mkdirp-classic@^0.5.2, mkdirp-classic@^0.5.3:
|
|||
resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113"
|
||||
integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==
|
||||
|
||||
moment-timezone@^0.5.31:
|
||||
version "0.5.34"
|
||||
resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.5.34.tgz#a75938f7476b88f155d3504a9343f7519d9a405c"
|
||||
integrity sha512-3zAEHh2hKUs3EXLESx/wsgw6IQdusOT8Bxm3D9UrHPQR7zlMmzwybC8zHEM1tQ4LJwP7fcxrWr8tuBg05fFCbg==
|
||||
dependencies:
|
||||
moment ">= 2.9.0"
|
||||
|
||||
"moment@>= 2.9.0":
|
||||
version "2.29.1"
|
||||
resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3"
|
||||
integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==
|
||||
|
||||
ms@2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
|
||||
|
@ -4823,6 +4907,13 @@ node-addon-api@^4.2.0:
|
|||
resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-4.3.0.tgz#52a1a0b475193e0928e98e0426a0d1254782b77f"
|
||||
integrity sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==
|
||||
|
||||
node-cron@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/node-cron/-/node-cron-3.0.0.tgz#b33252803e430f9cd8590cf85738efa1497a9522"
|
||||
integrity sha512-DDwIvvuCwrNiaU7HEivFDULcaQualDv7KoNlB/UU1wPW0n1tDEmBJKhEIE6DlF2FuoOHcNbLJ8ITL2Iv/3AWmA==
|
||||
dependencies:
|
||||
moment-timezone "^0.5.31"
|
||||
|
||||
node-gyp-build@^4.2.3, node-gyp-build@^4.3.0:
|
||||
version "4.3.0"
|
||||
resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.3.0.tgz#9f256b03e5826150be39c764bf51e993946d71a3"
|
||||
|
@ -4955,7 +5046,7 @@ on-finished@~2.3.0:
|
|||
dependencies:
|
||||
ee-first "1.1.1"
|
||||
|
||||
once@^1.3.0, once@^1.3.1, once@^1.4.0:
|
||||
once@1.4.0, once@^1.3.0, once@^1.3.1, once@^1.4.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
|
||||
integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
|
||||
|
@ -5538,11 +5629,23 @@ pupa@^2.1.1:
|
|||
dependencies:
|
||||
escape-goat "^2.0.0"
|
||||
|
||||
qs@6.9.3:
|
||||
version "6.9.3"
|
||||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.3.tgz#bfadcd296c2d549f1dffa560619132c977f5008e"
|
||||
integrity sha512-EbZYNarm6138UKKq46tdx08Yo/q9ZhFoAXAI1meAFd2GtbRDhbZY2WQSICskT0c5q99aFzLG1D4nvTk9tqfXIw==
|
||||
|
||||
qs@6.9.7:
|
||||
version "6.9.7"
|
||||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.7.tgz#4610846871485e1e048f44ae3b94033f0e675afe"
|
||||
integrity sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw==
|
||||
|
||||
qs@^6.10.1:
|
||||
version "6.10.3"
|
||||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.3.tgz#d6cde1b2ffca87b5aa57889816c5f81535e22e8e"
|
||||
integrity sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==
|
||||
dependencies:
|
||||
side-channel "^1.0.4"
|
||||
|
||||
querystring-es3@^0.2.1:
|
||||
version "0.2.1"
|
||||
resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73"
|
||||
|
@ -6120,6 +6223,31 @@ stylehacks@^*:
|
|||
browserslist "^4.16.6"
|
||||
postcss-selector-parser "^6.0.4"
|
||||
|
||||
superagent@^7.1.0:
|
||||
version "7.1.1"
|
||||
resolved "https://registry.yarnpkg.com/superagent/-/superagent-7.1.1.tgz#2ab187d38c3078c31c3771c0b751f10163a27136"
|
||||
integrity sha512-CQ2weSS6M+doIwwYFoMatklhRbx6sVNdB99OEJ5czcP3cng76Ljqus694knFWgOj3RkrtxZqIgpe6vhe0J7QWQ==
|
||||
dependencies:
|
||||
component-emitter "^1.3.0"
|
||||
cookiejar "^2.1.3"
|
||||
debug "^4.3.3"
|
||||
fast-safe-stringify "^2.1.1"
|
||||
form-data "^4.0.0"
|
||||
formidable "^2.0.1"
|
||||
methods "^1.1.2"
|
||||
mime "^2.5.0"
|
||||
qs "^6.10.1"
|
||||
readable-stream "^3.6.0"
|
||||
semver "^7.3.5"
|
||||
|
||||
supertest@^6.2.2:
|
||||
version "6.2.2"
|
||||
resolved "https://registry.yarnpkg.com/supertest/-/supertest-6.2.2.tgz#04a5998fd3efaff187cb69f07a169755d655b001"
|
||||
integrity sha512-wCw9WhAtKJsBvh07RaS+/By91NNE0Wh0DN19/hWPlBOU8tAfOtbZoVSV4xXeoKoxgPx0rx2y+y+8660XtE7jzg==
|
||||
dependencies:
|
||||
methods "^1.1.2"
|
||||
superagent "^7.1.0"
|
||||
|
||||
supports-color@^5.3.0, supports-color@^5.5.0:
|
||||
version "5.5.0"
|
||||
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
|
||||
|
|
Loading…
Reference in New Issue