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 = new (...args: any[]) => T; /** * Mixin to add event support to any file system implementation */ export function withEvents>( Base: TBase, ): TBase & Constructor { return class extends Base { public readonly events = new TypedEventEmitter(); // Override methods to emit events async readFile(path: string): Promise { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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; } } }; }