Files
reverse-engineering-anything/src/application/CapabilityScope.ts
T
gensokyo-dev e18594b247 refactor(composition): unify capability bindings and cleanup ownership (#8441)
Bind typed service creation, observed availability and retryable cleanup in one composition owner. Reuse selected CLI/MCP factories and canonical contract groups without copying provider operation inventories.

Closes #8429. Refs #740.

Validated paired real application workflows, actual local Codex CLI Terra/high use, complete Evidence and Unknown checkpoint parity, compiler-backed import boundaries, SDK schemas and final real mixed routing. Automated review findings were fixed at their producing owners. Existing exact-base CodeQL alerts are documented on the PR under the owner-authorized unrelated-check exception; no global alerts or security rules were changed.

Assisted-by: Suika (gensokyo-skills:suika-scatter-gather-planning)
2026-11-01 13:30:21 +08:00

41 lines
1.4 KiB
TypeScript

/** One host owns these exact close capabilities; a released peer stays released. */
export class CapabilityScope {
readonly #pending = new Map<string, () => Promise<void>>();
#accepting = true;
#closing: Promise<void> | undefined;
constructor(readonly failureMessage = "REA provider cleanup failed") {}
own<Value>(id: string, value: Value, close: () => Promise<void>): Value {
if (!this.#accepting)
throw new Error(`Cannot acquire capability ${id} after host shutdown`);
if (this.#pending.has(id))
throw new Error(`Capability ${id} already belongs to this host`);
this.#pending.set(id, close);
return value;
}
/** Join concurrent closes, retaining only failed owners for the next retry. */
close(): Promise<void> {
this.#accepting = false;
this.#closing ??= Promise.allSettled(
[...this.#pending].map(async ([id, close]) => {
// A synchronous adapter throw must not prevent other owners' cleanup.
await Promise.resolve().then(close);
this.#pending.delete(id);
}),
)
.then((results) => {
const failures = results.flatMap((result) =>
result.status === "rejected" ? [result.reason] : [],
);
if (failures.length > 0)
throw new AggregateError(failures, this.failureMessage);
})
.finally(() => {
this.#closing = undefined;
});
return this.#closing;
}
}