@struktoai/mirage-core
Version:
A Unified Virtual Filesystem For AI Agents
80 lines • 3.39 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 { toIsoZ } from "../../utils/dates.js";
import { DirListing, FileStat, FileType } from "../../types.js";
import { contentTypeForPath } from "../../utils/filetype.js";
import { rstripSlash } from "../../utils/slash.js";
import { getMetadata, getDirectoryMetadata, listDirectoryContents } from "./client.js";
import { isNotFound } from "./errors.js";
import { backendPath } from "./path.js";
import { compareCodePoints } from "../../utils/sort.js";
import { listingError } from "../../errors/fs.js";
async function exists(accessor, probe, key) {
try {
await probe(accessor, backendPath(accessor.config, key));
}
catch (exc) {
if (isNotFound(exc))
return false;
throw exc;
}
return true;
}
/**
* A folder's rows, as the Files API lists them; past a walk's `limit` the
* API is paged no further, and the listing is partial. Mirrors Python's
* `readdir`.
*
* Throws:
* FileNotFoundError: nothing is at the path.
* NotADirectoryError: the path, or a folder above it, is a file.
*/
export async function readdir(accessor, path, limit) {
const listPath = path.pattern !== null ? path.dir : path;
const remotePath = backendPath(accessor.config, listPath);
let entries;
try {
entries = await listDirectoryContents(accessor, remotePath, limit);
}
catch (exc) {
// The Files API answers 404 for a missing path and for a path under a
// file alike, so the errno comes from walking the ancestors: one
// metadata request per component, on this failure path only.
if (isNotFound(exc)) {
throw await listingError(listPath.virtual, listPath.mountPath, (p) => exists(accessor, getMetadata, p), (p) => exists(accessor, getDirectoryMetadata, p));
}
throw exc;
}
const rows = [];
for (const entry of [...entries].sort((a, b) => compareCodePoints(a.path, b.path))) {
const name = rstripSlash(entry.path).split('/').pop() ?? entry.path;
if (entry.isDirectory) {
rows.push(new FileStat({ name, type: FileType.DIRECTORY }));
continue;
}
let size = entry.fileSize;
size ??= (await getMetadata(accessor, entry.path)).contentLength;
rows.push(new FileStat({
name,
size,
modified: entry.lastModified === null ? null : toIsoZ(new Date(entry.lastModified)),
type: FileType.FILE,
content: contentTypeForPath(name),
}));
}
if (limit !== undefined && entries.length >= limit)
return new DirListing({ entries: rows, partial: true });
return rows;
}
//# sourceMappingURL=readdir.js.map