@struktoai/mirage-core
Version:
A Unified Virtual Filesystem For AI Agents
88 lines • 3.96 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 { FileStat, FileType } from "../../types.js";
import { enoent } from "../../errors/fs.js";
import { GraphError, graphGet } from "../msgraph/client.js";
import { asNumber, batchTree, deltaTree, folderChildCount, statItem, virtualKey, } from "../msgraph/drive.js";
import { driveBase, driveLoc } from "./client.js";
export async function stat(accessor, path) {
if (path.vfsPath === '') {
// The mount root is a real Graph item (`/drive/root` or the keyPrefix
// folder); fetch it so modified is populated instead of synthesizing a
// bare directory stat. Its `size` is Graph's aggregate subtree storage
// number, not rendered content length: expose it as extra, like every
// other folder (see entryStat).
try {
const item = await graphGet(accessor.config, driveLoc(accessor.config, '').item());
return new FileStat({
name: '/',
type: FileType.DIRECTORY,
modified: typeof item.lastModifiedDateTime === 'string' ? item.lastModifiedDateTime : null,
extra: { size_bytes: asNumber(item.size), child_count: folderChildCount(item) },
});
}
catch (error) {
if (error instanceof GraphError && error.status === 404)
throw enoent(path);
throw error;
}
}
return statItem(accessor.config, driveLoc(accessor.config, path.vfsPath), path);
}
/**
* Every item under a folder, each as a listing's stat renders it.
*
* A personal drive answers from one read of Graph's `/delta` feed
* (`deltaTree`): it starts the feed at any folder and carries each file's
* `cTag`, the token its listing row holds. OneDrive for Business starts the
* feed only at the drive root and leaves the `cTag` out, so its folders'
* listings go out batched instead (`batchTree`), as they do for a feed that
* does not answer and for a drive that will not say which kind it is. Which
* kind the drive is, Graph's `driveType`, is asked once per mount. Mirrors
* Python's `tree`.
*/
export async function tree(accessor, path, limit) {
const config = accessor.config;
const loc = driveLoc(config, path.vfsPath);
if (limit === undefined && (await driveType(accessor)) === 'personal') {
const found = await deltaTree(config, loc, virtualKey(path));
if (found !== null)
return found;
}
return batchTree(config, loc, path, limit);
}
/**
* The drive's Graph `driveType`, asked on first use and kept; '' while Graph
* refuses to say, which is asked again next time. Mirrors Python's
* `_drive_type`.
*/
async function driveType(accessor) {
if (accessor.driveType === null) {
let drive;
try {
drive = await graphGet(accessor.config, driveBase(accessor.config), {
$select: 'driveType',
});
}
catch (err) {
if (!(err instanceof GraphError))
throw err;
console.debug(`onedrive drive type unanswered: ${err.message}`);
return '';
}
accessor.driveType = typeof drive.driveType === 'string' ? drive.driveType : '';
}
return accessor.driveType;
}
//# sourceMappingURL=stat.js.map