UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

61 lines (60 loc) • 2.94 kB
//#region src/shared/global-singleton.ts /** * Process-local singleton helpers for registries, caches, and SDK-visible shared state. * Keys must be symbols so unrelated modules cannot collide on `globalThis` property names. */ const GLOBAL_SINGLETON_RESETS_KEY = Symbol.for("openclaw.globalSingletonLifecycleResets"); function resolveGlobalSingletonResetRegistry() { const globalStore = globalThis; const existing = globalStore[GLOBAL_SINGLETON_RESETS_KEY]; if (existing instanceof Map) return existing; const created = /* @__PURE__ */ new Map(); globalStore[GLOBAL_SINGLETON_RESETS_KEY] = created; return created; } /** Observe an existing owner's state without initializing it or registering cleanup. */ function readGlobalSingleton(key) { const globalStore = globalThis; return Object.hasOwn(globalStore, key) ? globalStore[key] : void 0; } /** Resolves a process-local singleton for caches and registries that tolerate helper lookup. */ function resolveGlobalSingleton(key, create, reset, lifecycle = "close-and-restart") { const globalStore = globalThis; let value; if (Object.hasOwn(globalStore, key)) value = globalStore[key]; else { value = create(); globalStore[key] = value; } if (reset) resolveGlobalSingletonResetRegistry().set(key, { lifecycle, reset: () => reset(value) }); return value; } /** Resolves a process-local Map singleton for keyed caches backed by globalThis. */ function resolveGlobalMap(key, reset, lifecycle) { return resolveGlobalSingleton(key, () => /* @__PURE__ */ new Map(), typeof reset === "string" ? (value) => value.clear() : reset, typeof reset === "string" ? reset : lifecycle); } /** Resolves a lifecycle-owned process-local Set singleton. */ function resolveGlobalSet(key, lifecycle) { return resolveGlobalSingleton(key, () => /* @__PURE__ */ new Set(), (value) => value.clear(), lifecycle); } /** Resets every opt-in singleton while preserving shared object identity for the next lifecycle. */ async function drainGlobalSingletonLifecycleState(event = "close") { const resets = [...resolveGlobalSingletonResetRegistry().values()].filter(({ lifecycle }) => { if (event === "plugin-registry") return lifecycle === "plugin-registry"; if (lifecycle === "plugin-registry") return false; return event === "close" || lifecycle === "close-and-restart"; }).map(({ reset }) => { try { return Promise.resolve(reset()); } catch (error) { return Promise.reject(error instanceof Error ? error : new Error("Global singleton reset failed", { cause: error })); } }); const errors = (await Promise.allSettled(resets)).flatMap((result) => result.status === "rejected" ? [result.reason] : []); if (errors.length > 0) throw new AggregateError(errors, "Failed to reset global singleton lifecycle state"); } //#endregion export { resolveGlobalSingleton as a, resolveGlobalSet as i, readGlobalSingleton as n, resolveGlobalMap as r, drainGlobalSingletonLifecycleState as t };