UNPKG

@struktoai/mirage-core

Version:

A Unified Virtual Filesystem For AI Agents

101 lines • 4.24 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 { checkPath } from "./paths.js"; import { DirListing, FileStat, FileType } from "../../types.js"; import { listingError } from "../../errors/fs.js"; import { DropboxApiError } from "./client.js"; import { getMetadata, listFolder } from "./api.js"; import { CONTENT_HASH } from "./constants.js"; import { tokenOf } from "./fingerprint.js"; import { contentTypeForPath } from "../../utils/filetype.js"; import { stripSlash } from "../../utils/slash.js"; import { compareCodePoints } from "../../utils/sort.js"; export function statFromEntry(entry) { const modified = entry.server_modified ?? entry.client_modified ?? ''; if (entry['.tag'] === 'folder') { return new FileStat({ name: entry.name, type: FileType.DIRECTORY, modified, extra: { dropbox_id: entry.id ?? entry.path_display ?? entry.name }, }); } return new FileStat({ name: entry.name, size: typeof entry.size === 'number' ? entry.size : null, type: FileType.FILE, content: contentTypeForPath(entry.name), modified, fingerprint: tokenOf(entry[CONTENT_HASH]), extra: { dropbox_id: entry.id ?? entry.path_display ?? entry.name, resource_type: 'dropbox/file', }, }); } export function dropboxPathFromKey(root, key) { if (key === '') return root; return `${root}/${key}`; } async function metadataOrNull(accessor, key) { try { return await getMetadata(accessor.tokenManager, dropboxPathFromKey(accessor.rootPath, stripSlash(key))); } catch (err) { if (err instanceof DropboxApiError && err.status === 409) return null; throw err; } } async function isFile(accessor, key) { const entry = await metadataOrNull(accessor, key); return entry !== null && entry['.tag'] !== 'folder'; } async function isDir(accessor, key) { const entry = await metadataOrNull(accessor, key); return entry !== null && entry['.tag'] === 'folder'; } /** * A folder's children, each as `stat` answers it; past a walk's `limit` * Dropbox is paged no further, and the listing is partial. Mirrors Python's * `readdir`. */ export async function readdir(accessor, path, limit) { await checkPath(accessor, path.pattern !== null ? path.dir : path); const key = (path.pattern !== null ? path.dir : path).vfsPath; const dropboxPath = dropboxPathFromKey(accessor.rootPath, stripSlash(key)); let files; try { files = await listFolder(accessor.tokenManager, dropboxPath, limit === undefined ? {} : { limit }); } catch (err) { // list_folder 409s on a missing path and on a file operand alike // (path/not_found, path/not_folder), and the subtag cannot tell ENOENT // from ENOTDIR either: a path under a file is not_found, where // opendir(2) reports Not a directory. So walk the ancestors and let the // walk pick the errno, at one request per component on this failure // path only. if (err instanceof DropboxApiError && err.status === 409) { throw await listingError(path.virtual, key, (p) => isFile(accessor, p), (p) => isDir(accessor, p)); } throw err; } const rows = files.map(statFromEntry); rows.sort((a, b) => compareCodePoints(a.name, b.name)); if (limit !== undefined && files.length >= limit) return new DirListing({ entries: rows, partial: true }); return rows; } //# sourceMappingURL=readdir.js.map