import { pathExistsSync, readFileSync, writeFileSync } from "fs-extra"; import { tryit } from "shared/helpers/error.helpers"; import log from "electron-log"; import { Subject, debounceTime } from "rxjs"; export class JsonCache { private _cache: Record = {}; private readonly setEvent: Subject = new Subject(); public constructor( private readonly jsonPath: string, private readonly options: JsonCacheOptions = { autoSave: true, saveDebounce: 1000} ){ this.load(); if(this.options.autoSave){ this.setEvent = new Subject(); this.setEvent.pipe(debounceTime(this.options.saveDebounce ?? 1000)).subscribe(() => this.save()); } } private load(): void { try { if(pathExistsSync(this.jsonPath)){ this._cache = JSON.parse(readFileSync(this.jsonPath).toString()); } else { log.warn("File cache not exist yet", this.jsonPath); } } catch (error) { log.warn("Failed to load cache file", this.jsonPath, error); } finally { this._cache ??= {}; } } public save(): void { const res = tryit(() => writeFileSync(this.jsonPath, JSON.stringify(this._cache), { flush: true })); if(res.error){ log.error("Failed to save cache", this.jsonPath, res.error); } } public get(key: string): T { return this._cache[key]; } public set(key: string, value: T): void { this._cache[key] = value; this.setEvent?.next(); } public delete(key: string): void { delete this._cache[key]; this.setEvent?.next(); } public clear(): void { this._cache = {}; this.setEvent?.next(); } public get cache(): Record { return this._cache; } } export type JsonCacheOptions = { /** * Load the cache immediately after creating the instance * @default true */ loadImmediately?: boolean; /** * Auto save the cache after a change * @default true */ autoSave?: boolean; /** * Time in ms to wait before saving the cache after a change * @default 1000 */ saveDebounce?: number; };