UNPKG

@struktoai/mirage-core

Version:

A Unified Virtual Filesystem For AI Agents

220 lines • 9.79 kB
// ========= 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