@struktoai/mirage-core
Version:
A Unified Virtual Filesystem For AI Agents
325 lines • 13.7 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 { 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