openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
104 lines (103 loc) • 3.99 kB
JavaScript
import { S as resolveGlobalSingleton } from "./number-coercion-CmpLVzZ6.mjs";
import { t as createDeferredCore } from "./deferred-D0La5CRk.mjs";
import { AsyncLocalStorage, AsyncResource } from "node:async_hooks";
//#region src/shared/async-work-scope.ts
const currentWorkScope = resolveGlobalSingleton(Symbol.for("openclaw.asyncWorkScope"), () => new AsyncLocalStorage());
const currentWorkScopeAncestry = resolveGlobalSingleton(Symbol.for("openclaw.asyncWorkScopeAncestry"), () => new AsyncLocalStorage());
const detachedAsyncContext = resolveGlobalSingleton(Symbol.for("openclaw.detachedAsyncContext"), () => new AsyncResource("openclaw.detached-async-context"));
/** Joins cooperating descendants even when their caller returns a cached value first. */
var AsyncWorkScope = class AsyncWorkScope {
constructor(failures) {
this.failures = failures;
this.pending = /* @__PURE__ */ new Set();
this.controller = new AbortController();
this.phase = "open";
}
get signal() {
return this.controller.signal;
}
get hasPendingWork() {
return this.pending.size > 0;
}
get isClosing() {
return this.phase !== "open";
}
enter(run) {
const owner = currentWorkScope.getStore();
const ancestry = currentWorkScopeAncestry.getStore();
const parent = owner ? ancestry?.scope === owner ? ancestry : { scope: owner } : void 0;
return currentWorkScopeAncestry.run({
scope: this,
parent
}, () => currentWorkScope.run(this, run));
}
/** Enters synchronous work without inspecting or assimilating its return value. */
run(run) {
if (this.phase === "closed") throw new Error("Async work scope is closed");
const operation = createDeferredCore();
this.pending.add(operation.promise);
try {
return this.enter(run);
} finally {
operation.resolve();
this.pending.delete(operation.promise);
}
}
track(run) {
if (this.phase === "closed") return Promise.reject(/* @__PURE__ */ new Error("Async work scope is closed"));
const operation = this.registerWork();
try {
operation.resolve(this.enter(run));
} catch (error) {
operation.reject(error);
}
return operation.promise;
}
registerWork() {
const operation = createDeferredCore();
this.pending.add(operation.promise);
operation.promise.then(() => this.pending.delete(operation.promise), (error) => {
this.pending.delete(operation.promise);
this.failures?.add(error);
});
return operation;
}
beginClose(reason) {
if (this.phase !== "open") return;
this.phase = "closing";
this.controller.abort(reason);
}
/** Starts the next phase in the same continuation that observes settled pending work. */
runWhenIdle(run) {
return AsyncWorkScope.runWhenAllIdle(() => [this], () => this.track(run));
}
/** Reselects owners so work admitted into a later phase is not mistaken for earlier work. */
static async runWhenAllIdle(selectScopes, run) {
let scopes = selectScopes();
while (scopes.some((scope) => scope.pending.size > 0)) {
await Promise.allSettled(scopes.flatMap((scope) => Array.from(scope.pending)));
scopes = selectScopes();
}
return run();
}
async drain() {
this.beginClose();
while (this.pending.size > 0) await Promise.allSettled(this.pending);
this.phase = "closed";
}
};
/** Outside a managed scope, the returned promise remains the caller's responsibility. */
async function trackAsyncWork(run) {
const scope = currentWorkScope.getStore();
return await (scope ? scope.track(run) : run());
}
/** Starts work its caller does not own, so the caller's scope neither waits for it nor closes under it. */
function runOutsideAsyncWorkScope(run) {
return currentWorkScope.exit(() => currentWorkScopeAncestry.exit(run));
}
/** Runs under the context-free async root initialized before managed work can begin. */
function runInDetachedAsyncContext(run) {
return detachedAsyncContext.runInAsyncScope(run);
}
//#endregion
export { trackAsyncWork as i, runInDetachedAsyncContext as n, runOutsideAsyncWorkScope as r, AsyncWorkScope as t };