UNPKG

@struktoai/mirage-core

Version:

A Unified Virtual Filesystem For AI Agents

325 lines • 13.7 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 { chunks } from "../../io/cooperative.js"; import { closeQuietly } from "../../io/stream.js"; import { materialize } from "../../io/types.js"; import { requireSupported, writeCondition } from "../../cache/context.js"; import { ANY_WRITE, OwnRead } from "../../cache/types.js"; import { record, revisionFor, startOp } from "../../observe/context.js"; import { FileStat, FileType } from "../../types.js"; import { eacces, eexist, enoent, enotdir, enotsup, isEacces, isMissingPath, } from "../../errors/fs.js"; import * as kp from "../../utils/key_prefix.js"; import { contentTypeForPath } from "../../utils/filetype.js"; import { ancestors, gnuBasename, norm, parent } from "../../utils/path.js"; import { isDir } from "../../utils/stat_view.js"; import { refused, requireHook, } from "./driver.js"; import { ConditionLostError } from "./errors.js"; import { makeStat, provesDirectory } from "./stat.js"; /** Describe only the facts confirmed by a successful object write. */ export function writtenStat(path, meta) { if (meta === null) return null; return new FileStat({ name: gnuBasename(path.mountPath), size: meta.size, modified: meta.modified ?? null, type: FileType.FILE, content: contentTypeForPath(path.mountPath), fingerprint: meta.fingerprint ?? null, revision: meta.revision ?? null, extra: { ...(meta.extra ?? {}) }, }); } // Put one object, translating a missing container to ENOENT. The driver // primitives speak keys, so a store error for a missing repository or // bucket names the backend key, and only the factory holds the PathSpec // the message has to carry. // // Callers stamp `meta.fingerprint` on the op record and deliberately // leave `meta.revision` off it. captureFingerprints reads a write record // now, and an entry carrying a revision is pinned by installFingerprints // instead of drift-checked, so on a versioned store stamping one here // would pin replay to the revision preceding this write. async function put(driver, conn, key, data, path, cond = null) { try { requireSupported(driver.conditionSupport ?? {}, driver.vfs, path, 'put', cond ?? ANY_WRITE); if (cond === null || cond.kind === 'any') return await driver.put(conn, key, data); return await requireHook(driver.putIf)(conn, key, data, cond); } catch (err) { if (err instanceof ConditionLostError) throw await refused(path, err, cond); if (driver.isNotFound(err)) throw enoent(path); throw err; } } /** * Build the object write over one driver: bytes go up in one put, a stream as * it is produced where the store takes one, collected first where it does not. */ export function makeReplace(driver) { return async function replace(accessor, request, signal) { const { path, condition: cond } = request; const data = request.body; try { requireSupported(driver.conditionSupport ?? {}, driver.vfs, path, 'put', cond); const body = driver.putStream === undefined ? await materialize(data) : data; const key = kp.apply(driver.keyPrefixOf(accessor), path.mountPath); const timer = startOp(); let meta; let size; const { conn, close } = await driver.connect(accessor); try { if (body instanceof Uint8Array) { meta = await put(driver, conn, key, body, path, cond); size = body.byteLength; } else { const source = chunks(body, signal); try { meta = await requireHook(driver.putStream)(conn, key, source, cond); } finally { await closeQuietly(source); } size = meta.size ?? 0; } } catch (error) { if (error instanceof ConditionLostError) throw await refused(path, error, cond); if (driver.isNotFound(error)) throw enoent(path); throw error; } finally { await close(); } record('write', path.virtual, driver.vfs, size, timer, { fingerprint: meta?.fingerprint ?? null, }); return writtenStat(path, meta); } finally { await closeQuietly(data); } }; } export function makeWrite(driver) { const replace = makeReplace(driver); return async function write(accessor, path, body, signal) { let condition; try { condition = await writeCondition(path, 'put'); } catch (error) { await closeQuietly(body); throw error; } return replace(accessor, { path, body, condition }, signal); }; } /** Build the empty-object create over one driver. */ export function makeCreate(driver) { return async function create(accessor, path) { const key = kp.apply(driver.keyPrefixOf(accessor), path.mountPath); const cond = await writeCondition(path, 'put'); const timer = startOp(); const { conn, close } = await driver.connect(accessor); let meta; try { meta = await put(driver, conn, key, new Uint8Array(0), path, cond); } finally { await close(); } record('create', path.virtual, driver.vfs, 0, timer, { fingerprint: meta?.fingerprint ?? null, }); return writtenStat(path, meta); }; } /** Build read-slice-pad-rewrite truncation over one driver. */ export function makeTruncate(driver) { return async function truncate(accessor, path, length, noCreate = false) { if (noCreate) throw enotsup(driver.vfs, 'truncate --no-create', path); const key = kp.apply(driver.keyPrefixOf(accessor), path.mountPath); const timer = startOp(); const { conn, close } = await driver.connect(accessor); let meta; try { if (length === 0) { // Emptying reads nothing, so it carries the agent's version. const cond = await writeCondition(path, 'put'); meta = await put(driver, conn, key, new Uint8Array(0), path, cond); } else { let existing; let own = null; if (driver.getVersioned !== undefined) { let got; try { got = await driver.getVersioned(conn, key, revisionFor(path.virtual)); } catch (err) { if (isMissingPath(err)) throw enoent(path); throw err; } existing = got?.[0] ?? null; own = got === null ? OwnRead.ABSENT : got[1]; } else { existing = await driver.get(conn, key); } if (existing === null) own = OwnRead.ABSENT; else own ??= OwnRead.UNKNOWN; const data = existing ?? new Uint8Array(0); const result = new Uint8Array(length); result.set(data.subarray(0, Math.min(data.byteLength, length)), 0); // Remaining bytes are already zero-filled (Uint8Array default). const cond = await writeCondition(path, 'put', own); meta = await put(driver, conn, key, result, path, cond); } } finally { await close(); } record('truncate', path.virtual, driver.vfs, 0, timer, { fingerprint: meta?.fingerprint ?? null, }); return writtenStat(path, meta); }; } /** What the store holds at `path`, as a key or a prefix. */ async function rowAt(stat, accessor, path) { try { return await stat(accessor, path); } catch (err) { if (isMissingPath(err)) return null; throw err; } } /** * The outermost ancestor of `path` the store holds as a file. A refused * ancestor with keys below is a directory, as in stat; one with nothing below * is EACCES, naming the operand unless `-p` names the ancestor it stops at. */ async function fileAbove(driver, accessor, path, parents) { const prefix = driver.keyPrefixOf(accessor); const { conn, close } = await driver.connect(accessor); try { for (const ancestor of ancestors(norm(path.mountPath))) { let held; try { held = await driver.head(conn, kp.apply(prefix, ancestor)); } catch (err) { if (!isEacces(err)) throw err; const named = parents ? kp.mountedPath(path, ancestor) : path; if (await provesDirectory(driver, conn, kp.applyDir(prefix, ancestor), named)) continue; throw eacces(named); } if (held !== null) return ancestor; } } finally { await close(); } return null; } /** Build the marker-object mkdir over one driver. */ export function makeMkdir(driver) { const stat = makeStat(driver); return async function mkdir(accessor, path, parents = false) { const row = await rowAt(stat, accessor, path); if (row !== null && !(parents && isDir(row))) { // mkdir(2) refuses a name that exists, file or directory, and // `mkdir -p` passes only a directory. Rewriting the marker answered // success instead, and only the command builders check first: a // guest, FUSE and ws.vfs reach the op directly, the same callers // `makeRmdir` protects. throw eexist(path); } if (row === null) { const above = await fileAbove(driver, accessor, path, parents); // A directory cannot sit under a file, and a marker below one made // both unreadable. mkdir(2) blames the operand; the walk `-p` makes // stops at the file and names it. if (above !== null) throw enotdir(parents ? kp.mountedPath(path, above) : path); const up = parent(norm(path.mountPath)); // mkdir(2) makes one directory, under one that exists; only `-p` // makes the chain, so a marker under a missing parent answers // ENOENT however the caller reached it. A store without markers // holds no empty directory, so a parent made a moment ago has no // row to find. if (!parents && driver.markersSupported !== false && up !== '/' && (await rowAt(stat, accessor, kp.mountedPath(path, up))) === null) { throw enoent(path); } } if (driver.markersSupported === false) { // The store refuses the marker client-side (hf: create_dir is // unsupported and a slash-terminated write is IsADirectory), so a // directory exists only while it holds a key and mkdir has // nothing to write: `mkdir x` then `rmdir x` is ENOENT here but // fine on a marker store. return; } // Object stores have no real directories. A zero-byte marker keyed at // the prefix makes the empty directory visible, and `-p` keys one for // every directory of the chain it makes: one only the leaf's marker // implies has no time and goes with the leaf. const made = [path.mountPath]; if (row === null && parents) { for (const up of ancestors(norm(path.mountPath)).reverse()) { if ((await rowAt(stat, accessor, kp.mountedPath(path, up))) !== null) break; made.push(up); } } const prefix = driver.keyPrefixOf(accessor); const pfx = kp.applyDir(prefix, path.mountPath); if (pfx === '') return; const { conn, close } = await driver.connect(accessor); try { // `-p` on a directory that holds its marker leaves the marker as it // is: a rewrite replaces its metadata and, in a versioned bucket, adds // a version. A directory only a key below implies still gets one. if (row !== null && (await driver.head(conn, pfx)) !== null) return; for (const dir of made.reverse()) { await driver.put(conn, kp.applyDir(prefix, dir), new Uint8Array(0)); } } finally { await close(); } }; } //# sourceMappingURL=write.js.map