/** * The generic shape behind every persisted runtime store: one file, one * owner-supplied loader, one committed in-memory value, and one serialized * queue for every change. It knows no schema, version, legacy-file error, or * path rule — those stay with the caller's `load()` and, for a mutation, the * caller's `change`/`afterCommit` callbacks. This module imports nothing from * any other Dreamux package, matching the `dreamux-utils` boundary. * * The file is written before the in-memory value is replaced, so a reader * only ever sees a value that is also on disk, and a write that failed left * neither changed. `update`/`create`/`remove` share one FIFO promise tail so * two callers changing the same store never race each other's * read-modify-write. `load()` is deliberately not queued on that same tail: * `update`/`remove` call it as their own first step from inside a * tail-queued callback, and if `load()` were itself queued on that tail it * could never run — the callback waiting on it is what occupies the queue's * current slot. `load()` uses its own, independent in-flight-promise guard * instead, so a store nobody has explicitly loaded yet still works * correctly the first time `update()`/`remove()` runs. */ export interface TransactionalStoreOptions { /** Absolute path of the one file this store owns. */ path: string; /** The owner's initial read: what a missing, unreadable, or legacy file means. */ load(): Promise; /** Defaults to JSON with two-space indentation and a trailing newline. */ encode?(value: T): string; /** Mode for a parent directory the store has to create; omitted = process default. */ dirMode?: number; } export declare class TransactionalStore { private readonly opts; private slot; private loading; private tail; constructor(opts: TransactionalStoreOptions); /** Read once; concurrent callers share the in-flight read. A failed read caches nothing. */ load(): Promise; /** The last committed value. Throws before a successful `load()` — a plain read call site awaits `load()` itself first. */ get current(): T; /** * Change the committed value. `change` sees the committed value and returns * the next one; returning that same reference is a no-op — nothing is * written and `afterCommit` does not run. Otherwise the file is written * first, then memory is swapped, then `afterCommit` runs (still inside the * queue, so the next queued operation waits for it too); a throw from * `change`, the write, or `afterCommit` rejects the caller, and a throw from * `afterCommit` specifically leaves the commit itself standing. `change` may * await other work, but must not call `update`/`create`/`remove` on this * same store — that call would be queued behind this one and wait on it * forever. */ update(change: (current: T) => T | Promise, afterCommit?: (next: T, previous: T) => void | Promise): Promise; /** * Write `value` without reading the file first. Default is create-only — * `EEXIST` (via `publishFileExclusive`) rejects the caller instead of * clobbering whatever is there. `{ replace: true }` writes over whatever is * there via the same atomic-write path `update` uses. */ create(value: T, options?: { replace?: boolean; afterCommit?: (next: T) => void | Promise; }): Promise; /** Unlink the file (`ENOENT` counts as success), then the committed value becomes `next`. */ remove(next: T): Promise; /** Resolves once every operation queued so far has settled. */ drain(): Promise; private encode; private ensureDir; /** Mode `0600`, no fsync — see `writeFileAtomic` for the tmp+rename shape. */ private publishReplace; private enqueue; } //# sourceMappingURL=transactional-store.d.ts.map