import { copyFileSync, existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs"; import { dirname } from "node:path"; import type { AppState } from "./types.js"; function defaultState(): AppState { return { sessions: [], messages: [], tasks: [], workers: [], approvals: [], deviceBindings: [], events: [], }; } export class FileStore { private state: AppState; private readonly backupFilePath: string; private readonly tempFilePath: string; constructor(private readonly filePath: string) { this.backupFilePath = `${filePath}.bak`; this.tempFilePath = `${filePath}.tmp`; this.ensureDirectory(); this.state = this.load(); } get snapshot(): AppState { return structuredClone(this.state); } mutate(mutator: (state: AppState) => T): T { const result = mutator(this.state); this.save(); return result; } reset(): AppState { this.state = defaultState(); this.save(); return this.snapshot; } private ensureDirectory(): void { mkdirSync(dirname(this.filePath), { recursive: true }); } private load(): AppState { for (const path of [this.filePath, this.backupFilePath]) { if (!existsSync(path)) { continue; } try { const raw = readFileSync(path, "utf8"); return { ...defaultState(), ...(JSON.parse(raw) as AppState) }; } catch { // try backup/default } } return defaultState(); } private save(): void { const content = `${JSON.stringify(this.state, null, 2)}\n`; writeFileSync(this.tempFilePath, content, "utf8"); if (existsSync(this.filePath)) { copyFileSync(this.filePath, this.backupFilePath); } renameSync(this.tempFilePath, this.filePath); } }