UNPKG

@struktoai/mirage-core

Version:

A Unified Virtual Filesystem For AI Agents

74 lines • 3.41 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 { rstripSlash } from "../../utils/slash.js"; import { FileStat, FileType } from "../../types.js"; import { keyStat } from "./stat.js"; import { readdirError } from "../../errors/fs.js"; import { gnuBasename, norm } from "../../utils/path.js"; import { compareCodePoints } from "../../utils/sort.js"; /** The keys of a directory's children, sorted. Mirrors Python's `child_keys`. */ export async function childKeys(accessor, path) { // A pattern spec addresses the directory whose entries the glob filters, // and the rest of this function works in mount-relative space, so the // directory has to be read off `dir` rather than off the virtual // `directory` string (python spells it the same way). const target = path.pattern !== null ? path.dir : path; const p = norm(target.mountPath); if (!accessor.store.dirs.has(p)) { throw await readdirError(path, p, (k) => accessor.store.files.has(k), (k) => accessor.store.dirs.has(k)); } const dirPrefix = p === '/' ? '/' : `${p}/`; const seen = new Set(); const keys = [...accessor.store.files.keys(), ...accessor.store.dirs]; for (const key of keys) { if (key === p) continue; if (!key.startsWith(dirPrefix)) continue; const first = key.slice(dirPrefix.length).split('/')[0]; if (first) seen.add(`${dirPrefix}${first}`); } return [...seen].sort(compareCodePoints); } export async function readdir(accessor, path) { // A key restored without its folder still lists the folder. return (await childKeys(accessor, path)).map((key) => keyStat(accessor.store, key) ?? new FileStat({ name: gnuBasename(key), type: FileType.DIRECTORY })); } /** * Every key under a directory, each as `stat` answers it; null when `path` is * no directory, or when a key's parent is none (a file restored without its * folder), so the walk's readdir and stat report what is there. Mirrors * Python's `tree`. */ export function tree(accessor, path) { const store = accessor.store; const p = norm(path.mountPath); if (!store.dirs.has(p)) return Promise.resolve(null); const base = rstripSlash(p) + '/'; const keys = [...store.files.keys(), ...store.dirs].filter((key) => key.startsWith(base) && key !== p); if (keys.some((key) => !store.dirs.has(key.slice(0, key.lastIndexOf('/')) || '/'))) { return Promise.resolve(null); } const found = new Map(); for (const key of keys) { const entry = keyStat(store, key); if (entry !== null) found.set(key.slice(base.length), entry); } return Promise.resolve(found); } //# sourceMappingURL=readdir.js.map