@struktoai/mirage-core
Version:
A Unified Virtual Filesystem For AI Agents
65 lines • 3.25 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 { enoent, enotempty } from "../../errors/fs.js";
import { FileType } from "../../types.js";
import { liveRow, statFromItem } from "./readdir.js";
import { driveTargetName, eaccesOnDenied, parentFolder } from "./resolve.js";
/**
* Move an item, replacing what is at the destination, handed what is at
* `src` and its parent and at `dst` and its parent. Mirrors Python's
* `rename`.
*/
export const rename = eaccesOnDenied(async function (accessor, src, dst, handed = {}) {
const drive = accessor.drive;
const entry = await liveRow(accessor, src, handed.entry);
if (entry === null)
throw enoent(src);
const dstEntry = await liveRow(accessor, dst, handed.dstEntry);
if (dstEntry !== null) {
// GNU mv overwrites the destination: drop a conflicting file (or empty
// folder) before the move. A non-empty folder conflict is mv's
// "Directory not empty", mirroring the msgraph rename_replace.
if (dstEntry.type === FileType.DIRECTORY) {
const driveId = dstEntry.extra.drive_id;
const children = await drive.listFiles({
folderId: dstEntry.extra.file_id,
driveId: typeof driveId === 'string' ? driveId : null,
limit: 1,
});
if (children.length > 0)
throw enotempty(dst);
}
await drive.deleteFile(dstEntry.extra.file_id);
}
const [srcParentId] = await parentFolder(accessor, src, handed.parent);
const [dstParentId] = await parentFolder(accessor, dst, handed.dstParent);
const dstKey = dst.vfsPath;
const basename = dstKey.includes('/') ? dstKey.slice(dstKey.lastIndexOf('/') + 1) : dstKey;
const mime = entry.extra.mime_type;
const name = driveTargetName(basename, typeof mime === 'string' ? mime : '');
const move = dstParentId !== srcParentId;
// Drive stamps a rename as an edit, so the time goes back as rename(2)
// keeps it, as the live row just read it. Drive has no conditional update;
// reading it just before leaves one request for another writer's edit to
// land in.
const fileId = entry.extra.file_id;
const modified = entry.modified;
const item = await drive.patchFile(fileId, {
body: modified ? { name, modifiedTime: modified } : { name },
...(move ? { addParents: dstParentId, removeParents: srcParentId } : {}),
});
const moved = item;
return typeof moved === 'object' && moved !== null ? statFromItem(item) : null;
});
//# sourceMappingURL=rename.js.map