Added pageNamespace hook support
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
import { Plug } from "@plugos/plugos/plug";
|
||||
import { System } from "@plugos/plugos/system";
|
||||
import { Hook, Manifest } from "@plugos/plugos/types";
|
||||
import { Express, NextFunction, Request, Response, Router } from "express";
|
||||
|
||||
export type PageNamespaceOperation =
|
||||
| "readPage"
|
||||
| "writePage"
|
||||
| "listPages"
|
||||
| "getPageMeta"
|
||||
| "deletePage";
|
||||
|
||||
export type PageNamespaceDef = {
|
||||
pattern: string;
|
||||
operation: PageNamespaceOperation;
|
||||
};
|
||||
|
||||
export type PageNamespaceHookT = {
|
||||
pageNamespace?: PageNamespaceDef;
|
||||
};
|
||||
|
||||
type SpaceFunction = {
|
||||
operation: PageNamespaceOperation;
|
||||
pattern: RegExp;
|
||||
plug: Plug<PageNamespaceHookT>;
|
||||
name: string;
|
||||
};
|
||||
|
||||
export class PageNamespaceHook implements Hook<PageNamespaceHookT> {
|
||||
spaceFunctions: SpaceFunction[] = [];
|
||||
constructor() {}
|
||||
|
||||
apply(system: System<PageNamespaceHookT>): void {
|
||||
system.on({
|
||||
plugLoaded: () => {
|
||||
this.updateCache(system);
|
||||
},
|
||||
plugUnloaded: () => {
|
||||
this.updateCache(system);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
updateCache(system: System<PageNamespaceHookT>) {
|
||||
this.spaceFunctions = [];
|
||||
for (let plug of system.loadedPlugs.values()) {
|
||||
if (plug.manifest?.functions) {
|
||||
for (let [funcName, funcDef] of Object.entries(
|
||||
plug.manifest.functions
|
||||
)) {
|
||||
if (funcDef.pageNamespace) {
|
||||
this.spaceFunctions.push({
|
||||
operation: funcDef.pageNamespace.operation,
|
||||
pattern: new RegExp(funcDef.pageNamespace.pattern),
|
||||
plug,
|
||||
name: funcName,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
validateManifest(manifest: Manifest<PageNamespaceHookT>): string[] {
|
||||
let errors: string[] = [];
|
||||
if (!manifest.functions) {
|
||||
return [];
|
||||
}
|
||||
for (let [funcName, funcDef] of Object.entries(manifest.functions)) {
|
||||
if (funcDef.pageNamespace) {
|
||||
if (!funcDef.pageNamespace.pattern) {
|
||||
errors.push(`Function ${funcName} has a namespace but no pattern`);
|
||||
}
|
||||
if (!funcDef.pageNamespace.operation) {
|
||||
errors.push(`Function ${funcName} has a namespace but no operation`);
|
||||
}
|
||||
if (
|
||||
!["readPage", "writePage", "getPageMeta", "listPages"].includes(
|
||||
funcDef.pageNamespace.operation
|
||||
)
|
||||
) {
|
||||
errors.push(
|
||||
`Function ${funcName} has an invalid operation ${funcDef.pageNamespace.operation}`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
return errors;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
import { Plug } from "@plugos/plugos/plug";
|
||||
import { SpacePrimitives } from "@silverbulletmd/common/spaces/space_primitives";
|
||||
import { PageMeta } from "@silverbulletmd/common/types";
|
||||
import { PageNamespaceHook, PageNamespaceOperation } from "./page_namespace";
|
||||
|
||||
export class PlugSpacePrimitives implements SpacePrimitives {
|
||||
constructor(
|
||||
private wrapped: SpacePrimitives,
|
||||
private hook: PageNamespaceHook
|
||||
) {}
|
||||
|
||||
performOperation(
|
||||
type: PageNamespaceOperation,
|
||||
pageName: string,
|
||||
...args: any[]
|
||||
): Promise<any> | false {
|
||||
for (let { operation, pattern, plug, name } of this.hook.spaceFunctions) {
|
||||
if (operation === type && pageName.match(pattern)) {
|
||||
return plug.invoke(name, [pageName, ...args]);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
async fetchPageList(): Promise<{
|
||||
pages: Set<PageMeta>;
|
||||
nowTimestamp: number;
|
||||
}> {
|
||||
let allPages = new Set<PageMeta>();
|
||||
for (let { plug, name, operation } of this.hook.spaceFunctions) {
|
||||
if (operation === "listPages") {
|
||||
for (let pm of await plug.invoke(name, [])) {
|
||||
allPages.add(pm);
|
||||
}
|
||||
}
|
||||
}
|
||||
let result = await this.wrapped.fetchPageList();
|
||||
for (let pm of result.pages) {
|
||||
allPages.add(pm);
|
||||
}
|
||||
return {
|
||||
nowTimestamp: result.nowTimestamp,
|
||||
pages: allPages,
|
||||
};
|
||||
}
|
||||
|
||||
readPage(name: string): Promise<{ text: string; meta: PageMeta }> {
|
||||
let result = this.performOperation("readPage", name);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
return this.wrapped.readPage(name);
|
||||
}
|
||||
|
||||
getPageMeta(name: string): Promise<PageMeta> {
|
||||
let result = this.performOperation("getPageMeta", name);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
return this.wrapped.getPageMeta(name);
|
||||
}
|
||||
|
||||
writePage(
|
||||
name: string,
|
||||
text: string,
|
||||
selfUpdate?: boolean,
|
||||
lastModified?: number
|
||||
): Promise<PageMeta> {
|
||||
let result = this.performOperation(
|
||||
"writePage",
|
||||
name,
|
||||
text,
|
||||
selfUpdate,
|
||||
lastModified
|
||||
);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
return this.wrapped.writePage(name, text, selfUpdate, lastModified);
|
||||
}
|
||||
|
||||
deletePage(name: string): Promise<void> {
|
||||
let result = this.performOperation("deletePage", name);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
return this.wrapped.deletePage(name);
|
||||
}
|
||||
|
||||
proxySyscall(plug: Plug<any>, name: string, args: any[]): Promise<any> {
|
||||
return this.wrapped.proxySyscall(plug, name, args);
|
||||
}
|
||||
|
||||
invokeFunction(
|
||||
plug: Plug<any>,
|
||||
env: string,
|
||||
name: string,
|
||||
args: any[]
|
||||
): Promise<any> {
|
||||
return this.wrapped.invokeFunction(plug, env, name, args);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user