@firesystem/core
Version:
Virtual File System core interfaces and types
305 lines (250 loc) • 8.25 kB
text/typescript
import type { IFileSystem } from "../interfaces/IFileSystem";
import type { IReactiveFileSystem } from "../interfaces/IReactiveFileSystem";
import type { FileEntry, FileMetadata } from "../types/FileEntry";
import type { FileSystemEventPayloads } from "../types/FileSystemEvents";
import { TypedEventEmitter } from "../utils/EventEmitter";
import { FileSystemEvents } from "../types/FileSystemEvents";
/**
* Constructor type helper
*/
type Constructor<T = {}> = new (...args: any[]) => T;
/**
* Mixin to add event support to any file system implementation
*/
export function withEvents<TBase extends Constructor<IFileSystem>>(
Base: TBase,
): TBase & Constructor<IReactiveFileSystem> {
return class extends Base {
public readonly events = new TypedEventEmitter<FileSystemEventPayloads>();
// Override methods to emit events
async readFile(path: string): Promise<FileEntry> {
const startTime = Date.now();
const operationId = `read-${path}-${startTime}`;
this.events.emit(FileSystemEvents.OPERATION_START, {
operation: "readFile",
path,
id: operationId,
});
this.events.emit(FileSystemEvents.FILE_READING, { path });
try {
const result = await super.readFile(path);
this.events.emit(FileSystemEvents.FILE_READ, {
path,
size: result.size || 0,
});
this.events.emit(FileSystemEvents.OPERATION_END, {
operation: "readFile",
path,
id: operationId,
duration: Date.now() - startTime,
});
return result;
} catch (error) {
this.events.emit(FileSystemEvents.OPERATION_ERROR, {
operation: "readFile",
path,
error: error as Error,
});
throw error;
}
}
async writeFile(
path: string,
content: any,
metadata?: FileMetadata,
): Promise<FileEntry> {
const startTime = Date.now();
const operationId = `write-${path}-${startTime}`;
const size =
typeof content === "string"
? content.length
: JSON.stringify(content).length;
this.events.emit(FileSystemEvents.OPERATION_START, {
operation: "writeFile",
path,
id: operationId,
});
this.events.emit(FileSystemEvents.FILE_WRITING, { path, size });
try {
const result = await super.writeFile(path, content, metadata);
this.events.emit(FileSystemEvents.FILE_WRITTEN, { path, size });
this.events.emit(FileSystemEvents.OPERATION_END, {
operation: "writeFile",
path,
id: operationId,
duration: Date.now() - startTime,
});
return result;
} catch (error) {
this.events.emit(FileSystemEvents.OPERATION_ERROR, {
operation: "writeFile",
path,
error: error as Error,
});
throw error;
}
}
async deleteFile(path: string): Promise<void> {
const startTime = Date.now();
const operationId = `delete-${path}-${startTime}`;
this.events.emit(FileSystemEvents.OPERATION_START, {
operation: "deleteFile",
path,
id: operationId,
});
this.events.emit(FileSystemEvents.FILE_DELETING, { path });
try {
await super.deleteFile(path);
this.events.emit(FileSystemEvents.FILE_DELETED, { path });
this.events.emit(FileSystemEvents.OPERATION_END, {
operation: "deleteFile",
path,
id: operationId,
duration: Date.now() - startTime,
});
} catch (error) {
this.events.emit(FileSystemEvents.OPERATION_ERROR, {
operation: "deleteFile",
path,
error: error as Error,
});
throw error;
}
}
async readDir(path: string): Promise<FileEntry[]> {
const startTime = Date.now();
const operationId = `readDir-${path}-${startTime}`;
this.events.emit(FileSystemEvents.OPERATION_START, {
operation: "readDir",
path,
id: operationId,
});
this.events.emit(FileSystemEvents.DIR_READING, { path });
try {
const result = await super.readDir(path);
this.events.emit(FileSystemEvents.DIR_READ, {
path,
count: result.length,
});
this.events.emit(FileSystemEvents.OPERATION_END, {
operation: "readDir",
path,
id: operationId,
duration: Date.now() - startTime,
});
return result;
} catch (error) {
this.events.emit(FileSystemEvents.OPERATION_ERROR, {
operation: "readDir",
path,
error: error as Error,
});
throw error;
}
}
async mkdir(path: string, recursive: boolean = false): Promise<FileEntry> {
const startTime = Date.now();
const operationId = `mkdir-${path}-${startTime}`;
this.events.emit(FileSystemEvents.OPERATION_START, {
operation: "mkdir",
path,
id: operationId,
});
this.events.emit(FileSystemEvents.DIR_CREATING, { path, recursive });
try {
const result = await super.mkdir(path, recursive);
this.events.emit(FileSystemEvents.DIR_CREATED, { path });
this.events.emit(FileSystemEvents.OPERATION_END, {
operation: "mkdir",
path,
id: operationId,
duration: Date.now() - startTime,
});
return result;
} catch (error) {
this.events.emit(FileSystemEvents.OPERATION_ERROR, {
operation: "mkdir",
path,
error: error as Error,
});
throw error;
}
}
async rmdir(path: string, recursive: boolean = false): Promise<void> {
const startTime = Date.now();
const operationId = `rmdir-${path}-${startTime}`;
this.events.emit(FileSystemEvents.OPERATION_START, {
operation: "rmdir",
path,
id: operationId,
});
this.events.emit(FileSystemEvents.DIR_DELETING, { path, recursive });
try {
await super.rmdir(path, recursive);
this.events.emit(FileSystemEvents.DIR_DELETED, { path });
this.events.emit(FileSystemEvents.OPERATION_END, {
operation: "rmdir",
path,
id: operationId,
duration: Date.now() - startTime,
});
} catch (error) {
this.events.emit(FileSystemEvents.OPERATION_ERROR, {
operation: "rmdir",
path,
error: error as Error,
});
throw error;
}
}
async clear(): Promise<void> {
const startTime = Date.now();
const operationId = `clear-${startTime}`;
this.events.emit(FileSystemEvents.OPERATION_START, {
operation: "clear",
id: operationId,
});
this.events.emit(FileSystemEvents.STORAGE_CLEARING, undefined as any);
try {
await super.clear();
const duration = Date.now() - startTime;
this.events.emit(FileSystemEvents.STORAGE_CLEARED, { duration });
this.events.emit(FileSystemEvents.OPERATION_END, {
operation: "clear",
id: operationId,
duration,
});
} catch (error) {
this.events.emit(FileSystemEvents.OPERATION_ERROR, {
operation: "clear",
error: error as Error,
});
throw error;
}
}
async size(): Promise<number> {
const startTime = Date.now();
const operationId = `size-${startTime}`;
this.events.emit(FileSystemEvents.OPERATION_START, {
operation: "size",
id: operationId,
});
try {
const size = await super.size();
this.events.emit(FileSystemEvents.STORAGE_SIZE_CALCULATED, { size });
this.events.emit(FileSystemEvents.OPERATION_END, {
operation: "size",
id: operationId,
duration: Date.now() - startTime,
});
return size;
} catch (error) {
this.events.emit(FileSystemEvents.OPERATION_ERROR, {
operation: "size",
error: error as Error,
});
throw error;
}
}
};
}