UNPKG

@struktoai/mirage-core

Version:

A Unified Virtual Filesystem For AI Agents

256 lines • 13.6 kB
import { type FileStat, PathSpec } from '../../../../types.ts'; import type { Files } from '../../../../workspace/files.ts'; /** Read one virtual path through the session's files. */ export declare function readFile(vfs: Files, path: PathSpec): Promise<Uint8Array>; /** What stands at a path, a link followed, null when nothing does. */ export declare function statOf(vfs: Files, path: PathSpec): Promise<FileStat | null>; /** * What sits at a path, a link reported as itself, null when nothing does. * * git lstats the working tree, so a symlink is an entry in its own right: it * records mode 120000 and the target string, and reports a link to nothing as * untracked rather than as absent. */ export declare function lstat(vfs: Files, path: PathSpec): Promise<FileStat | null>; /** Whether a path names a symlink itself, the lstat a walk asks before it * follows anything. */ export declare function isLink(vfs: Files, path: PathSpec): Promise<boolean>; /** * The bytes git stores for one working-tree entry. * * A symlink's blob is its target string, not what the target holds, so reading * through the link would stage a second copy of the target under mode 100644 * and then report the entry modified forever after (the staged blob and the * bytes behind the link never match). The target is namespace state, which is * why it arrives on the stat rather than from a read. */ export declare function entryBytes(vfs: Files, path: PathSpec, info: FileStat): Promise<Uint8Array>; /** * Materialize one tree entry into the working tree. * * A 120000 entry is a symlink whose blob is the target string, so it is * restored through the namespace rather than written as content: writing the * blob would leave a regular file spelling the target. * * Whatever is already there goes first, whichever kind it is, because git * replaces a tree entry rather than merging with it. Two of the four * combinations are the ones that corrupt state: writing a regular blob at a * path the namespace holds a link for follows the link and lands the content in * the file it points at, damaging a path no branch touched while the link * stays; and linking over a regular file leaves that file behind the link, * ready to reappear when the link goes. The fourth, a link over a link, is the * retarget a checkout does when a branch moves where a link points: symlink(2) * does not overwrite, so the old name is removed rather than replaced in place. * The check is one lstat, which the listing the checkout walked usually * answers. * * The permission bits are part of the entry, not decoration on it. git records * exactly one of them, the owner's execute bit, and puts it back in both * directions: `chmod -x` on a `100755` path is a modification `restore` undoes, * and `chmod +x` on a `100644` one is a modification it undoes the other way. * Writing the bytes alone left the bit as the working tree had it, so the file * came back unrunnable and `status` went on calling it modified for ever. The * write is unconditional rather than probed: a stat to decide costs the same op * as the setattr it would save, and the backends git actually runs on apply it * natively, so nothing reaches the overlay. */ export declare function restoreEntry(vfs: Files, path: PathSpec, mode: string, blob: Uint8Array): Promise<void>; /** * Leave a submodule's working tree alone, but make sure it has one. * * A 160000 entry names a commit in another repository, which this one does not * hold: reading it as a blob is either a miss (an ordinary submodule keeps its * objects in its own store) or, when the id does happen to resolve here, an * empty string written over the directory. git does neither. It checks out no * submodule content at all without `--recurse-submodules`, and all the entry * asks of the working tree is that a directory stand at the name: an existing * one is left exactly as it is, untracked work included, a regular file or a * link is replaced by an empty one, and a missing one is created. Pinned * against git 2.50.1. * * @param vfs the session's files * @param path absolute virtual path of the submodule */ export declare function keepGitlink(vfs: Files, path: PathSpec): Promise<void>; /** * Take a submodule's directory away, or say why it stays. * * The other direction of `keepGitlink`, and it is not an unlink: what stands at * a 160000 entry is a directory, so git calls `rmdir` and warns rather than * failing when that cannot be done. An empty one goes; one still holding a * checked-out submodule, or anything else untracked, stays and is named; a * regular file or a link at the name is the `ENOTDIR` wording of the same * warning; a name with nothing at it is silent. The switch itself succeeds * either way, which is the whole point of warning instead of throwing. Pinned * against git 2.50.1. * * @param vfs the session's files * @param path absolute virtual path of the submodule * @param name the path as git prints it, repository-relative * @returns the warning line to write, null when there is nothing to say */ export declare function dropGitlink(vfs: Files, path: PathSpec, name: string): Promise<string | null>; /** Read a byte range of one virtual path. */ export declare function readRange(vfs: Files, path: PathSpec, offset: number, size: number): Promise<Uint8Array>; /** * Read a path that a repository may legitimately not have. * * `packed-refs` and `HEAD`-adjacent files are absent in perfectly valid * repositories, so a miss is an answer rather than an error. */ export declare function readOptional(vfs: Files, path: PathSpec): Promise<Uint8Array | null>; /** List a directory, empty when it does not exist. */ export declare function readNames(vfs: Files, path: PathSpec): Promise<string[]>; /** * Create a directory and every missing directory above it. * * Written out rather than delegated to `mkdir -p` because the parents flag is a * per-backend capability: the ops factory only wires `parents: true` for * backends that declare it, so a plain `mkdir` of `objects/ab` fails on the * rest. * * Existence is probed with a point stat, which on a prefix store misses a * directory that has no object of its own. That false negative is harmless here * and the reason this does not need the two-channel stat: on such a store a * directory is the set of keys under it, so creating one again costs a no-op * rather than an error. */ export declare function ensureDir(vfs: Files, path: PathSpec): Promise<void>; /** * Whether a path names a directory, false when nothing is there. * * Asked with a stat rather than inferred from a failed read: a read of a * directory misses on a keyed store and raises EISDIR on disk, so "the read did * not work" says nothing about what the path is. */ export declare function isDirectory(vfs: Files, path: PathSpec): Promise<boolean>; /** Whether a point lookup finds anything at a path. */ export declare function exists(vfs: Files, path: PathSpec): Promise<boolean>; /** * Take a file's lock and let it go, as git does before rewriting it. * * git creates `<path>.lock` before it rewrites the index or the config, even * when nothing in them changes, so a read-only repository refuses the verb there * before it looks any further. Creating the lock and removing it is that * refusal, and it leaves the file itself alone, so a write another session makes * to it is never undone. A lock already there is another writer's, which git * refuses and so does this (EEXIST), untouched. The look and the create are two * ops, since the dispatcher has no exclusive create. */ export declare function takeLock(vfs: Files, path: PathSpec): Promise<void>; /** Write one virtual path, creating the directories above it. */ export declare function writeFile(vfs: Files, path: PathSpec, data: Uint8Array): Promise<void>; /** * Delete one virtual path, tolerating one that is already gone. * * A miss is an answer rather than an error for every caller here: unstaging a * path deletes whatever ref or lock may or may not exist, and a checkout removes * files the other branch does not have. */ export declare function removeFile(vfs: Files, path: PathSpec): Promise<void>; /** * The nearest component above an entry that is not a directory. * * An entry's path is only a way through the working tree while every component * above it is a directory. Anything else standing on one -- a symlink, a * regular file, tracked or not -- is not a way through, and the two directions * take it differently. Writing the entry *replaces* it with the directory the * entry needs, leaving whatever a link pointed at exactly as it was; removing * the entry does nothing at all, because the path never led there. Both halves * are what git does, probed against git 2.50.1. * * The lstat is asked before the stat, and the order is the whole point: * `stat` dereferences, so a link to a directory stats as a directory and the * walk would carry on straight through it. * * An exact-path lookup cannot see any of this, since what is in the way sits * above the name being looked up rather than on it. * * @param vfs the session's files * @param worktree absolute virtual path of the working tree root * @param name the entry, repository-relative * @returns the absolute virtual path of the nearest such component, null when * every component above the entry is a directory */ export declare function blockingAncestor(vfs: Files, worktree: PathSpec, name: string): Promise<PathSpec | null>; /** * Move one virtual path, file or directory, to another name. * * The mount's own rename, so a directory moves with everything under it, * tracked or not, which is what `git mv` does with a directory. The * destination's directory is not created: git's rename fails when it is * missing, and the caller words that failure. */ export declare function renamePath(vfs: Files, source: PathSpec, target: PathSpec): Promise<void>; /** * Ask of every destination first what the write loop would meet later. * * `removeTree` refuses a directory holding a mount, but the loop reaches one * entry at a time, so a refusal there leaves the entries already written * standing on the target's content with HEAD and the index still where they * were. Asking first is the shape every other collision check in these verbs * already has: name what is in the way and change nothing. The condition * mirrors the write loop's exactly, a link included, so a destination the loop * would not clear is not refused here either. * * @param vfs the session's files * @param worktree absolute virtual path of the working tree root * @param names repository-relative paths about to be written */ export declare function refuseReplacedMounts(vfs: Files, worktree: PathSpec, names: readonly string[]): Promise<void>; /** * Refuse a removal that would take a nested mount with it. * * A mount nested inside the working tree is served by another VFS, and * `readdir` merges it into the parent's listing, so a walk that empties a * directory walks straight into the child backend and unlinks what is in it. * No branch ever recorded any of that, and the `rmdir` that follows takes the * mount root itself. Asking the mount table is the only way to see the * boundary: the parent backend cannot. * * A mount this session cannot see still blocks the removal: `submounts` * reports it by the nearest folder above it the session sees, which is not a * mount root, so only a visible one is named, naming a hidden mount being what * the hide exists to prevent. * * @param vfs the session's files * @param path absolute virtual path about to be removed */ export declare function refuseMount(vfs: Files, path: PathSpec): void; /** * Delete a path and everything under it, tracked or not. * * git replaces a tree entry rather than merging with it, so a directory * standing where the source keeps a file goes entirely. That is one of the few * places git removes a file it never tracked: an untracked child keeps the * directory alive after the tracked ones are gone, and restoring the file over * it would otherwise fail with the index already changed. * * A file and an absent path both walk out through the same two steps, since * `readdir` reads a non-directory as nothing there and `rmdir` refuses it. * * A child that is a symlink is unlinked, never descended into. Its lstat has * to say so, because `readdir` dereferences: a link to a directory lists * that directory's contents, and recursing on them deletes a tree outside the * one being replaced. `rm -r` does not follow a link either, so a branch * recording a file where the working tree has a directory takes the link away * with the directory and leaves whatever it pointed at exactly as it was. * Pinned against git 2.50.1. * * @param vfs the session's files * @param path absolute virtual path to clear */ export declare function removeTree(vfs: Files, path: PathSpec): Promise<void>; /** * Drop the directories a deletion left empty, up to a root. * * git removes a directory the moment its last tracked file is deleted or * restored away, so `rm -r docs` leaves no `docs/` behind. The walk stops at the * first directory that still holds something and never touches `stop` itself. * * @param vfs the session's files * @param path the file that was removed * @param stop the working tree root */ export declare function removeEmptyParents(vfs: Files, path: PathSpec, stop: PathSpec): Promise<void>; //# sourceMappingURL=io.d.ts.map