Plug format now changed to YAML
This commit is contained in:
parent
a382ab3baa
commit
621e55dbcf
@ -4,8 +4,7 @@ import { CronHook } from "../plugbox/feature/node_cron";
|
|||||||
import { EventHook } from "../plugbox/feature/event";
|
import { EventHook } from "../plugbox/feature/event";
|
||||||
|
|
||||||
export type CommandDef = {
|
export type CommandDef = {
|
||||||
// Function name to invoke
|
name: string;
|
||||||
invoke: string;
|
|
||||||
|
|
||||||
// Bind to keyboard shortcut
|
// Bind to keyboard shortcut
|
||||||
key?: string;
|
key?: string;
|
||||||
@ -17,9 +16,7 @@ export type CommandDef = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type SilverBulletHooks = {
|
export type SilverBulletHooks = {
|
||||||
commands?: {
|
command?: CommandDef | CommandDef[];
|
||||||
[key: string]: CommandDef;
|
|
||||||
};
|
|
||||||
} & EndpointHook &
|
} & EndpointHook &
|
||||||
CronHook &
|
CronHook &
|
||||||
EventHook;
|
EventHook;
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
"watch": "rm -rf .parcel-cache && parcel watch",
|
"watch": "rm -rf .parcel-cache && parcel watch",
|
||||||
"build": "parcel build",
|
"build": "parcel build",
|
||||||
"clean": "rm -rf dist",
|
"clean": "rm -rf dist",
|
||||||
"plugs": "plugbox-bundle -w --dist plugs/dist plugs/core/core.plug.json plugs/git/git.plug.json",
|
"plugs": "plugbox-bundle -w --dist plugs/dist plugs/*/*.plug.yaml",
|
||||||
"server": "nodemon -w dist/server dist/server/server.js pages",
|
"server": "nodemon -w dist/server dist/server/server.js pages",
|
||||||
"test": "jest"
|
"test": "jest"
|
||||||
},
|
},
|
||||||
|
@ -8,6 +8,7 @@ import yargs from "yargs";
|
|||||||
import { hideBin } from "yargs/helpers";
|
import { hideBin } from "yargs/helpers";
|
||||||
import { Manifest } from "../types";
|
import { Manifest } from "../types";
|
||||||
import { watchFile } from "fs";
|
import { watchFile } from "fs";
|
||||||
|
import YAML from "yaml";
|
||||||
|
|
||||||
async function compile(filePath: string, functionName: string, debug: boolean) {
|
async function compile(filePath: string, functionName: string, debug: boolean) {
|
||||||
let outFile = "out.js";
|
let outFile = "out.js";
|
||||||
@ -48,7 +49,7 @@ export default ${functionName};`
|
|||||||
|
|
||||||
async function bundle(manifestPath: string, sourceMaps: boolean) {
|
async function bundle(manifestPath: string, sourceMaps: boolean) {
|
||||||
const rootPath = path.dirname(manifestPath);
|
const rootPath = path.dirname(manifestPath);
|
||||||
const manifest = JSON.parse(
|
const manifest = YAML.parse(
|
||||||
(await readFile(manifestPath)).toString()
|
(await readFile(manifestPath)).toString()
|
||||||
) as Manifest<any>;
|
) as Manifest<any>;
|
||||||
|
|
||||||
@ -71,7 +72,12 @@ async function buildManifest(
|
|||||||
debug: boolean
|
debug: boolean
|
||||||
) {
|
) {
|
||||||
let generatedManifest = await bundle(manifestPath, debug);
|
let generatedManifest = await bundle(manifestPath, debug);
|
||||||
const outPath = path.join(distPath, path.basename(manifestPath));
|
const outFile =
|
||||||
|
manifestPath.substring(
|
||||||
|
0,
|
||||||
|
manifestPath.length - path.extname(manifestPath).length
|
||||||
|
) + ".json";
|
||||||
|
const outPath = path.join(distPath, path.basename(outFile));
|
||||||
console.log("Emitting bundle to", outPath);
|
console.log("Emitting bundle to", outPath);
|
||||||
await writeFile(outPath, JSON.stringify(generatedManifest, null, 2));
|
await writeFile(outPath, JSON.stringify(generatedManifest, null, 2));
|
||||||
return { generatedManifest, outPath };
|
return { generatedManifest, outPath };
|
||||||
@ -93,7 +99,7 @@ async function run() {
|
|||||||
.parse();
|
.parse();
|
||||||
if (args._.length === 0) {
|
if (args._.length === 0) {
|
||||||
console.log(
|
console.log(
|
||||||
"Usage: plugbox-bundle [--debug] [--dist <path>] <manifest.plug.json> <manifest2.plug.json> ..."
|
"Usage: plugbox-bundle [--debug] [--dist <path>] <manifest.plug.yaml> <manifest2.plug.yaml> ..."
|
||||||
);
|
);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,8 @@ import {
|
|||||||
storeWriteSyscalls,
|
storeWriteSyscalls,
|
||||||
} from "../syscall/store.knex_node";
|
} from "../syscall/store.knex_node";
|
||||||
import { fetchSyscalls } from "../syscall/fetch.node";
|
import { fetchSyscalls } from "../syscall/fetch.node";
|
||||||
|
import { EventFeature, EventHook } from "../feature/event";
|
||||||
|
import { eventSyscalls } from "../syscall/event";
|
||||||
|
|
||||||
let args = yargs(hideBin(process.argv))
|
let args = yargs(hideBin(process.argv))
|
||||||
.option("port", {
|
.option("port", {
|
||||||
@ -33,7 +35,7 @@ const plugPath = args._[0] as string;
|
|||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
|
|
||||||
type ServerHook = EndpointHook & CronHook;
|
type ServerHook = EndpointHook & CronHook & EventHook;
|
||||||
const system = new System<ServerHook>("server");
|
const system = new System<ServerHook>("server");
|
||||||
|
|
||||||
safeRun(async () => {
|
safeRun(async () => {
|
||||||
@ -51,6 +53,9 @@ safeRun(async () => {
|
|||||||
await plugLoader.loadPlugs();
|
await plugLoader.loadPlugs();
|
||||||
plugLoader.watcher();
|
plugLoader.watcher();
|
||||||
system.addFeature(new NodeCronFeature());
|
system.addFeature(new NodeCronFeature());
|
||||||
|
let eventFeature = new EventFeature();
|
||||||
|
system.addFeature(eventFeature);
|
||||||
|
system.registerSyscalls("event", [], eventSyscalls(eventFeature));
|
||||||
system.addFeature(new EndpointFeature(app, ""));
|
system.addFeature(new EndpointFeature(app, ""));
|
||||||
system.registerSyscalls("shell", [], shellSyscalls("."));
|
system.registerSyscalls("shell", [], shellSyscalls("."));
|
||||||
system.registerSyscalls("fetch", [], fetchSyscalls());
|
system.registerSyscalls("fetch", [], fetchSyscalls());
|
||||||
|
@ -13,6 +13,9 @@ test("Run a plugbox endpoint server", async () => {
|
|||||||
{
|
{
|
||||||
functions: {
|
functions: {
|
||||||
testhandler: {
|
testhandler: {
|
||||||
|
http: {
|
||||||
|
path: "/",
|
||||||
|
},
|
||||||
code: `(() => {
|
code: `(() => {
|
||||||
return {
|
return {
|
||||||
default: (req) => {
|
default: (req) => {
|
||||||
@ -23,9 +26,6 @@ test("Run a plugbox endpoint server", async () => {
|
|||||||
})()`,
|
})()`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
hooks: {
|
|
||||||
endpoints: [{ method: "GET", path: "/", handler: "testhandler" }],
|
|
||||||
},
|
|
||||||
} as Manifest<EndpointHook>,
|
} as Manifest<EndpointHook>,
|
||||||
createSandbox
|
createSandbox
|
||||||
);
|
);
|
||||||
|
@ -17,13 +17,12 @@ export type EndpointResponse = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type EndpointHook = {
|
export type EndpointHook = {
|
||||||
endpoints?: EndPointDef[];
|
http?: EndPointDef | EndPointDef[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type EndPointDef = {
|
export type EndPointDef = {
|
||||||
method: "GET" | "POST" | "PUT" | "DELETE" | "HEAD" | "OPTIONS";
|
method?: "GET" | "POST" | "PUT" | "DELETE" | "HEAD" | "OPTIONS" | "ANY";
|
||||||
path: string;
|
path: string;
|
||||||
handler: string; // function name
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export class EndpointFeature implements Feature<EndpointHook> {
|
export class EndpointFeature implements Feature<EndpointHook> {
|
||||||
@ -43,35 +42,42 @@ export class EndpointFeature implements Feature<EndpointHook> {
|
|||||||
console.log("Endpoint request", req.path);
|
console.log("Endpoint request", req.path);
|
||||||
Promise.resolve()
|
Promise.resolve()
|
||||||
.then(async () => {
|
.then(async () => {
|
||||||
|
// Iterate over all loaded plugins
|
||||||
for (const [plugName, plug] of system.loadedPlugs.entries()) {
|
for (const [plugName, plug] of system.loadedPlugs.entries()) {
|
||||||
const manifest = plug.manifest;
|
const manifest = plug.manifest;
|
||||||
if (!manifest) {
|
if (!manifest) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const endpoints = manifest.hooks?.endpoints;
|
const functions = manifest.functions;
|
||||||
console.log("Checking plug", plugName, endpoints);
|
console.log("Checking plug", plugName);
|
||||||
if (endpoints) {
|
let prefix = `${this.prefix}/${plugName}`;
|
||||||
let prefix = `${this.prefix}/${plugName}`;
|
if (!req.path.startsWith(prefix)) {
|
||||||
console.log("Need prefix", prefix, "got", req.path);
|
continue;
|
||||||
if (!req.path.startsWith(prefix)) {
|
}
|
||||||
|
for (const [name, functionDef] of Object.entries(functions)) {
|
||||||
|
if (!functionDef.http) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
for (const { path, method, handler } of endpoints) {
|
let endpoints = Array.isArray(functionDef.http)
|
||||||
|
? functionDef.http
|
||||||
|
: [functionDef.http];
|
||||||
|
console.log(endpoints);
|
||||||
|
for (const { path, method } of endpoints) {
|
||||||
let prefixedPath = `${prefix}${path}`;
|
let prefixedPath = `${prefix}${path}`;
|
||||||
if (prefixedPath === req.path && method === req.method) {
|
if (
|
||||||
|
prefixedPath === req.path &&
|
||||||
|
((method || "GET") === req.method || method === "ANY")
|
||||||
|
) {
|
||||||
try {
|
try {
|
||||||
const response: EndpointResponse = await plug.invoke(
|
const response: EndpointResponse = await plug.invoke(name, [
|
||||||
handler,
|
{
|
||||||
[
|
path: req.path,
|
||||||
{
|
method: req.method,
|
||||||
path: req.path,
|
body: req.body,
|
||||||
method: req.method,
|
query: req.query,
|
||||||
body: req.body,
|
headers: req.headers,
|
||||||
query: req.query,
|
} as EndpointRequest,
|
||||||
headers: req.headers,
|
]);
|
||||||
} as EndpointRequest,
|
|
||||||
]
|
|
||||||
);
|
|
||||||
let resp = res.status(response.status);
|
let resp = res.status(response.status);
|
||||||
if (response.headers) {
|
if (response.headers) {
|
||||||
for (const [key, value] of Object.entries(
|
for (const [key, value] of Object.entries(
|
||||||
@ -101,21 +107,26 @@ export class EndpointFeature implements Feature<EndpointHook> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
validateManifest(manifest: Manifest<EndpointHook>): string[] {
|
validateManifest(manifest: Manifest<EndpointHook>): string[] {
|
||||||
const endpoints = manifest.hooks.endpoints;
|
|
||||||
let errors = [];
|
let errors = [];
|
||||||
if (endpoints) {
|
for (const [name, functionDef] of Object.entries(manifest.functions)) {
|
||||||
for (let { method, path, handler } of endpoints) {
|
if (!functionDef.http) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let endpoints = Array.isArray(functionDef.http)
|
||||||
|
? functionDef.http
|
||||||
|
: [functionDef.http];
|
||||||
|
for (let { path, method } of endpoints) {
|
||||||
if (!path) {
|
if (!path) {
|
||||||
errors.push("Path not defined for endpoint");
|
errors.push("Path not defined for endpoint");
|
||||||
}
|
}
|
||||||
if (["GET", "POST", "PUT", "DELETE"].indexOf(method) === -1) {
|
if (
|
||||||
|
method &&
|
||||||
|
["GET", "POST", "PUT", "DELETE", "ANY"].indexOf(method) === -1
|
||||||
|
) {
|
||||||
errors.push(
|
errors.push(
|
||||||
`Invalid method ${method} for end point with with ${path}`
|
`Invalid method ${method} for end point with with ${path}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (!manifest.functions[handler]) {
|
|
||||||
errors.push(`Endpoint handler function ${handler} not found`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return errors;
|
return errors;
|
||||||
|
@ -2,29 +2,24 @@ import { Feature, Manifest } from "../types";
|
|||||||
import { System } from "../system";
|
import { System } from "../system";
|
||||||
|
|
||||||
export type EventHook = {
|
export type EventHook = {
|
||||||
events?: { [key: string]: string[] };
|
events?: string[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export class EventFeature implements Feature<EventHook> {
|
export class EventFeature implements Feature<EventHook> {
|
||||||
private system?: System<EventHook>;
|
private system?: System<EventHook>;
|
||||||
|
|
||||||
async dispatchEvent(name: string, data?: any): Promise<any[]> {
|
async dispatchEvent(eventName: string, data?: any): Promise<any[]> {
|
||||||
if (!this.system) {
|
if (!this.system) {
|
||||||
throw new Error("EventFeature is not initialized");
|
throw new Error("EventFeature is not initialized");
|
||||||
}
|
}
|
||||||
let promises: Promise<any>[] = [];
|
let promises: Promise<any>[] = [];
|
||||||
for (const plug of this.system.loadedPlugs.values()) {
|
for (const plug of this.system.loadedPlugs.values()) {
|
||||||
if (!plug.manifest!.hooks?.events) {
|
for (const [name, functionDef] of Object.entries(
|
||||||
continue;
|
plug.manifest!.functions
|
||||||
}
|
)) {
|
||||||
let functionsToSpawn = plug.manifest!.hooks.events[name];
|
if (functionDef.events && functionDef.events.includes(eventName)) {
|
||||||
if (functionsToSpawn) {
|
promises.push(plug.invoke(name, [data]));
|
||||||
functionsToSpawn.forEach((functionToSpawn) => {
|
}
|
||||||
// Only dispatch functions on events when they're allowed to be invoked in this environment
|
|
||||||
if (plug.canInvoke(functionToSpawn)) {
|
|
||||||
promises.push(plug.invoke(functionToSpawn, [data]));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Promise.all(promises);
|
return Promise.all(promises);
|
||||||
@ -32,12 +27,15 @@ export class EventFeature implements Feature<EventHook> {
|
|||||||
|
|
||||||
apply(system: System<EventHook>): void {
|
apply(system: System<EventHook>): void {
|
||||||
this.system = system;
|
this.system = system;
|
||||||
system.on({
|
|
||||||
plugLoaded: (name, plug) => {},
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
validateManifest(manifest: Manifest<EventHook>): string[] {
|
validateManifest(manifest: Manifest<EventHook>): string[] {
|
||||||
return [];
|
let errors = [];
|
||||||
|
for (const [name, functionDef] of Object.entries(manifest.functions)) {
|
||||||
|
if (functionDef.events && !Array.isArray(functionDef.events)) {
|
||||||
|
errors.push("'events' key must be an array of strings");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return errors;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,12 +4,7 @@ import { safeRun } from "../util";
|
|||||||
import { System } from "../system";
|
import { System } from "../system";
|
||||||
|
|
||||||
export type CronHook = {
|
export type CronHook = {
|
||||||
crons?: CronDef[];
|
cron?: string | string[];
|
||||||
};
|
|
||||||
|
|
||||||
export type CronDef = {
|
|
||||||
cron: string;
|
|
||||||
handler: string; // function name
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export class NodeCronFeature implements Feature<CronHook> {
|
export class NodeCronFeature implements Feature<CronHook> {
|
||||||
@ -27,19 +22,28 @@ export class NodeCronFeature implements Feature<CronHook> {
|
|||||||
reloadCrons();
|
reloadCrons();
|
||||||
|
|
||||||
function reloadCrons() {
|
function reloadCrons() {
|
||||||
// ts-ignore
|
|
||||||
tasks.forEach((task) => task.stop());
|
tasks.forEach((task) => task.stop());
|
||||||
tasks = [];
|
tasks = [];
|
||||||
for (let plug of system.loadedPlugs.values()) {
|
for (let plug of system.loadedPlugs.values()) {
|
||||||
const crons = plug.manifest?.hooks?.crons;
|
if (!plug.manifest) {
|
||||||
if (crons) {
|
continue;
|
||||||
|
}
|
||||||
|
for (const [name, functionDef] of Object.entries(
|
||||||
|
plug.manifest.functions
|
||||||
|
)) {
|
||||||
|
if (!functionDef.cron) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const crons = Array.isArray(functionDef.cron)
|
||||||
|
? functionDef.cron
|
||||||
|
: [functionDef.cron];
|
||||||
for (let cronDef of crons) {
|
for (let cronDef of crons) {
|
||||||
tasks.push(
|
tasks.push(
|
||||||
cron.schedule(cronDef.cron, () => {
|
cron.schedule(cronDef, () => {
|
||||||
console.log("Now acting on cron", cronDef.cron);
|
console.log("Now acting on cron", cronDef);
|
||||||
safeRun(async () => {
|
safeRun(async () => {
|
||||||
try {
|
try {
|
||||||
await plug.invoke(cronDef.handler, []);
|
await plug.invoke(name, [cronDef]);
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error("Execution of cron function failed", e);
|
console.error("Execution of cron function failed", e);
|
||||||
}
|
}
|
||||||
@ -53,15 +57,17 @@ export class NodeCronFeature implements Feature<CronHook> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
validateManifest(manifest: Manifest<CronHook>): string[] {
|
validateManifest(manifest: Manifest<CronHook>): string[] {
|
||||||
const crons = manifest.hooks.crons;
|
|
||||||
let errors = [];
|
let errors = [];
|
||||||
if (crons) {
|
for (const [name, functionDef] of Object.entries(manifest.functions)) {
|
||||||
|
if (!functionDef.cron) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const crons = Array.isArray(functionDef.cron)
|
||||||
|
? functionDef.cron
|
||||||
|
: [functionDef.cron];
|
||||||
for (let cronDef of crons) {
|
for (let cronDef of crons) {
|
||||||
if (!cron.validate(cronDef.cron)) {
|
if (!cron.validate(cronDef)) {
|
||||||
errors.push(`Invalid cron expression ${cronDef.cron}`);
|
errors.push(`Invalid cron expression ${cronDef}`);
|
||||||
}
|
|
||||||
if (!manifest.functions[cronDef.handler]) {
|
|
||||||
errors.push(`Cron handler function ${cronDef.handler} not found`);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -82,9 +82,6 @@ test("Run a Node sandbox", async () => {
|
|||||||
})()`,
|
})()`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
hooks: {
|
|
||||||
events: {},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
createSandbox
|
createSandbox
|
||||||
);
|
);
|
||||||
|
10
plugbox/syscall/event.ts
Normal file
10
plugbox/syscall/event.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import { SysCallMapping } from "../system";
|
||||||
|
import { EventFeature } from "../feature/event";
|
||||||
|
|
||||||
|
export function eventSyscalls(eventFeature: EventFeature): SysCallMapping {
|
||||||
|
return {
|
||||||
|
async dispatch(ctx, eventName: string, data: any) {
|
||||||
|
return eventFeature.dispatchEvent(eventName, data);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
@ -3,11 +3,11 @@ import { SysCallMapping } from "../system";
|
|||||||
|
|
||||||
export function fetchSyscalls(): SysCallMapping {
|
export function fetchSyscalls(): SysCallMapping {
|
||||||
return {
|
return {
|
||||||
async fetchJson(ctx, url: RequestInfo, init: RequestInit) {
|
async json(ctx, url: RequestInfo, init: RequestInit) {
|
||||||
let resp = await fetch(url, init);
|
let resp = await fetch(url, init);
|
||||||
return resp.json();
|
return resp.json();
|
||||||
},
|
},
|
||||||
async fetchText(ctx, url: RequestInfo, init: RequestInit) {
|
async text(ctx, url: RequestInfo, init: RequestInit) {
|
||||||
let resp = await fetch(url, init);
|
let resp = await fetch(url, init);
|
||||||
return resp.text();
|
return resp.text();
|
||||||
},
|
},
|
||||||
|
@ -12,7 +12,6 @@ test("Test store", async () => {
|
|||||||
let plug = await system.load(
|
let plug = await system.load(
|
||||||
"test",
|
"test",
|
||||||
{
|
{
|
||||||
hooks: {},
|
|
||||||
functions: {
|
functions: {
|
||||||
test1: {
|
test1: {
|
||||||
code: `(() => {
|
code: `(() => {
|
||||||
|
@ -28,7 +28,6 @@ test("Test store", async () => {
|
|||||||
let plug = await system.load(
|
let plug = await system.load(
|
||||||
"test",
|
"test",
|
||||||
{
|
{
|
||||||
hooks: {},
|
|
||||||
functions: {
|
functions: {
|
||||||
test1: {
|
test1: {
|
||||||
code: `(() => {
|
code: `(() => {
|
||||||
|
@ -2,17 +2,16 @@ import { System } from "./system";
|
|||||||
|
|
||||||
export interface Manifest<HookT> {
|
export interface Manifest<HookT> {
|
||||||
requiredPermissions?: string[];
|
requiredPermissions?: string[];
|
||||||
hooks: HookT;
|
|
||||||
functions: {
|
functions: {
|
||||||
[key: string]: FunctionDef;
|
[key: string]: FunctionDef<HookT>;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FunctionDef {
|
export type FunctionDef<HookT> = {
|
||||||
path?: string;
|
path?: string;
|
||||||
code?: string;
|
code?: string;
|
||||||
env?: RuntimeEnvironment;
|
env?: RuntimeEnvironment;
|
||||||
}
|
} & HookT;
|
||||||
|
|
||||||
export type RuntimeEnvironment = "client" | "server";
|
export type RuntimeEnvironment = "client" | "server";
|
||||||
|
|
||||||
|
@ -1,95 +0,0 @@
|
|||||||
{
|
|
||||||
"hooks": {
|
|
||||||
"commands": {
|
|
||||||
"Navigate To page": {
|
|
||||||
"invoke": "linkNavigate",
|
|
||||||
"key": "Ctrl-Enter",
|
|
||||||
"mac": "Cmd-Enter"
|
|
||||||
},
|
|
||||||
"Insert Current Date": {
|
|
||||||
"invoke": "insertToday",
|
|
||||||
"slashCommand": "today"
|
|
||||||
},
|
|
||||||
"Toggle : Heading 1": {
|
|
||||||
"invoke": "toggle_h1",
|
|
||||||
"mac": "Cmd-1",
|
|
||||||
"key": "Ctrl-1"
|
|
||||||
},
|
|
||||||
"Toggle : Heading 2": {
|
|
||||||
"invoke": "toggle_h2",
|
|
||||||
"mac": "Cmd-2",
|
|
||||||
"key": "Ctrl-2"
|
|
||||||
},
|
|
||||||
"Page: Delete": {
|
|
||||||
"invoke": "deletePage"
|
|
||||||
},
|
|
||||||
"Page: Rename": {
|
|
||||||
"invoke": "renamePage"
|
|
||||||
},
|
|
||||||
"Pages: Reindex": {
|
|
||||||
"invoke": "reindexPages"
|
|
||||||
},
|
|
||||||
"Pages: Back Links": {
|
|
||||||
"invoke": "showBackLinks"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"events": {
|
|
||||||
"page:click": ["taskToggle", "clickNavigate"],
|
|
||||||
"editor:complete": ["pageComplete"],
|
|
||||||
"page:index": ["indexLinks"],
|
|
||||||
"load": ["welcome"]
|
|
||||||
},
|
|
||||||
"endpoints": [
|
|
||||||
{
|
|
||||||
"method": "GET",
|
|
||||||
"path": "/",
|
|
||||||
"handler": "endpointTest"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"functions": {
|
|
||||||
"indexLinks": {
|
|
||||||
"path": "./page.ts:indexLinks"
|
|
||||||
},
|
|
||||||
"deletePage": {
|
|
||||||
"path": "./page.ts:deletePage"
|
|
||||||
},
|
|
||||||
"showBackLinks": {
|
|
||||||
"path": "./page.ts:showBackLinks"
|
|
||||||
},
|
|
||||||
"renamePage": {
|
|
||||||
"path": "./page.ts:renamePage"
|
|
||||||
},
|
|
||||||
"reindexPages": {
|
|
||||||
"path": "./page.ts:reindex"
|
|
||||||
},
|
|
||||||
"pageComplete": {
|
|
||||||
"path": "./navigate.ts:pageComplete"
|
|
||||||
},
|
|
||||||
"linkNavigate": {
|
|
||||||
"path": "./navigate.ts:linkNavigate"
|
|
||||||
},
|
|
||||||
"clickNavigate": {
|
|
||||||
"path": "./navigate.ts:clickNavigate"
|
|
||||||
},
|
|
||||||
"taskToggle": {
|
|
||||||
"path": "./task.ts:taskToggle"
|
|
||||||
},
|
|
||||||
"insertToday": {
|
|
||||||
"path": "./dates.ts:insertToday"
|
|
||||||
},
|
|
||||||
"toggle_h1": {
|
|
||||||
"path": "./markup.ts:toggleH1"
|
|
||||||
},
|
|
||||||
"toggle_h2": {
|
|
||||||
"path": "./markup.ts:toggleH2"
|
|
||||||
},
|
|
||||||
"endpointTest": {
|
|
||||||
"path": "./server.ts:endpointTest"
|
|
||||||
},
|
|
||||||
"welcome": {
|
|
||||||
"path": "./server.ts:welcome",
|
|
||||||
"env": "server"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
46
plugs/core/core.plug.yaml
Normal file
46
plugs/core/core.plug.yaml
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
functions:
|
||||||
|
indexLinks:
|
||||||
|
path: "./page.ts:indexLinks"
|
||||||
|
events:
|
||||||
|
- page:index
|
||||||
|
deletePage:
|
||||||
|
path: "./page.ts:deletePage"
|
||||||
|
command:
|
||||||
|
name: "Page: Delete"
|
||||||
|
showBackLinks:
|
||||||
|
path: "./page.ts:showBackLinks"
|
||||||
|
command:
|
||||||
|
name: "Page: Back Links"
|
||||||
|
renamePage:
|
||||||
|
path: "./page.ts:renamePage"
|
||||||
|
command:
|
||||||
|
name: "Page: Rename"
|
||||||
|
pageComplete:
|
||||||
|
path: "./navigate.ts:pageComplete"
|
||||||
|
events:
|
||||||
|
- editor:complete
|
||||||
|
linkNavigate:
|
||||||
|
path: "./navigate.ts:linkNavigate"
|
||||||
|
command:
|
||||||
|
name: Navigate To page
|
||||||
|
key: Ctrl-Enter
|
||||||
|
mac: Cmd-Enter
|
||||||
|
clickNavigate:
|
||||||
|
path: "./navigate.ts:clickNavigate"
|
||||||
|
events:
|
||||||
|
- page:click
|
||||||
|
taskToggle:
|
||||||
|
path: "./task.ts:taskToggle"
|
||||||
|
events:
|
||||||
|
- page:click
|
||||||
|
insertToday:
|
||||||
|
path: "./dates.ts:insertToday"
|
||||||
|
command:
|
||||||
|
name: Insert Current Date
|
||||||
|
slashCommand: today
|
||||||
|
welcome:
|
||||||
|
path: "./server.ts:welcome"
|
||||||
|
events:
|
||||||
|
- load
|
||||||
|
env: server
|
||||||
|
|
@ -1,37 +0,0 @@
|
|||||||
{
|
|
||||||
"requiredPermissions": ["shell"],
|
|
||||||
"hooks": {
|
|
||||||
"commands": {
|
|
||||||
"Git: Snapshot": {
|
|
||||||
"invoke": "snapshotCommand"
|
|
||||||
},
|
|
||||||
"Git: Sync": {
|
|
||||||
"invoke": "syncCommand"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"crons": [
|
|
||||||
{
|
|
||||||
"cron": "*/15 * * * *",
|
|
||||||
"handler": "commit"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"functions": {
|
|
||||||
"snapshotCommand": {
|
|
||||||
"path": "./git.ts:snapshotCommand",
|
|
||||||
"env": "client"
|
|
||||||
},
|
|
||||||
"syncCommand": {
|
|
||||||
"path": "./git.ts:syncCommand",
|
|
||||||
"env": "client"
|
|
||||||
},
|
|
||||||
"commit": {
|
|
||||||
"path": "./git.ts:commit",
|
|
||||||
"env": "server"
|
|
||||||
},
|
|
||||||
"sync": {
|
|
||||||
"path": "./git.ts:sync",
|
|
||||||
"env": "server"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
22
plugs/git/git.plug.yaml
Normal file
22
plugs/git/git.plug.yaml
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
requiredPermissions:
|
||||||
|
- shell
|
||||||
|
functions:
|
||||||
|
snapshotCommand:
|
||||||
|
path: "./git.ts:snapshotCommand"
|
||||||
|
env: client
|
||||||
|
command:
|
||||||
|
name: "Git: Snapshot"
|
||||||
|
syncCommand:
|
||||||
|
path: "./git.ts:syncCommand"
|
||||||
|
env: client
|
||||||
|
command:
|
||||||
|
name: "Git: Sync"
|
||||||
|
commit:
|
||||||
|
path: "./git.ts:commit"
|
||||||
|
env: server
|
||||||
|
cron:
|
||||||
|
- "*/15 * * * *"
|
||||||
|
sync:
|
||||||
|
path: "./git.ts:sync"
|
||||||
|
env: server
|
||||||
|
|
@ -9,16 +9,16 @@ import {hideBin} from "yargs/helpers";
|
|||||||
import {SilverBulletHooks} from "../common/manifest";
|
import {SilverBulletHooks} from "../common/manifest";
|
||||||
import {ExpressServer} from "./express_server";
|
import {ExpressServer} from "./express_server";
|
||||||
import {DiskPlugLoader} from "../plugbox/plug_loader";
|
import {DiskPlugLoader} from "../plugbox/plug_loader";
|
||||||
import { NodeCronFeature } from "../plugbox/feature/node_cron";
|
import {NodeCronFeature} from "../plugbox/feature/node_cron";
|
||||||
import shellSyscalls from "../plugbox/syscall/shell.node";
|
import shellSyscalls from "../plugbox/syscall/shell.node";
|
||||||
import { System } from "../plugbox/system";
|
import {System} from "../plugbox/system";
|
||||||
|
|
||||||
let args = yargs(hideBin(process.argv))
|
let args = yargs(hideBin(process.argv))
|
||||||
.option("port", {
|
.option("port", {
|
||||||
type: "number",
|
type: "number",
|
||||||
default: 3000,
|
default: 3000,
|
||||||
})
|
})
|
||||||
.parse();
|
.parse();
|
||||||
|
|
||||||
if (!args._.length) {
|
if (!args._.length) {
|
||||||
console.error("Usage: silverbullet <path-to-pages>");
|
console.error("Usage: silverbullet <path-to-pages>");
|
||||||
@ -50,20 +50,20 @@ socketServer.init().catch((e) => {
|
|||||||
|
|
||||||
const expressServer = new ExpressServer(app, pagesPath, distDir, system);
|
const expressServer = new ExpressServer(app, pagesPath, distDir, system);
|
||||||
expressServer
|
expressServer
|
||||||
.init()
|
.init()
|
||||||
.then(async () => {
|
.then(async () => {
|
||||||
let plugLoader = new DiskPlugLoader(
|
let plugLoader = new DiskPlugLoader(
|
||||||
system,
|
system,
|
||||||
`${__dirname}/../../plugs/dist`
|
`${__dirname}/../../plugs/dist`
|
||||||
);
|
);
|
||||||
await plugLoader.loadPlugs();
|
await plugLoader.loadPlugs();
|
||||||
plugLoader.watcher();
|
plugLoader.watcher();
|
||||||
system.registerSyscalls("shell", ["shell"], shellSyscalls(pagesPath));
|
system.registerSyscalls("shell", ["shell"], shellSyscalls(pagesPath));
|
||||||
system.addFeature(new NodeCronFeature());
|
system.addFeature(new NodeCronFeature());
|
||||||
server.listen(port, () => {
|
server.listen(port, () => {
|
||||||
console.log(`Server listening on port ${port}`);
|
console.log(`Server listening on port ${port}`);
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch((e) => {
|
|
||||||
console.error(e);
|
|
||||||
});
|
});
|
||||||
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
console.error(e);
|
||||||
|
});
|
||||||
|
@ -163,15 +163,23 @@ export class Editor implements AppEventDispatcher {
|
|||||||
console.log("Loaded plugs, now updating editor commands");
|
console.log("Loaded plugs, now updating editor commands");
|
||||||
this.editorCommands.clear();
|
this.editorCommands.clear();
|
||||||
for (let plug of this.system.loadedPlugs.values()) {
|
for (let plug of this.system.loadedPlugs.values()) {
|
||||||
const cmds = plug.manifest!.hooks.commands;
|
for (const [name, functionDef] of Object.entries(
|
||||||
for (let name in cmds) {
|
plug.manifest!.functions
|
||||||
let cmd = cmds[name];
|
)) {
|
||||||
this.editorCommands.set(name, {
|
if (!functionDef.command) {
|
||||||
command: cmd,
|
continue;
|
||||||
run: async (arg): Promise<any> => {
|
}
|
||||||
return await plug.invoke(cmd.invoke, [arg]);
|
const cmds = Array.isArray(functionDef.command)
|
||||||
},
|
? functionDef.command
|
||||||
});
|
: [functionDef.command];
|
||||||
|
for (let cmd of cmds) {
|
||||||
|
this.editorCommands.set(cmd.name, {
|
||||||
|
command: cmd,
|
||||||
|
run: async (arg): Promise<any> => {
|
||||||
|
return await plug.invoke(name, [arg]);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.viewDispatch({
|
this.viewDispatch({
|
||||||
|
Loading…
Reference in New Issue
Block a user