@struktoai/mirage-core
Version:
A Unified Virtual Filesystem For AI Agents
133 lines • 5.38 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 { appendByStreamedRewrite } from "../../core/generic/rewrite.js";
import { checksum as s3Checksum } from "../../core/s3/checksum.js";
import { AWS_DOMAINS, CONDITION_SUPPORT, CUSTOM_CONDITION_SUPPORT, MD5_ETAG_STORES, } from "../../core/s3/constants.js";
import { copy as s3Copy, copyFrom as s3CopyFrom } from "../../core/s3/copy.js";
import { create as s3Create } from "../../core/s3/create.js";
import { exists as s3Exists } from "../../core/s3/exists.js";
import { mkdir as s3Mkdir } from "../../core/s3/mkdir.js";
import { readdir as s3Readdir, tree as s3Tree } from "../../core/s3/readdir.js";
import { rename as s3Rename } from "../../core/s3/rename.js";
import { rmR as s3RmR } from "../../core/s3/rm.js";
import { rmdir as s3Rmdir } from "../../core/s3/rmdir.js";
import { stat as s3Stat } from "../../core/s3/stat.js";
import { readRange as s3ReadRange, readStream as s3Stream } from "../../core/s3/stream.js";
import { truncate as s3Truncate } from "../../core/s3/truncate.js";
import { unlink as s3Unlink } from "../../core/s3/unlink.js";
import { replace as s3Replace, write as s3Write } from "../../core/s3/write.js";
import { exdev } from "../../errors/fs.js";
import { BaseVFS } from "../base.js";
function awsEndpoint(endpoint) {
let host;
try {
host = new URL(endpoint).hostname;
}
catch (error) {
console.debug(`endpoint not parsed: ${endpoint}: ${String(error)}`);
return false;
}
return AWS_DOMAINS.some((domain) => host === domain || host.endsWith(`.${domain}`));
}
/**
* S3's functions over its accessor, which the node and browser
* packages build over their own transport.
*/
export class S3VFS extends BaseVFS {
get conditionSupport() {
if (this.presigned)
return {};
if (this.name === 'minio')
return CUSTOM_CONDITION_SUPPORT;
const endpoint = this.resolvedEndpoint;
if (this.name === 's3' && endpoint !== undefined && endpoint !== '' && !awsEndpoint(endpoint))
return CUSTOM_CONDITION_SUPPORT;
return CONDITION_SUPPORT;
}
readsRanges = true;
/**
* The endpoint this mount's writes go to, for its write-condition row: the
* config's. The node mount also reads the environment. Mirrors Python's
* `resolved_endpoint`.
*/
get resolvedEndpoint() {
return this.accessor.config.endpoint;
}
/** Whether requests go through presigned URLs, which carry no condition. */
get presigned() {
return this.accessor.config.presignedUrlProvider !== undefined;
}
readTree(path, handed) {
return s3Tree(this.accessor, path, handed?.limit);
}
readdir(path, handed) {
return s3Readdir(this.accessor, path, handed?.limit);
}
read(path, _handed, offset = 0, size = null) {
if (offset === 0 && size === null)
return s3Stream(this.accessor, path);
return s3ReadRange(this.accessor, path, offset, size);
}
stat(path) {
return s3Stat(this.accessor, path);
}
copyFrom(source, src, dst) {
if (!(source instanceof S3VFS))
return Promise.reject(exdev(dst));
return s3CopyFrom(this.accessor, source.accessor, src, dst);
}
checksum(path, algorithm) {
if (!MD5_ETAG_STORES.has(this.name) || this.presigned)
return Promise.resolve(null);
const aws = this.name === 's3' && this.resolvedEndpoint === undefined;
return s3Checksum(this.accessor, path, algorithm, aws);
}
exists(path) {
return s3Exists(this.accessor, path);
}
write(path, data, signal) {
return s3Write(this.accessor, path, data, signal);
}
append(path, data) {
return appendByStreamedRewrite((p, remember) => s3Stream(this.accessor, p, remember), (request) => s3Replace(this.accessor, request), (p) => this.stat(p), path, data);
}
_replace(request, signal) {
return s3Replace(this.accessor, request, signal);
}
create(path) {
return s3Create(this.accessor, path);
}
mkdir(path, parents = false) {
return s3Mkdir(this.accessor, path, parents);
}
unlink(path) {
return s3Unlink(this.accessor, path);
}
rmdir(path) {
return s3Rmdir(this.accessor, path);
}
rmR(path) {
return s3RmR(this.accessor, path);
}
rename(src, dst) {
return s3Rename(this.accessor, src, dst);
}
copy(src, dst) {
return s3Copy(this.accessor, src, dst);
}
truncate(path, length, noCreate = false) {
return s3Truncate(this.accessor, path, length, noCreate);
}
}
//# sourceMappingURL=s3.js.map