openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
148 lines (147 loc) • 6.87 kB
JavaScript
import { i as resolveGlobalSingleton } from "./global-singleton-Dc_stLtU.js";
import { t as PluginLruCache } from "./plugin-cache-primitives-8mVpZ8Xc.js";
//#region src/plugins/loader-cache-state.ts
/** Cache state helper for plugin loader registries, in-flight loads, and warning suppression. */
/** Error thrown when one plugin registry cache key attempts nested loading. */
var PluginLoadReentryError = class extends Error {
constructor(cacheKey) {
super(`plugin load reentry detected for cache key: ${cacheKey}`);
this.name = "PluginLoadReentryError";
this.cacheKey = cacheKey;
}
};
/** Small registry cache with reentry detection and per-key warning memory. */
var PluginLoaderCacheState = class {
#registryCache;
#inFlightLoads = /* @__PURE__ */ new Set();
#openAllowlistWarningCache;
constructor(defaultMaxEntries) {
this.#registryCache = new PluginLruCache(defaultMaxEntries);
this.#openAllowlistWarningCache = new PluginLruCache(defaultMaxEntries);
}
clear() {
this.#registryCache.clear();
this.#inFlightLoads.clear();
this.#openAllowlistWarningCache.clear();
}
clearCachedRegistries() {
this.#registryCache.clear();
this.#openAllowlistWarningCache.clear();
}
get(cacheKey) {
return this.#registryCache.get(cacheKey);
}
set(cacheKey, state) {
this.#registryCache.set(cacheKey, state);
}
deleteValue(state) {
this.#registryCache.deleteValue(state);
}
isLoadInFlight(cacheKey) {
return this.#inFlightLoads.has(cacheKey);
}
beginLoad(cacheKey) {
if (this.#inFlightLoads.has(cacheKey)) throw new PluginLoadReentryError(cacheKey);
this.#inFlightLoads.add(cacheKey);
}
finishLoad(cacheKey) {
this.#inFlightLoads.delete(cacheKey);
}
hasOpenAllowlistWarning(cacheKey) {
return this.#openAllowlistWarningCache.get(cacheKey) === true;
}
recordOpenAllowlistWarning(cacheKey) {
this.#openAllowlistWarningCache.set(cacheKey, true);
}
};
const pluginLoaderCacheState = new PluginLoaderCacheState(128);
const { retiredRegistries, activatedRegistries, registryEpochs, recordEpochs, revokedRecordEpoch } = resolveGlobalSingleton(Symbol.for("openclaw.pluginRegistryLifecycle"), () => ({
retiredRegistries: /* @__PURE__ */ new WeakSet(),
activatedRegistries: /* @__PURE__ */ new WeakSet(),
registryEpochs: /* @__PURE__ */ new WeakMap(),
recordEpochs: /* @__PURE__ */ new WeakMap(),
revokedRecordEpoch: Object.freeze({})
}));
/** Marks a registry retired so late runtime calls can reject stale plugin state. */
function markPluginRegistryRetired(registry) {
if (registry) {
const previous = registryEpochs.get(registry);
retiredRegistries.add(registry);
registryEpochs.delete(registry);
pluginLoaderCacheState.deleteValue(registry);
previous?.controller.abort();
}
}
/** Marks a registry active and clears any previous retired state. */
function markPluginRegistryActive(registry) {
if (registry) {
const previous = registryEpochs.get(registry);
activatedRegistries.add(registry);
retiredRegistries.delete(registry);
registryEpochs.set(registry, {
epoch: Object.freeze({}),
controller: new AbortController()
});
previous?.controller.abort();
}
}
/** Capture the exact activation generation currently owned by a registry. */
function capturePluginRegistryLifecycleEpoch(registry) {
return retiredRegistries.has(registry) ? void 0 : registryEpochs.get(registry)?.epoch;
}
/** Observe an exact active epoch or explicitly scoped handle without granting activation. */
function capturePluginRegistryLifecycleSignal(registry, epoch, options) {
let current = registryEpochs.get(registry);
if (retiredRegistries.has(registry) || epoch === void 0 && options?.scopedRuntime !== true || current?.epoch !== epoch) return;
if (!current) {
current = {
epoch: void 0,
controller: new AbortController()
};
registryEpochs.set(registry, current);
}
return current.controller.signal;
}
/** True only while the exact captured registry activation remains current. */
function isPluginRegistryLifecycleEpochActive(registry, epoch) {
return !retiredRegistries.has(registry) && registryEpochs.get(registry)?.epoch === epoch;
}
/** Mint the exact record generation used by one registered native channel runtime. */
function activatePluginRecordLifecycleEpoch(registry, record) {
const registryEpoch = registryEpochs.get(registry)?.epoch;
if (!registryEpoch || retiredRegistries.has(registry)) return;
const epoch = Object.freeze({ registryEpoch });
const epochs = recordEpochs.get(registry) ?? /* @__PURE__ */ new WeakMap();
epochs.set(record, epoch);
recordEpochs.set(registry, epochs);
return epoch;
}
/** Return an epoch only while its exact registry activation and record remain current. */
function isPluginRecordLifecycleEpochActive(registry, record, epoch) {
const registryEpoch = registryEpochs.get(registry)?.epoch;
const epochRegistry = Object.getOwnPropertyDescriptor(epoch, "registryEpoch");
return registryEpoch !== void 0 && !retiredRegistries.has(registry) && epochRegistry !== void 0 && "value" in epochRegistry && epochRegistry.value === registryEpoch && recordEpochs.get(registry)?.get(record) === epoch;
}
/** Revoke one record without changing unrelated records in the same registry. */
function revokePluginRecordLifecycleEpoch(registry, record) {
const epochs = recordEpochs.get(registry) ?? /* @__PURE__ */ new WeakMap();
epochs.set(record, revokedRecordEpoch);
recordEpochs.set(registry, epochs);
}
/** True when a registry has been activated for runtime use. */
function isPluginRegistryActivated(registry) {
return activatedRegistries.has(registry);
}
/** True when a registry has been retired by a newer active registry. */
function isPluginRegistryRetired(registry) {
return retiredRegistries.has(registry);
}
/** Capture an activation; reactivating the same objects must not revive an old operation. */
function capturePluginLifecycleAuthority(registry, record, options) {
const epoch = registryEpochs.get(registry)?.epoch;
const recordEpoch = record && recordEpochs.get(registry)?.get(record);
if (!epoch && !options?.scopedRuntime || retiredRegistries.has(registry) || recordEpoch === revokedRecordEpoch) return;
return () => registryEpochs.get(registry)?.epoch === epoch && !retiredRegistries.has(registry) && (!record || registry.plugins.includes(record) && record.enabled && record.status === "loaded" && recordEpochs.get(registry)?.get(record) === recordEpoch);
}
//#endregion
export { isPluginRecordLifecycleEpochActive as a, isPluginRegistryRetired as c, pluginLoaderCacheState as d, revokePluginRecordLifecycleEpoch as f, capturePluginRegistryLifecycleSignal as i, markPluginRegistryActive as l, capturePluginLifecycleAuthority as n, isPluginRegistryActivated as o, capturePluginRegistryLifecycleEpoch as r, isPluginRegistryLifecycleEpochActive as s, activatePluginRecordLifecycleEpoch as t, markPluginRegistryRetired as u };