@struktoai/mirage-core
Version:
A Unified Virtual Filesystem For AI Agents
84 lines • 3.81 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 { fileEntry } from "../hierarchy/probe.js";
import { publishRead, writesConditioned } from "../../cache/context.js";
import { record, recordStream, startOp } from "../../observe/context.js";
import {} from "../../types.js";
import { Sha1, sha1Hex } from "../../utils/hash.js";
import { downloadFile, downloadFileStream } from "./api.js";
import { entryToken, readToken } from "./fingerprint.js";
import { windowFor } from "../../utils/ranges.js";
/**
* Read a file, optionally only a byte range of it. On a `write: conditional`
* mount a whole read stamps its bytes' own sha1, even through an outdated
* listing row.
*
* Args:
* accessor: Box accessor.
* path: the path to read.
* options: `{offset, size}`, the byte window, or absent for the whole file.
* entry: what is at the path.
*/
export async function read(accessor, path, options, entry) {
const window = windowFor(options?.offset ?? 0, options?.size ?? null);
const found = fileEntry(path, entry);
const timer = startOp();
const data = await downloadFile(accessor.tokenManager, found.extra.box_id, window);
// Only a whole read through a row with a sha1 can yield a token, so nothing
// else is hashed. The token never depends on a recorder being bound
// (read_revalidatable.test.ts holds each declarer to it). A whole buffer
// prefers WebCrypto's native SHA-1, falling back to incremental Sha1
// when unavailable. Streams use incremental Sha1 without buffering.
const hashable = window === undefined && entryToken(found) !== null;
const digest = hashable ? await sha1Hex(data) : null;
let fingerprint = null;
if (digest !== null)
fingerprint = writesConditioned(path) ? digest : readToken(found, digest);
publishRead(path.virtual, data, fingerprint);
record('read', path.virtual, 'box', data.byteLength, timer, {
fingerprint,
});
return data;
}
/**
* Stream a file, stamped with its sha1 once it has been read whole. When a
* token can result (a recorder is bound and the row has a sha1), each chunk
* feeds a running SHA-1; the token lands only after the last chunk, so a
* stream abandoned part-way stamps nothing. The token is chosen as `read`
* chooses it.
*
* Args:
* accessor: Box accessor.
* path: the path to read.
* entry: what is at the path.
*/
export async function* readStream(accessor, path, entry) {
const found = fileEntry(path, entry);
const rec = recordStream('read', path.virtual, 'box');
const conditioned = writesConditioned(path);
const digest = rec !== null && entryToken(found) !== null ? new Sha1() : null;
for await (const chunk of downloadFileStream(accessor.tokenManager, found.extra.box_id)) {
digest?.update(chunk);
if (rec !== null)
rec.bytes += chunk.byteLength;
yield chunk;
}
if (rec !== null && digest !== null) {
const token = digest.digest();
rec.fingerprint = conditioned ? token : readToken(found, token);
}
if (rec !== null)
rec.complete = true;
}
//# sourceMappingURL=read.js.map