@struktoai/mirage-core
Version:
A Unified Virtual Filesystem For AI Agents
239 lines • 12.5 kB
TypeScript
import type { ByteSource } from '../../io/types.ts';
import type { Accessor } from '../../accessor/base.ts';
import { type ConditionSupport, type KnownVersions, type WriteCondition } from '../../cache/types.ts';
import type { StaleWriteError } from '../../errors/types.ts';
import type { DirListing, FileStat, PathSpec } from '../../types.ts';
import type { ConditionLostError } from './errors.ts';
export type ReaddirFn<A extends Accessor> = (accessor: A, path: PathSpec, limit?: number) => Promise<FileStat[] | DirListing>;
export type TreeFn<A extends Accessor> = (accessor: A, path: PathSpec, limit?: number) => Promise<Map<string, FileStat> | null>;
export type StatFn<A extends Accessor> = (accessor: A, path: PathSpec) => Promise<FileStat>;
export type ExistsFn<A extends Accessor> = (accessor: A, path: PathSpec) => Promise<boolean>;
export type PathFn<A extends Accessor> = (accessor: A, path: PathSpec) => Promise<void>;
export type CreateFn<A extends Accessor> = (accessor: A, path: PathSpec) => Promise<FileStat | null>;
export type PairFn<A extends Accessor> = (accessor: A, src: PathSpec, dst: PathSpec) => Promise<void>;
export type WriteFn<A extends Accessor> = (accessor: A, path: PathSpec, data: ByteSource, signal?: AbortSignal) => Promise<FileStat | null>;
export type MkdirFn<A extends Accessor> = (accessor: A, path: PathSpec, parents?: boolean) => Promise<void>;
export type TruncateFn<A extends Accessor> = (accessor: A, path: PathSpec, length: number, noCreate?: boolean) => Promise<FileStat | null>;
/**
* One entry a driver saw while listing the immediate children of a
* prefix.
*
* `marker` entries carry no name of their own (the zero-byte marker
* keyed at the listed prefix itself, or a key the delimiter listing
* cannot classify); they still prove the prefix holds a key, which is
* what separates an empty directory from a missing one.
*/
export interface ChildEntry {
/** Raw backend key, no trailing slash for files and directories. */
key: string;
/** File, directory, or a key that only proves existence. */
kind: 'f' | 'd' | 'marker';
/** File byte size; null/absent when the store did not report one. */
size?: number | null;
/** ISO-8601 mtime; empty/absent when unknown. */
modified?: string;
}
/** One key of a recursive listing, directory markers included. */
export interface TreeEntry {
/** Raw backend key; a directory marker keeps its trailing slash. */
key: string;
/** Byte size; markers report 0 and may omit it. */
size?: number;
modified?: string;
}
/** What a point lookup of one key returned. */
export interface ObjectMeta {
/** Byte size of the object; null when the store omitted it. */
size: number | null;
/** ISO-8601 mtime, when the store has one. */
modified?: string | null;
/** Content identity (ETag, revision id). */
fingerprint?: string | null;
/** Addressable revision id, when the store versions objects. */
revision?: string | null;
/** Backend-shaped stat extras, forwarded into `FileStat.extra` verbatim. */
extra?: Record<string, string>;
}
/**
* One open connection a kit op holds for its whole body.
*
* Python's driver exposes `connect` as an async context manager; the
* TS equivalent is this open/close handle consumed with try/finally.
* The existing `withClient(config, fn)` callback style was not reused
* because a callback cannot live in a record field without fixing the
* callback's return type, and the handle lets every kit op scope one
* connection over listings, probes and mutations alike. A store that
* keeps a live client on its accessor returns the accessor itself with
* a no-op close.
*/
export interface ObjectStoreConnection<C> {
conn: C;
close: () => Promise<void>;
}
/**
* The native surface of one keyed byte store.
*
* Everything above this line — readdir with its index write-back,
* stat's probe ladder, find's implicit-directory synthesis, du, and
* the mutation family — derives from
* these primitives in `core/object_store`; a backend supplies only
* the raw store calls.
*
* Directory semantics the primitives must honor: a directory is a key
* prefix, an empty directory is a zero-byte marker object keyed at the
* prefix itself (`key/`) on stores that accept one (`markersSupported`),
* and symlinks or hardlinks do not exist — ops that would need them
* stay unwired, which the dispatcher already surfaces as ENOTSUP.
*/
export interface ObjectStoreDriver<A extends Accessor, C> {
readonly conditionSupport?: ConditionSupport;
/** VFS name, used in op records and log lines. */
vfs: string;
/** Listing size above which readdir logs a warning. */
scopeError: number;
/** Mount key prefix from the accessor's config ('' for the whole store). */
keyPrefixOf: (accessor: A) => string;
/** Per-op connection handle; see {@link ObjectStoreConnection}. */
connect: (accessor: A) => Promise<ObjectStoreConnection<C>>;
/**
* One-level listing of a prefix as {@link ChildEntry} items; may
* repeat directories, the kit deduplicates.
*/
listChildren: (conn: C, pfx: string) => AsyncIterable<ChildEntry>;
/**
* The key at `stem` itself plus every key under `stem + '/'` — du's
* walk, which must not match sibling keys sharing the stem as a
* name prefix.
*/
listSubtree: (conn: C, stem: string) => AsyncIterable<TreeEntry>;
/**
* Point lookup of one key, null when absent; a key the store refuses this
* caller throws EACCES, which stat and mkdir check against `probePrefix`; other
* classification failures propagate.
*/
head: (conn: C, key: string) => Promise<ObjectMeta | null>;
/** Full object bytes, null when absent. */
get: (conn: C, key: string) => Promise<Uint8Array | null>;
/**
* Write one object, returning what the store's write response said
* about it, or null when the store's write API reports nothing at all
* (opendal). The meta is partial: `size` is the bytes written and
* `fingerprint` is the store's token, spelled exactly as `head` spells
* it so the two can be compared; `modified` is absent, because no
* store's write response carries one. A response without a token can
* still confirm size. The write factory returns these facts as a
* FileStat for index retention.
* A store error meaning the container is absent
* (`isNotFound`) propagates, and the write factory restates it as
* ENOENT on the path.
*/
put: (conn: C, key: string, data: Uint8Array) => Promise<ObjectMeta | null>;
/**
* Delete one key (every revision on a versioned store); silent on a
* missing key.
*/
deleteFile: (conn: C, key: string) => Promise<void>;
/** Delete every key under a prefix. */
deletePrefix: (conn: C, pfx: string) => Promise<void>;
/** Whether any key sits under a prefix; a refused listing throws EACCES. */
probePrefix: (conn: C, pfx: string) => Promise<boolean>;
/** Whether a store error means the key is absent. */
isNotFound: (err: unknown) => boolean;
/**
* Relocate one object; false when the source names no object. Absent
* when the store has no native move — rename stays unwired then,
* which the dispatcher surfaces as ENOTSUP.
*/
moveFile?: (conn: C, srcKey: string, dstKey: string) => Promise<boolean>;
/**
* Relocate every key under a prefix; false when the source prefix
* holds nothing. Absence follows `moveFile`.
*/
movePrefix?: (conn: C, srcPfx: string, dstPfx: string) => Promise<boolean>;
/**
* Copy one object; false when the source names no object, if the
* store can tell cheaply. Absent when the store has no native copy —
* copy stays unwired then.
*/
copyFile?: (conn: C, srcKey: string, dstKey: string) => Promise<boolean>;
/**
* Point lookup of one revision of a key by the token `head` spells, null
* when it is gone. A stat pinned by a snapshot asks this, so a revision
* the store still holds under another name (a trash, a version) answers.
*/
headRevision?: (conn: C, key: string, revision: string) => Promise<ObjectMeta | null>;
/**
* Whether the store accepts the zero-byte `key/` marker object;
* absent means true. False when the store refuses one client-side
* (hf): an empty directory cannot exist there, `makeMkdir` has
* nothing to write, and a directory exists exactly while it holds a
* key.
*/
markersSupported?: boolean;
/**
* `put` carrying a write condition; a lost one throws `ConditionLostError`.
* Absent when the store cannot condition a write.
*/
putStream?: (conn: C, key: string, data: ByteSource, cond: WriteCondition) => Promise<ObjectMeta>;
putIf?: (conn: C, key: string, data: Uint8Array, cond: WriteCondition) => Promise<ObjectMeta | null>;
/** `get` plus the token of the bytes returned, for an op that writes back what it read; a revision reads that revision. */
getVersioned?: (conn: C, key: string, revision: string | null) => Promise<[Uint8Array, string | null] | null>;
/** `copyFile` with the destination's condition. */
copyIf?: (conn: C, srcKey: string, dstKey: string, cond: WriteCondition) => Promise<boolean>;
/** `deleteFile` with a condition. */
deleteIf?: (conn: C, key: string, cond: WriteCondition) => Promise<void>;
/**
* `moveFile` whose copy and delete are both conditioned on the source's
* version (the one given, else the one its own lookup sees), and whose
* copy carries the destination's.
*/
moveFileIf?: (conn: C, srcKey: string, dstKey: string, cond: WriteCondition, source: string | null) => Promise<boolean>;
/**
* `movePrefix` moving each key only if it is still the version the mount
* saw (`known`), else the one listed, onto a destination key the mount
* holds a version for (`dstKnown`) only if it is still that version; the
* keys that changed stay where they were and are thrown in a ConditionLostError,
* and one changed before its copy keeps every source key where it was.
*/
movePrefixIf?: (conn: C, srcPfx: string, dstPfx: string, known: KnownVersions, dstKnown: KnownVersions) => Promise<boolean>;
/**
* `deletePrefix` deleting each key only if it is still the version the
* mount saw, else the one listed.
*/
deletePrefixIf?: (conn: C, pfx: string, known: KnownVersions) => Promise<void>;
}
/** The refusal for a one-path op whose condition lost. Mirrors Python's `refused`. */
export declare function refused(path: PathSpec, err: ConditionLostError, cond: WriteCondition | null): Promise<StaleWriteError>;
/**
* Keep every lost key's version, then throw the walk's later error. Mirrors
* Python's `keep_all_lost`.
*/
export declare function keepAllLost(root: PathSpec, keyPrefix: string, err: ConditionLostError): Promise<never>;
/**
* Keep what a walk's refusal measured, and name the key it reports: every
* lost key but the named one keeps its version; a later error that stopped
* the walk is thrown instead, with every version kept. Mirrors Python's
* `keep_walk`.
*/
export declare function keepWalk(root: PathSpec, keyPrefix: string, err: ConditionLostError): Promise<string>;
/**
* Keep the version of each key a walk lost on, except `skip`, which the
* refusal itself names and keeps. A key found gone keeps what the session
* last saw of it. Mirrors Python's `keep_lost`.
*/
export declare function keepLost(root: PathSpec, keyPrefix: string, err: ConditionLostError, skip: string | null): Promise<void>;
/**
* The `*If` hook a conditional op calls. Every `write: conditional` object
* store is on the S3 driver, which carries them all; Box and Dropbox condition
* their own ops and never reach here.
*/
export declare function requireHook<F>(hook: F | undefined): F;
/**
* Whether a prefix holds a child, as `readdir` would list one. The prefix's
* own marker is no child: it proves only that the directory exists, and one
* made again after a removal is a later `mkdir`, not a key the removal
* missed. The listing stops at the first child. Mirrors Python's
* `holds_child`.
*/
export declare function holdsChild<A extends Accessor, C>(driver: ObjectStoreDriver<A, C>, conn: C, pfx: string): Promise<boolean>;
//# sourceMappingURL=driver.d.ts.map