@struktoai/mirage-core
Version:
A Unified Virtual Filesystem For AI Agents
103 lines • 3.49 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 { KeyLock } from "../cache/lock.js";
/** Work in flight on one resource, awaited before it is released. */
export class Activity {
count = 0;
waiters = [];
acquire() {
this.count++;
let released = false;
return () => {
if (released)
return;
released = true;
this.count--;
if (this.count === 0) {
for (const resolve of this.waiters.splice(0))
resolve();
}
};
}
wait() {
if (this.count === 0)
return Promise.resolve();
return new Promise((resolve) => {
this.waiters.push(resolve);
});
}
/** Count `source` in flight until it ends or is closed. */
hold(source) {
if (source instanceof Uint8Array)
return source;
return new ActivityStream(source, this.acquire());
}
}
/** Explicit return releases even a stream that was never pulled. */
class ActivityStream {
release;
source;
pullLock = new KeyLock();
closing;
constructor(source, release) {
this.release = release;
this.source = source[Symbol.asyncIterator]();
}
[Symbol.asyncIterator]() {
return this;
}
isClosing() {
return this.closing !== undefined;
}
next() {
return this.pullLock.withLock('', async () => {
if (this.isClosing())
return { done: true, value: undefined };
try {
const step = await this.source.next();
if (step.done === true && this.closing === undefined)
this.release();
return step;
}
catch (error) {
if (this.closing === undefined)
this.release();
throw error;
}
});
}
return() {
this.closing ??= this.finish();
return this.closing;
}
async finish() {
// A pull can wait on a source that only yields when something lands (a
// follow), so the source is told to close before the pull ends, not
// after: its own close is what ends that wait.
const closing = Promise.resolve().then(() => this.source.return?.());
// Handled here so a failed close is not unhandled while the pull ends;
// awaiting it below still hands the failure to the caller.
void closing.catch(() => undefined);
return this.pullLock.withLock('', async () => {
try {
await closing;
return { done: true, value: undefined };
}
finally {
this.release();
}
});
}
}
//# sourceMappingURL=activity.js.map