@struktoai/mirage-core
Version:
A Unified Virtual Filesystem For AI Agents
191 lines • 7.67 kB
TypeScript
import { type SetAttrFields } from '../../../../types.ts';
import type { MutationJournal } from './journal.ts';
import type { ErrnoCodes, FlushFailure, FSHost, FSNode, FSStream, FSType, SetAttr, SyncVFS } from './types.ts';
/**
* The metadata a `setattr` actually changes, or null when it changes
* none of it.
*
* Emscripten hands this callback more than a guest's chmod and utime:
* a stamp arrives beside a resize and beside a mode, and a write that
* moved no field would still journal an op. Sending one costs a
* dispatch and, worse, splits two writes the journal would otherwise
* coalesce. Comparing against the node costs one read.
*
* The one case comparing cannot catch is a create, whose finalizing
* chmod does lower a real mode; `PyodideFs.fresh` is what suppresses
* that one.
*
* `ctime` is never included: no POSIX call sets it directly, so a mount
* has nothing to write it to. `size` is not metadata here either, it is
* the resize branch's bytes.
*
* Args:
* node: the node as it stands before the write.
* attr: the fields Emscripten passed, times as epoch ms.
*/
export declare function changedAttrs(node: FSNode, attr: SetAttr): SetAttrFields | null;
/**
* An Emscripten filesystem backed by a mirage mount.
*
* This is the pyodide runtime's interception layer. Mounting it at a mount
* prefix puts mirage below the guest's syscall boundary, where every
* spelling of an operation (`open`, `os.open`, `pathlib`, `shutil`'s
* fd-relative walk) arrives as the same callback. Nothing is patched
* inside the interpreter.
*
* Callbacks are synchronous: lookups, reads and writes wait for the host
* through SyncVFS, which needs the worker. A file's stream reads and
* writes through a handle on the run's open-file core, which holds its
* bytes as every other runtime's open files are held; names and metadata
* enter the journal and land at once, in guest order.
*
* The node table lives in `NodeTable`; what is left here is one method
* per Emscripten callback, plus the errno translation only this layer
* performs.
*/
export declare class PyodideFs {
private readonly sync;
private readonly deferred;
private readonly streams;
readonly type: FSType;
private readonly host;
private readonly errno;
private readonly journal;
private readonly nodes;
private readonly mountOf;
private fresh;
/**
* Args:
* host: the Emscripten FS namespace (`pyodide.FS`).
* errno: Emscripten's errno table (`pyodide.ERRNO_CODES`).
* journal: the write-ahead log guest mutations are recorded on.
* prefix: the mount prefix this filesystem serves.
* mountOf: the mirage mount owning a path (`RuntimeFiles.mountOf`).
* One mountpoint serves every mirage mount nested under its
* prefix, so this is the only boundary fact left to check ops
* against; Emscripten's own cross-mount checks cannot see it.
* sync: the worker's blocking way to the mounts.
* deferred: the refusals the mount gave a path whose call has not
* come yet, shared by every mountpoint as the journal is.
* streams: the file streams that hold a core handle, shared the same
* way, which the run's end closes.
*/
constructor(host: FSHost, errno: ErrnoCodes, journal: MutationJournal, prefix: string, mountOf: (path: string) => string | null, sync: SyncVFS, deferred?: Map<string, FlushFailure>, streams?: Set<FSStream>);
private getattr;
/**
* One node's stat, its inode named by `path`.
*
* Args:
* node: the node to describe.
* path: the name its inode is reported under.
* fh: the core handle a stream stats through, which counts what it
* holds; absent stats the node's path.
*/
private attrOf;
private setattr;
/**
* Resize a file whose name is gone through a stream that writes it: its
* bytes are only what the core's handles on it hold.
*
* Args:
* node: the file with no name left.
* size: the length it is left at.
*/
private resizeHeld;
private lookup;
private mknod;
private rename;
private unlink;
/**
* A file whose name the mount removed or replaced. One a stream has open
* reads and writes on through the core, which keeps its bytes; marked
* only once the mount took the change, so a refused removal leaves it
* named.
*
* Args:
* node: the file that lost its name.
*/
private orphan;
private rmdir;
/**
* The directory's names, asking the mount for any it has gained.
*
* The first listing is classified, since Emscripten's getdents reads
* every name's kind off its node. A node is never re-asked about once
* placed, so a later listing asks for names alone and stats only the
* names the tree lacks, rather than classifying every entry again.
*/
private readdir;
invalidate(): void;
/**
* Send the journal to the mount now, in guest order. An entry the mount
* refuses fails the call that touched its path with the mount's errno,
* this call or that file's next one (its close), as a deferred write
* error does; until then nothing more for that path reaches the mount,
* and every other file's entries still land. Without a worker there is
* no mount to send to yet, and the journal waits for the run's end.
*
* Args:
* paths: the paths this call touched.
*/
private settle;
private readThrough;
private placeEntry;
/**
* Restamp a node placed from an unclassified row with what the mount
* said it is.
*
* The listing that placed it swallowed a failed stat so the rest of
* the directory could list; the guest's own stat is where that
* failure belongs, so that stat asks the mount rather than answering
* from the guess. Its answer turns the node into a directory if that
* is what it is.
*
* Args:
* node: the node to classify.
* stat: the row the guest's stat got.
*/
private classifyNode;
/**
* Create a symlink node and record it for the mounts.
*
* A link is namespace state, which is exactly why this can be served:
* the op reaches the node table rather than a backend, so a link lands
* on an s3 or notion mount whose store has no such thing. The target
* is stored as typed and never resolved here.
*
* Args:
* parent: directory the link is created in.
* name: the link's own name.
* target: what it points at, verbatim.
*/
private symlink;
/**
* The target of a symlink node.
*
* Args:
* node: the node to read.
*/
private readlink;
/**
* Open a file's stream on the run's core, which judges, holds and lands
* it as it does every runtime's open files. Emscripten made the open's
* create and truncate before it got here; what is left is how the
* stream reads and writes.
*/
private streamOpen;
/**
* The core handle a file's stream reads and writes through. One a run's
* end closed opens again, as long as its file still has a name: the
* core held the file for that run only. A guest that exited
* (`os._exit`, caught) has no files left. `create` makes the file the
* open's own mknod named.
*/
private handleOf;
/** Land what the stream wrote and let its handle go. */
private streamClose;
private streamRead;
private streamWrite;
private llseek;
}
//# sourceMappingURL=fs.d.ts.map