@struktoai/mirage-core
Version:
A Unified Virtual Filesystem For AI Agents
220 lines • 9.79 kB
JavaScript
// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
import { entryOrWarm } from "../../cache/index/warm.js";
import { enoent } from "../../errors/fs.js";
import { compareCodePoints } from "../../utils/sort.js";
import { DirListing, FileStat, FileType } from "../../types.js";
import { rstripSlash, stripSlash } from "../../utils/slash.js";
/**
* The key a mount keeps `path` under in its index: the virtual path, the
* mount root spelled without its slash. A glob word is keyed as the
* directory it lists. Mirrors Python's `index_key`.
*/
export function indexKey(prefix, path) {
const root = rstripSlash(prefix);
const key = stripSlash((path.pattern !== null ? path.dir : path).vfsPath);
return key !== '' ? `${root}/${key}` : root || '/';
}
/** The key of the directory holding `key`; the root holds itself. */
export function folderOf(key) {
return key.slice(0, key.lastIndexOf('/')) || '/';
}
/**
* A directory's listing as the index holds it, null when the index holds no
* complete, live listing for it. The mount's read policy rides the index: a
* listing past its bound, or one a `fresh` mount's check refuses, answers
* null here. A window (the newest messages, the last days) answers no glob:
* the backend moves its window to the span the glob asks for. Mirrors
* Python's `stored_listing`.
*/
export async function storedListing(index, key, glob = false) {
const listing = await index.listDir(key);
if (glob && listing.window === true)
return null;
return listing.entries ?? null;
}
/**
* Whether `key`'s folder holds a window that does not name it. A window shows
* everything to show but proves nothing absent: a day it does not list may
* still be there, and only the backend says. Mirrors Python's
* `beyond_window`.
*/
export async function beyondWindow(index, key) {
const listing = await index.listDir(folderOf(key), key);
return listing.window === true && listing.entries != null && !listing.entries.includes(key);
}
/**
* Keep what a backend listed, and answer the children's paths. A plain list is
* the directory's whole listing. A `DirListing` may also prove descendant
* listings (`seeds`), be a view rather than the whole directory (`partial`),
* or prove nothing absent (`window`); each is kept for what it proves, and a
* seed at the version its listing was read at.
*/
export async function keepListing(index, key, listed, refill = null, fetched) {
const listing = listed instanceof DirListing ? listed : new DirListing({ entries: listed });
const rows = listing.entries.map((row) => [row.name, row]);
if (refill !== null) {
const accepted = await index.refillDir(key, rows, refill, null, {
partial: listing.partial,
window: listing.window,
version: listing.version,
});
if (accepted === null) {
const stem = key === '/' ? '' : key;
if (fetched !== undefined) {
const named = new Map(listing.entries.map((row) => [`${stem}/${row.name}`, row]));
const complete = !listing.partial && !listing.window;
for (const child of [...fetched.keys()]) {
const row = named.get(child);
if (row !== undefined)
fetched.set(child, row);
else if (complete)
fetched.delete(child);
}
}
return listing.entries.map((row) => `${stem}/${row.name}`);
}
}
else if (listing.partial)
await index.setPartialDir(key, rows);
else
await index.setDir(key, rows, null, { window: listing.window, version: listing.version });
const stem = key === '/' ? '' : key;
for (const [relative, children] of listing.seeds) {
const seeded = children.map((row) => [row.name, row]);
const folder = `${stem}/${stripSlash(relative)}`;
const options = { window: listing.window, version: listing.version };
if (refill === null)
await index.setDir(folder, seeded, null, options);
else
await index.refillDir(folder, seeded, refill, null, options);
}
return listing.entries.map((row) => `${stem}/${row.name}`);
}
/**
* Whether the stored listing of the path's folder leaves it out without
* proving it absent: a complete listing the running command did not fetch,
* which another client may have added the name to since. Mirrors Python's
* `unproven_miss`.
*/
export async function unprovenMiss(index, key) {
const folder = folderOf(key);
const listing = await index.listDir(folder, key);
return (listing.entries != null &&
listing.window !== true &&
!listing.entries.includes(key) &&
!index.listedThisCommand(folder));
}
/**
* A stat the stored listings answer, null when none does. The row the
* parent's listing holds answers it, so a listing costs one request and the
* stat of each child after it none. A complete listing that does not name the
* path rejects with ENOENT the same way, where `proves` says its folder's
* listing proves a name absent; a window that does not answers nothing, and
* neither does a listing that proves nothing. A directory whose own listing is
* held answers with the row a subtree read kept for it. `key` is below the
* mount root.
*/
export async function storedStat(index, key, proves = null) {
const folder = folderOf(key);
const listing = await index.listDir(folder, key);
if (listing.entries != null) {
if (!listing.entries.includes(key)) {
if (listing.window === true || (proves !== null && !proves(folder)))
return null;
throw enoent(key);
}
}
else if (listing.partialEntries?.includes(key) !== true) {
if ((await storedListing(index, key)) === null)
return null;
}
const row = (await index.get(key)).entry;
return row instanceof FileStat ? row : null;
}
/**
* What is at `key`, as its parent's listing names it, listing the parent
* through `warm` when no listing proves it; null when nothing is there. `key`
* is below the mount root, which no listing names.
*/
export async function entryAt(index, key, warm) {
const row = await entryOrWarm(index, key, warm);
return row instanceof FileStat ? row : null;
}
/**
* A subtree read the stored listings answer, null when one does not: every
* entry below the directory as its parent's listing holds it, keyed by its
* path below `key`, which is what a walk of `readdir` and `stat` over the same
* listings would see. Null while any directory under it has no complete, live
* listing; a window is not one. Mirrors Python's `stored_tree`.
*/
export async function storedTree(index, key) {
const found = new Map();
const base = key === '/' ? '' : key;
const pending = [key];
for (let folder = pending.pop(); folder !== undefined; folder = pending.pop()) {
const listing = await index.listDir(folder);
const children = listing.window === true ? null : (listing.entries ?? null);
if (children === null)
return null;
for (const child of children) {
const row = (await index.get(child)).entry;
if (!(row instanceof FileStat))
return null;
found.set(child.slice(base.length + 1), row);
if (row.type === FileType.DIRECTORY)
pending.push(child);
}
}
return found;
}
/**
* Keep a subtree read as the listings it proves: it names everything below the
* directory, so it is a complete listing of the directory and of each
* directory below it. The directory's own stat, which a read may name under
* `''`, is kept where a stat of it finds it. A folder whose listing is live
* keeps it: one that landed while the read ran may be newer than the read, and
* replacing it would report its newer children gone. Mirrors Python's
* `keep_tree`.
*/
export async function keepTree(index, key, tree, refill = null) {
const base = key === '/' ? '' : key;
const listings = new Map([[key, []]]);
for (const relative of [...tree.keys()].sort(compareCodePoints)) {
const row = tree.get(relative);
if (row === undefined)
continue;
if (relative === '') {
await index.put(key, row);
continue;
}
const cut = relative.lastIndexOf('/');
const folder = cut === -1 ? key : `${base}/${relative.slice(0, cut)}`;
const rows = listings.get(folder) ?? [];
rows.push([row.name, row]);
listings.set(folder, rows);
if (row.type === FileType.DIRECTORY && !listings.has(`${base}/${relative}`)) {
listings.set(`${base}/${relative}`, []);
}
}
for (const [folder, rows] of listings) {
if ((await index.listDir(folder)).entries == null) {
if (refill === null)
await index.setDir(folder, rows);
else
await index.refillDir(folder, rows, refill);
}
}
}
//# sourceMappingURL=listing.js.map