import fs from 'fs' import pathlib from 'path' import * as json from './json-utils' export class FSKeyValueCache { private _initialized = false public constructor(private _filepath: string) {} public async init(): Promise { if (this._initialized) { return } const dirname = pathlib.dirname(this._filepath) if (!fs.existsSync(dirname)) { await fs.promises.mkdir(dirname, { recursive: true }) } if (!fs.existsSync(this._filepath)) { await this._writeJSON(this._filepath, {}) } this._initialized = true } public async sync( key: K, value: T[K] | undefined, prompt: (initial?: T[K]) => Promise ): Promise { await this.init() if (value) { await this.set(key, value) return value } const data = await this.get(key) const newValue = await prompt(data) await this.set(key, newValue) return newValue } public async has(key: K): Promise { await this.init() const data = await this._readJSON(this._filepath) return data[key] !== undefined } public async get(key: K): Promise { await this.init() const data: T = await this._readJSON(this._filepath) return data[key] } public async set(key: K, value: T[K]): Promise { await this.init() const data: T = await this._readJSON(this._filepath) data[key] = value return this._writeJSON(this._filepath, data) } public async rm(key: K): Promise { await this.init() const data: T = await this._readJSON(this._filepath) delete data[key] return this._writeJSON(this._filepath, data) } public async clear(): Promise { await this.init() return this._writeJSON(this._filepath, {}) } private _writeJSON = (filepath: string, data: any) => { const fileContent = JSON.stringify(data, null, 2) return fs.promises.writeFile(filepath, fileContent) } private _readJSON = async (filepath: string) => { const fileContent = await fs.promises.readFile(filepath, 'utf8') const parseResult = json.safeParseJson(fileContent) if (!parseResult.success) { throw new Error(`Failed to parse JSON file at ${filepath}: ${parseResult.error.message}`) } return parseResult.data as T } }