UNPKG

@furystack/filesystem-store

Version:

Simple File System store implementation for FuryStack

93 lines 3.96 kB
import type { Constructable, FilterType, FindOptions, PhysicalStore } from '@furystack/core'; import { EventHub, type ListenerErrorPayload } from '@furystack/utils'; import { promises } from 'fs'; /** * {@link PhysicalStore} backed by a JSON file on disk. * * Reads on construction, holds entities in an {@link InMemoryStore}, and flushes * pending writes on a `tickMs` interval (default 3000 ms). External edits to the * file are picked up via an `fs.watch` watcher that triggers {@link reloadData}. * * Disposal is async — `[Symbol.asyncDispose]` flushes pending changes, closes * the watcher, and clears the interval. Owners must `await` disposal or rely on * `await using` / {@link defineStore}'s `onDispose` hook to avoid lost writes. * * **Init race:** the constructor schedules the initial reload in the * background. Calls to `get` / `find` / `count` issued before the reload * resolves see an empty cache. Failures during the background reload (other * than `ENOENT`) are surfaced via `onLoadError` rather than thrown. * * Re-emits `onEntityAdded` / `onEntityUpdated` / `onEntityRemoved` from the * underlying in-memory store, plus `onWatcherError` (sync watcher setup * failure) and `onLoadError` (async file-load failure) for diagnostics. */ export declare class FileSystemStore<T, TPrimaryKey extends keyof T> extends EventHub<{ onEntityAdded: { entity: T; }; onEntityUpdated: { id: T[TPrimaryKey]; change: Partial<T>; }; onEntityRemoved: { key: T[TPrimaryKey]; }; onWatcherError: { error: unknown; }; onLoadError: { error: unknown; }; onListenerError: ListenerErrorPayload; }> implements PhysicalStore<T, TPrimaryKey, T> { private readonly options; private readonly watcher?; readonly model: Constructable<T>; readonly primaryKey: TPrimaryKey; private readonly inMemoryStore; private get cache(); remove(...keys: Array<T[TPrimaryKey]>): Promise<void>; private tick; private _hasChanges; /** Whether the in-memory cache has unflushed mutations. Read-only externally. */ get hasChanges(): boolean; get(key: T[TPrimaryKey], select?: Array<keyof T>): Promise<T | undefined>; add(...entries: T[]): Promise<import("@furystack/core").CreateResult<T>>; find<TFields extends Array<keyof T>>(filter: FindOptions<T, TFields>): Promise<import("@furystack/core").PartialResult<T, TFields>[]>; count(filter?: FilterType<T>): Promise<number>; /** * Writes the in-memory cache to disk if {@link hasChanges} is set. No-op * otherwise — the periodic tick calls this on every interval but only the * first call after a mutation actually touches the filesystem. */ saveChanges(): Promise<void>; /** * Flushes pending changes, closes the FS watcher and clears the tick interval. * Must be awaited — skipping `await` risks losing the final write. */ [Symbol.asyncDispose](): Promise<void>; /** * Replaces the in-memory cache with the contents of the backing file. Called * on construction and on every FS watcher event. Missing file (`ENOENT`) is * silently ignored so first-run writes succeed against a fresh path. */ reloadData(): Promise<void>; update(id: T[TPrimaryKey], data: T): Promise<void>; /** * Test seam — overridable to fault-inject the read path. Defaults to * `fs.promises.readFile`. Production code should not reassign. */ readFile: typeof promises.readFile; /** * Test seam — overridable to fault-inject the write path. Defaults to * `fs.promises.writeFile`. Production code should not reassign. */ writeFile: typeof promises.writeFile; constructor(options: { fileName: string; primaryKey: TPrimaryKey; tickMs?: number; model: Constructable<T>; }); } //# sourceMappingURL=filesystem-store.d.ts.map