@struktoai/mirage-core
Version:
A Unified Virtual Filesystem For AI Agents
85 lines • 3.8 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 { normalizeKeyPrefix } from "../../vfs/s3/config.js";
import { RecordNamespaceStore } from "../mount/namespace/store.js";
import { S3RecordClient } from "../record/s3.js";
import { WorkspaceStateStore, } from "./base.js";
/**
* WorkspaceStateStore hosting the sessions + metadata group on S3.
*
* Object layout under one bucket and prefix:
*
* - `{prefix}workspaces/{ws}.json` — metadata record
* - `{prefix}{ws}/sessions/{session_id}.json` — session table
*
* Every record write is CAS-gated by a conditional PUT anchored on the
* compare-read's ETag, giving S3 the same generation contract as the
* Redis Lua script; bucket versioning (when enabled) doubles as an
* audit trail for free. This store hosts only the sessions+meta
* group: namespace nodes and observer events are chatty per-op planes
* that belong on RAM or Redis, so use this store as the `workspace`
* group override of a RAM or Redis default store. Mirrors the Python
* S3WorkspaceStateStore.
*/
export class S3WorkspaceStateStore extends WorkspaceStateStore {
config;
prefix;
meta;
sessionTables = new Map();
constructor(config, overrides = {}) {
super(overrides);
this.config = config;
this.prefix = normalizeKeyPrefix(config.keyPrefix) ?? '';
this.meta = new S3RecordClient(config, `${this.prefix}workspaces/`);
}
makeNamespace(_workspaceId) {
throw new Error('The s3 store hosts only the sessions+meta group; keep the namespace ' +
"plane on RAM or Redis and pass the s3 store as the 'workspace' group override.");
}
makeObserver(_workspaceId) {
throw new Error('The s3 store hosts only the sessions+meta group; keep the observer ' +
"plane on RAM or Redis and pass the s3 store as the 'workspace' group override.");
}
makeJobs(_workspaceId) {
throw new Error('The s3 store hosts only the sessions+meta group; keep the jobs ' +
"plane on RAM or Redis and pass the s3 store as the 'workspace' group override.");
}
makeSessions(workspaceId) {
let table = this.sessionTables.get(workspaceId);
if (table === undefined) {
const scoped = normalizeKeyPrefix(`${this.prefix}${workspaceId}/`) ?? '';
table = new RecordNamespaceStore(new S3RecordClient(this.config, `${scoped}sessions/`));
this.sessionTables.set(workspaceId, table);
}
return table;
}
async readMeta(workspaceId) {
const [fields] = await this.meta.get(workspaceId);
return fields;
}
casWriteMeta(workspaceId, fields, expectedGeneration) {
return this.meta.casPut(workspaceId, fields, expectedGeneration);
}
async forgetSelf(workspaceId) {
await this.sessionTables.get(workspaceId)?.close();
this.sessionTables.delete(workspaceId);
await this.meta.delete([workspaceId]);
}
async closeSelf() {
for (const table of this.sessionTables.values())
await table.close();
await this.meta.close();
}
}
//# sourceMappingURL=s3.js.map