Huge event system refactoring
This commit is contained in:
@@ -3,17 +3,96 @@ import { EventHook } from "../../plugos/hooks/event.ts";
|
||||
|
||||
import type { SpacePrimitives } from "./space_primitives.ts";
|
||||
|
||||
export class EventedSpacePrimitives implements SpacePrimitives {
|
||||
constructor(private wrapped: SpacePrimitives, private eventHook: EventHook) {}
|
||||
/**
|
||||
* Events exposed:
|
||||
* - file:changed (FileMeta)
|
||||
* - file:deleted (string)
|
||||
* - file:listed (FileMeta[])
|
||||
* - page:saved (string, FileMeta)
|
||||
* - page:deleted (string)
|
||||
*/
|
||||
|
||||
fetchFileList(): Promise<FileMeta[]> {
|
||||
return this.wrapped.fetchFileList();
|
||||
export class EventedSpacePrimitives implements SpacePrimitives {
|
||||
private fileMetaCache = new Map<string, FileMeta>();
|
||||
initialFileListLoad = true;
|
||||
|
||||
constructor(
|
||||
private wrapped: SpacePrimitives,
|
||||
private eventHook: EventHook,
|
||||
private eventsToDispatch = [
|
||||
"file:changed",
|
||||
"file:deleted",
|
||||
"file:listed",
|
||||
"page:saved",
|
||||
"page:deleted",
|
||||
],
|
||||
) {}
|
||||
|
||||
dispatchEvent(name: string, ...args: any[]): Promise<any[]> {
|
||||
if (this.eventsToDispatch.includes(name)) {
|
||||
return this.eventHook.dispatchEvent(name, ...args);
|
||||
} else {
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
}
|
||||
|
||||
readFile(
|
||||
async fetchFileList(): Promise<FileMeta[]> {
|
||||
const newFileList = await this.wrapped.fetchFileList();
|
||||
const deletedFiles = new Set<string>(this.fileMetaCache.keys());
|
||||
for (const meta of newFileList) {
|
||||
const oldFileMeta = this.fileMetaCache.get(meta.name);
|
||||
const newFileMeta: FileMeta = { ...meta };
|
||||
if (
|
||||
(
|
||||
// New file scenario
|
||||
!oldFileMeta && !this.initialFileListLoad
|
||||
) || (
|
||||
// Changed file scenario
|
||||
oldFileMeta &&
|
||||
oldFileMeta.lastModified !== newFileMeta.lastModified
|
||||
)
|
||||
) {
|
||||
this.dispatchEvent("file:changed", newFileMeta);
|
||||
}
|
||||
// Page found, not deleted
|
||||
deletedFiles.delete(meta.name);
|
||||
|
||||
// Update in cache
|
||||
this.fileMetaCache.set(meta.name, newFileMeta);
|
||||
}
|
||||
|
||||
for (const deletedFile of deletedFiles) {
|
||||
this.fileMetaCache.delete(deletedFile);
|
||||
this.dispatchEvent("file:deleted", deletedFile);
|
||||
|
||||
if (deletedFile.endsWith(".md")) {
|
||||
const pageName = deletedFile.substring(0, deletedFile.length - 3);
|
||||
await this.dispatchEvent("page:deleted", pageName);
|
||||
}
|
||||
}
|
||||
|
||||
const fileList = [...new Set(this.fileMetaCache.values())];
|
||||
this.dispatchEvent("file:listed", fileList);
|
||||
this.initialFileListLoad = false;
|
||||
return fileList;
|
||||
}
|
||||
|
||||
async readFile(
|
||||
name: string,
|
||||
): Promise<{ data: Uint8Array; meta: FileMeta }> {
|
||||
return this.wrapped.readFile(name);
|
||||
const data = await this.wrapped.readFile(name);
|
||||
const previousMeta = this.fileMetaCache.get(name);
|
||||
const newMeta = data.meta;
|
||||
if (previousMeta) {
|
||||
if (previousMeta.lastModified !== newMeta.lastModified) {
|
||||
// Page changed since last cached metadata, trigger event
|
||||
this.dispatchEvent("file:changed", newMeta);
|
||||
}
|
||||
}
|
||||
return {
|
||||
data: data.data,
|
||||
meta: this.metaCacher(name, newMeta),
|
||||
};
|
||||
}
|
||||
|
||||
async writeFile(
|
||||
@@ -28,6 +107,11 @@ export class EventedSpacePrimitives implements SpacePrimitives {
|
||||
selfUpdate,
|
||||
meta,
|
||||
);
|
||||
if (!selfUpdate) {
|
||||
this.dispatchEvent("file:changed", newMeta);
|
||||
}
|
||||
this.metaCacher(name, newMeta);
|
||||
|
||||
// This can happen async
|
||||
if (name.endsWith(".md")) {
|
||||
const pageName = name.substring(0, name.length - 3);
|
||||
@@ -35,10 +119,9 @@ export class EventedSpacePrimitives implements SpacePrimitives {
|
||||
const decoder = new TextDecoder("utf-8");
|
||||
text = decoder.decode(data);
|
||||
|
||||
this.eventHook
|
||||
.dispatchEvent("page:saved", pageName, newMeta)
|
||||
this.dispatchEvent("page:saved", pageName, newMeta)
|
||||
.then(() => {
|
||||
return this.eventHook.dispatchEvent("page:index_text", {
|
||||
return this.dispatchEvent("page:index_text", {
|
||||
name: pageName,
|
||||
text,
|
||||
});
|
||||
@@ -48,20 +131,51 @@ export class EventedSpacePrimitives implements SpacePrimitives {
|
||||
});
|
||||
}
|
||||
if (name.startsWith("_plug/") && name.endsWith(".plug.js")) {
|
||||
await this.eventHook.dispatchEvent("plug:changed", name);
|
||||
await this.dispatchEvent("plug:changed", name);
|
||||
}
|
||||
return newMeta;
|
||||
}
|
||||
|
||||
getFileMeta(name: string): Promise<FileMeta> {
|
||||
return this.wrapped.getFileMeta(name);
|
||||
async getFileMeta(name: string): Promise<FileMeta> {
|
||||
try {
|
||||
const oldMeta = this.fileMetaCache.get(name);
|
||||
const newMeta = await this.wrapped.getFileMeta(name);
|
||||
if (oldMeta) {
|
||||
if (oldMeta.lastModified !== newMeta.lastModified) {
|
||||
// Changed on disk, trigger event
|
||||
this.dispatchEvent("file:changed", newMeta);
|
||||
}
|
||||
}
|
||||
return this.metaCacher(name, newMeta);
|
||||
} catch (e: any) {
|
||||
console.log("Checking error", e, name);
|
||||
if (e.message === "Not found") {
|
||||
this.dispatchEvent("file:deleted", name);
|
||||
if (name.endsWith(".md")) {
|
||||
const pageName = name.substring(0, name.length - 3);
|
||||
await this.dispatchEvent("page:deleted", pageName);
|
||||
}
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
async deleteFile(name: string): Promise<void> {
|
||||
if (name.endsWith(".md")) {
|
||||
const pageName = name.substring(0, name.length - 3);
|
||||
await this.eventHook.dispatchEvent("page:deleted", pageName);
|
||||
await this.dispatchEvent("page:deleted", pageName);
|
||||
}
|
||||
return this.wrapped.deleteFile(name);
|
||||
// await this.getPageMeta(name); // Check if page exists, if not throws Error
|
||||
await this.wrapped.deleteFile(name);
|
||||
this.fileMetaCache.delete(name);
|
||||
this.dispatchEvent("file:deleted", name);
|
||||
}
|
||||
|
||||
private metaCacher(name: string, meta: FileMeta): FileMeta {
|
||||
if (meta.lastModified !== 0) {
|
||||
// Don't cache metadata for pages with a 0 lastModified timestamp (usualy dynamically generated pages)
|
||||
this.fileMetaCache.set(name, meta);
|
||||
}
|
||||
return meta;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user