@struktoai/mirage-core
Version:
A Unified Virtual Filesystem For AI Agents
75 lines • 3.13 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 { rawPathOf } from "../../utils/key_prefix.js";
import { record, revisionFor, startOp } from "../../observe/context.js";
import { VFSName } from "../../types.js";
import { createS3Client, isDenied, isNotFound, loadS3Module, s3Key } from "./client.js";
import { eacces, enoent } from "../../errors/fs.js";
import { fpRevFromS3Response } from "./read.js";
// An MD5 as an ETag spells it: 32 lowercase hex digits, no part count.
const MD5_ETAG = /^[0-9a-f]{32}$/;
/**
* An object's MD5, read off its ETag when the ETag is one.
*
* A store names an object's MD5 as its ETag only for a single-part upload
* kept unencrypted, or on AWS under SSE-S3: a multipart ETag ends in `-N`, and
* an object under SSE-KMS or SSE-C carries some other 32 hex digits, which no
* look at the ETag tells apart, so the head's encryption decides. `aws` says
* the store is AWS itself, where SSE-S3 keeps the MD5. Every other algorithm
* is null. A digest answered is recorded as the read it stands in for, with
* the ETag and version the head saw, so a snapshot pins them and a
* conditional write later in the line is conditioned on them. Mirrors
* Python's core/s3/checksum.py.
*/
export async function checksum(accessor, path, algorithm, aws) {
if (algorithm !== 'md5')
return null;
const { config } = accessor;
const { HeadObjectCommand } = await loadS3Module(config);
const client = await createS3Client(config);
const input = {
Bucket: config.bucket,
Key: s3Key(rawPathOf(path), config),
};
const pinned = revisionFor(path.virtual);
if (pinned !== null)
input.VersionId = pinned;
let resp;
const timer = startOp();
try {
resp = await client.send(new HeadObjectCommand(input));
}
catch (err) {
if (isNotFound(err))
throw enoent(path);
if (isDenied(err))
throw eacces(path);
throw err;
}
finally {
;
client.destroy?.();
}
const sse = resp.ServerSideEncryption ?? null;
if ((resp.SSECustomerAlgorithm ?? null) !== null)
return null;
if (sse !== null && !(aws && sse === 'AES256'))
return null;
const etag = (resp.ETag ?? '').replace(/^"|"$/g, '');
if (!MD5_ETAG.test(etag))
return null;
record('read', path.virtual, VFSName.S3, 0, timer, fpRevFromS3Response(resp));
return etag;
}
//# sourceMappingURL=checksum.js.map