@struktoai/mirage-core
Version:
A Unified Virtual Filesystem For AI Agents
75 lines • 4.07 kB
TypeScript
import type { FileCacheMixin } from '../cache/file/mixin.ts';
import type { Evicted } from '../cache/index/config.ts';
import { FileStat } from '../types.ts';
import { BaseVFS } from '../vfs/base.ts';
import type { MountEntry } from './mount/mount.ts';
import type { Namespace } from './mount/namespace/namespace.ts';
/**
* Keep the local view honest against backend truth.
*
* The single reconcile point every read path shares. Under a mount's
* `read: fresh` a backend re-stat classifies a path as fresh, stale
* (fingerprint mismatch), gone
* (deletion), or unknown (no fingerprint to compare). One deletion signal
* feeds both consumers with separate reactions: the file cache evicts and the
* namespace GCs any orphaned attribute overlay.
*
* Three read paths call in, all at the dispatcher: its cached-read gate
* (mayServeCached), its main-op catch (onEnoent), and its first lookup of a
* path the running command's line names (reconcileRead). The re-stat goes
* through the ops registry (not mount.call, whose op set omits stat).
* Reconcile state follows each consumer's store (RAM local, Redis shared
* across runtimes), so this is a thin coordinator holding references, not
* config.
*
* The gate and reconcileRead both run for a warm named operand, once at the
* command's first lookup of it and again at the gate, and they share one
* scope: the command. The
* first probe's backend answer is kept on the mount's CacheManager for the
* rest of the command, so the gate, and the command's own stat of the
* operand, reuse it instead of asking again. A write in the command, the
* clear after an external program, or a re-list that finds the path gone
* retires it, and a read outside any command (FUSE, the dispatcher) never sees
* it.
*/
export declare class Reconciler {
private readonly cache;
private readonly namespace;
constructor(cache: FileCacheMixin & BaseVFS, namespace: Namespace);
private drop;
private probe;
private probeOrUnknown;
mayServeCached(mount: MountEntry, path: string): Promise<boolean>;
/**
* Gate a cached listing: may it be served without re-listing?
*
* Under `bounded` the listing is trusted within its bound. Under `fresh` a
* listing the running command fetched itself is served (a read outside any
* command trusts one fetched within the last `LISTING_TRUST_WINDOW`
* seconds instead, `CacheManager.listingTrusted`). Past that, a listing
* stored at the mount's pin is served without asking: github pins a
* full-sha ref and serves its listing unchecked when the stored version
* equals that sha. It names a commit: github.com refuses a 40- or 64-hex
* branch or tag name, and a GitHub Enterprise host is assumed to as well
* (`pinOf` in vfs/github/github.ts). A mount that declares a
* `listingVersion` then has its stored `version` checked against a stat of
* the mount root (MOUNT) or of the folder (FOLDER), sent through a
* throwaway index so no cached row answers it. One check answers for a
* whole command, and concurrent callers share it
* (`CacheManager.checkedVersion`).
*
* A match serves the listing. Anything else answers EXPIRED and keeps the
* listing stored for the re-list to diff: a moved version, a path the
* backend no longer has, a stat with no fingerprint, a mount with no stat
* at all, and a backend that cannot answer, which is logged. The index is
* never cleared here. A programming error propagates.
*/
mayServeListing(mount: MountEntry, folder: string, version: string | null): Promise<boolean>;
private listingFingerprint;
reconcileRead(mount: MountEntry, path: string): Promise<void>;
onEnoent(mount: MountEntry, name: string, path: string, err: unknown): Promise<void>;
onFreshStat(mount: MountEntry, path: string, row: FileStat | null): Promise<void>;
onGone(gone: readonly Evicted[], excluded?: readonly string[]): Promise<void>;
private onMissing;
}
//# sourceMappingURL=reconcile.d.ts.map