import * as path from "path"; import { Transport } from "../transports/transport-common.ts"; export class FileExistsError extends Error { constructor(message: string) { super(message); this.name = this.constructor.name; } } export class FileTracker { private readTimestamps = new Map(); async readUntracked( transport: Transport, signal: AbortSignal, filePath: string, ): Promise { const absolutePath = await transport.resolvePath(signal, filePath); const content = await transport.readFile(signal, absolutePath); return content; } async read(transport: Transport, signal: AbortSignal, filePath: string): Promise { const absolutePath = await transport.resolvePath(signal, filePath); const content = await transport.readFile(signal, absolutePath); const modified = await transport.modTime(signal, absolutePath); this.readTimestamps.set(absolutePath, modified); return content; } async recordFileReadTimestamp( transport: Transport, signal: AbortSignal, filePath: string, ): Promise { const absolutePath = await transport.resolvePath(signal, filePath); const modified = await transport.modTime(signal, absolutePath); this.readTimestamps.set(absolutePath, modified); } async write( transport: Transport, signal: AbortSignal, filePath: string, content: string, ): Promise { const absolutePath = await transport.resolvePath(signal, filePath); const dir = path.dirname(absolutePath); await transport.mkdir(signal, dir); await transport.writeFile(signal, absolutePath, content); // Update mod time const modified = await transport.modTime(signal, absolutePath); this.readTimestamps.set(absolutePath, modified); return absolutePath; } async isOutdated(transport: Transport, signal: AbortSignal, filePath: string): Promise { const absolutePath = await transport.resolvePath(signal, filePath); if (!this.readTimestamps.has(absolutePath)) return false; const lastReadTime = this.readTimestamps.get(absolutePath)!; try { const currentModified = await transport.modTime(signal, absolutePath); return currentModified > lastReadTime; } catch { // File was deleted or is otherwise inaccessible return false; } } async canCreate(transport: Transport, signal: AbortSignal, filePath: string) { const absolutePath = await transport.resolvePath(signal, filePath); try { await transport.modTime(signal, absolutePath); return false; } catch { return true; } } async assertCanCreate(transport: Transport, signal: AbortSignal, filePath: string) { const canCreate = await this.canCreate(transport, signal, filePath); if (!canCreate) throw new FileExistsError("File already exists"); } } // Default instance for application use export const fileTracker = new FileTracker();