Files
boss/src/store.ts

75 lines
1.7 KiB
TypeScript

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<T>(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);
}
}