UNPKG

@struktoai/mirage-core

Version:

A Unified Virtual Filesystem For AI Agents

142 lines • 7.83 kB
import type { FileStat } from '../../types.ts'; import { type Clock } from '../../utils/clock.ts'; import type { Evicted, IndexSnapshot, ListResult, LookupResult, SetDirOptions } from './config.ts'; export declare abstract class IndexCacheStore { /** The clock every listing deadline of this store is set and read on. */ readonly clock: Clock; /** @param clock the clock listings expire on; undefined means the real one. */ constructor(clock?: Clock); /** Seconds a listing lives when its writer names no expiry. */ abstract readonly ttl: number; /** * Merge snapshots by path; deferred stores flush before operations or * close. Clear discards them. `version` replaces the version of every * listed folder, and null clears it. */ abstract seed(entries: ReadonlyMap<string, FileStat>, children: ReadonlyMap<string, readonly string[]>, expiresAt: Date, version?: string | null): void; /** Apply this index's ownership rules to a refill snapshot. */ scopeSnapshot(snapshot: IndexSnapshot): IndexSnapshot; abstract entries(): Promise<Map<string, FileStat>>; abstract get(vfsPath: string): Promise<LookupResult>; /** Read retained rows by path, omitting missing entries. */ getMany(vfsPaths: readonly string[]): Promise<Map<string, FileStat>>; abstract put(vfsPath: string, entry: FileStat): Promise<void>; /** * Keep a row a create just made, named in its parent's listing. A listing * held for the parent gains the name, in order, and stays what it was: a * complete one stays complete, since the create is the only change to it. * Replacing a directory with a file leaves the parent partial so a * coexisting prefix can be listed again. * With none held, the parent gets a partial listing that names the one * entry, which proves it without proving anything absent; `expiredAt` is * the latest that listing may live, null for the store's own lifetime. */ abstract addEntry(vfsPath: string, entry: FileStat, expiredAt?: Date | null): Promise<void>; /** Atomically replace an existing row without renewing its listing; custom stores may decline. */ replaceIfUnchanged(_path: string, _predecessor: string, _entry: FileStat): Promise<boolean>; /** * A directory's listing. Given `child`, the one key a lookup asks about, * its entries are narrowed to that key, kept where the listing names it, * so a stat of each child of a large directory costs no scan or copy of * the whole listing, which made `ls` of one quadratic * (strukto-ai/mirage#1840). A custom store may answer the whole listing * instead: callers check the key against the entries either way. */ abstract listDir(vfsPath: string, child?: string): Promise<ListResult>; /** * Cache a complete directory listing. A complete listing names every * child, so a child the previous listing named and this one does not is * gone: its row goes, and a gone directory takes its listing and every * row beneath it. Rows only `put` wrote were never named, so they stay. A * window evicts nothing. Resolves to the children that went. */ abstract setDir(vfsPath: string, entries: readonly [string, FileStat][], expiredAt?: Date | null, options?: SetDirOptions): Promise<Evicted[]>; /** * Hand children a re-list found gone to the mount's cleanup. A raw store * belongs to no mount, so there is nothing to clean. */ reportGone(_gone: readonly Evicted[]): Promise<void>; /** Token captured before fetching listings, including descendant seeds. */ refillToken(): Promise<string>; /** Expire complete parent listings contradicted by the observed immediate child keys. */ invalidateMissing(_paths: readonly string[]): Promise<boolean>; /** null rejects an older refill without reporting any deletions. */ refillDir(_path: string, _entries: readonly [string, FileStat][], _token: string, _expiredAt?: Date | null, _options?: SetDirOptions & { partial?: boolean; }): Promise<Evicted[] | null>; /** * Drop a changed row and demote its parent listing to partial. * Retain siblings, the original deadline and deletion history. Clear the * parent's version and window without recording a new observation. * Missing or stale membership must never become fresh. */ abstract invalidateEntry(vfsPath: string): Promise<void>; abstract invalidateDir(vfsPath: string): Promise<void>; /** * Cache observed children without claiming a complete directory. Stores * supporting partial freshness return these keys as `partialEntries` * until expiry or invalidation. A partial listing proves nothing * complete, so it carries no version. Custom stores inherit the conservative * put-only fallback, which refreshes the parent on the next lookup. */ setPartialDir(vfsPath: string, entries: readonly [string, FileStat][], _expiredAt?: Date | null): Promise<void>; /** * Drop `vfsPath` and everything cached below it. * * `invalidateDir` drops one directory's listing and its direct children's * entries, which is enough for a mutation that named a path. A push * notification that can only name a scope needs the whole subtree gone, * because the listings further down were cached independently and nothing * above them expires them. * * `remember` retains current membership as deletion history for the next * complete listing; the default forgets it. * Mirrors Python `IndexCacheStore.invalidate_prefix`. */ abstract invalidatePrefix(vfsPath: string, excluded?: readonly string[], remember?: boolean): Promise<void>; /** * Whether the running command fetched `folder`'s listing itself. * * A raw store belongs to no mount and no command, so it cannot tell and * answers false; a mount's view answers from its cache manager. Mirrors * Python's `listed_this_command`. */ listedThisCommand(_folder: string): boolean; /** * Whether a listing is cached at `vfsPath` or anywhere under it. * * Asked when a removed path may be a folder, to avoid a subtree scan for * each plain-file delete. Only a retained listing counts, even one past its * ttl (expiry is checked when it is read). A row or tombstone does not prove * a listing remains below the path. A children-first delete stream may * already have removed every listing before the folder's own event arrives. * * A store that cannot tell answers true, so callers conservatively drop * the subtree and refetch it. * * Mirrors Python `IndexCacheStore.holds_subtree`. */ holdsSubtree(_vfsPath: string): Promise<boolean>; /** * Mark every entry stale without discarding it. * * The difference from `clear` is what a later lookup can tell. `clear` * leaves an empty store, which reads exactly like a store that was never * filled, so a backend whose index *is* its listing cannot tell an * invalidation from an empty repository. Expiring instead keeps that * distinction: the lookup answers EXPIRED and the backend refetches. */ abstract invalidate(): Promise<void>; abstract clear(): Promise<void>; /** * Release whatever the store holds open. The default is a no-op: * an in-memory index owns nothing. A store backed by a connection * (redis) overrides this and must be idempotent — `close()` is * called once per owning VFS, and a VFS shared between * workspaces is closed by whichever one owns it. * * Mirrors Python `IndexCacheStore.close` (`cache/index/store.py`). */ close(): Promise<void>; } //# sourceMappingURL=store.d.ts.map