mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-12 07:20:45 +00:00
plugins: harden global hook runner state (#40184)
This commit is contained in:
49
src/plugins/hook-runner-global.test.ts
Normal file
49
src/plugins/hook-runner-global.test.ts
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||||
|
import { createMockPluginRegistry } from "./hooks.test-helpers.js";
|
||||||
|
|
||||||
|
async function importHookRunnerGlobalModule() {
|
||||||
|
return import("./hook-runner-global.js");
|
||||||
|
}
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
const mod = await importHookRunnerGlobalModule();
|
||||||
|
mod.resetGlobalHookRunner();
|
||||||
|
vi.resetModules();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("hook-runner-global", () => {
|
||||||
|
it("preserves the initialized runner across module reloads", async () => {
|
||||||
|
const modA = await importHookRunnerGlobalModule();
|
||||||
|
const registry = createMockPluginRegistry([{ hookName: "message_received", handler: vi.fn() }]);
|
||||||
|
|
||||||
|
modA.initializeGlobalHookRunner(registry);
|
||||||
|
expect(modA.getGlobalHookRunner()?.hasHooks("message_received")).toBe(true);
|
||||||
|
|
||||||
|
vi.resetModules();
|
||||||
|
|
||||||
|
const modB = await importHookRunnerGlobalModule();
|
||||||
|
expect(modB.getGlobalHookRunner()).not.toBeNull();
|
||||||
|
expect(modB.getGlobalHookRunner()?.hasHooks("message_received")).toBe(true);
|
||||||
|
expect(modB.getGlobalPluginRegistry()).toBe(registry);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("clears the shared state across module reloads", async () => {
|
||||||
|
const modA = await importHookRunnerGlobalModule();
|
||||||
|
const registry = createMockPluginRegistry([{ hookName: "message_received", handler: vi.fn() }]);
|
||||||
|
|
||||||
|
modA.initializeGlobalHookRunner(registry);
|
||||||
|
|
||||||
|
vi.resetModules();
|
||||||
|
|
||||||
|
const modB = await importHookRunnerGlobalModule();
|
||||||
|
modB.resetGlobalHookRunner();
|
||||||
|
expect(modB.getGlobalHookRunner()).toBeNull();
|
||||||
|
expect(modB.getGlobalPluginRegistry()).toBeNull();
|
||||||
|
|
||||||
|
vi.resetModules();
|
||||||
|
|
||||||
|
const modC = await importHookRunnerGlobalModule();
|
||||||
|
expect(modC.getGlobalHookRunner()).toBeNull();
|
||||||
|
expect(modC.getGlobalPluginRegistry()).toBeNull();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -12,16 +12,31 @@ import type { PluginHookGatewayContext, PluginHookGatewayStopEvent } from "./typ
|
|||||||
|
|
||||||
const log = createSubsystemLogger("plugins");
|
const log = createSubsystemLogger("plugins");
|
||||||
|
|
||||||
let globalHookRunner: HookRunner | null = null;
|
type HookRunnerGlobalState = {
|
||||||
let globalRegistry: PluginRegistry | null = null;
|
hookRunner: HookRunner | null;
|
||||||
|
registry: PluginRegistry | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const hookRunnerGlobalStateKey = Symbol.for("openclaw.plugins.hook-runner-global-state");
|
||||||
|
|
||||||
|
function getHookRunnerGlobalState(): HookRunnerGlobalState {
|
||||||
|
const globalStore = globalThis as typeof globalThis & {
|
||||||
|
[hookRunnerGlobalStateKey]?: HookRunnerGlobalState;
|
||||||
|
};
|
||||||
|
return (globalStore[hookRunnerGlobalStateKey] ??= {
|
||||||
|
hookRunner: null,
|
||||||
|
registry: null,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize the global hook runner with a plugin registry.
|
* Initialize the global hook runner with a plugin registry.
|
||||||
* Called once when plugins are loaded during gateway startup.
|
* Called once when plugins are loaded during gateway startup.
|
||||||
*/
|
*/
|
||||||
export function initializeGlobalHookRunner(registry: PluginRegistry): void {
|
export function initializeGlobalHookRunner(registry: PluginRegistry): void {
|
||||||
globalRegistry = registry;
|
const state = getHookRunnerGlobalState();
|
||||||
globalHookRunner = createHookRunner(registry, {
|
state.registry = registry;
|
||||||
|
state.hookRunner = createHookRunner(registry, {
|
||||||
logger: {
|
logger: {
|
||||||
debug: (msg) => log.debug(msg),
|
debug: (msg) => log.debug(msg),
|
||||||
warn: (msg) => log.warn(msg),
|
warn: (msg) => log.warn(msg),
|
||||||
@@ -41,7 +56,7 @@ export function initializeGlobalHookRunner(registry: PluginRegistry): void {
|
|||||||
* Returns null if plugins haven't been loaded yet.
|
* Returns null if plugins haven't been loaded yet.
|
||||||
*/
|
*/
|
||||||
export function getGlobalHookRunner(): HookRunner | null {
|
export function getGlobalHookRunner(): HookRunner | null {
|
||||||
return globalHookRunner;
|
return getHookRunnerGlobalState().hookRunner;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -49,14 +64,14 @@ export function getGlobalHookRunner(): HookRunner | null {
|
|||||||
* Returns null if plugins haven't been loaded yet.
|
* Returns null if plugins haven't been loaded yet.
|
||||||
*/
|
*/
|
||||||
export function getGlobalPluginRegistry(): PluginRegistry | null {
|
export function getGlobalPluginRegistry(): PluginRegistry | null {
|
||||||
return globalRegistry;
|
return getHookRunnerGlobalState().registry;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if any hooks are registered for a given hook name.
|
* Check if any hooks are registered for a given hook name.
|
||||||
*/
|
*/
|
||||||
export function hasGlobalHooks(hookName: Parameters<HookRunner["hasHooks"]>[0]): boolean {
|
export function hasGlobalHooks(hookName: Parameters<HookRunner["hasHooks"]>[0]): boolean {
|
||||||
return globalHookRunner?.hasHooks(hookName) ?? false;
|
return getHookRunnerGlobalState().hookRunner?.hasHooks(hookName) ?? false;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function runGlobalGatewayStopSafely(params: {
|
export async function runGlobalGatewayStopSafely(params: {
|
||||||
@@ -83,6 +98,7 @@ export async function runGlobalGatewayStopSafely(params: {
|
|||||||
* Reset the global hook runner (for testing).
|
* Reset the global hook runner (for testing).
|
||||||
*/
|
*/
|
||||||
export function resetGlobalHookRunner(): void {
|
export function resetGlobalHookRunner(): void {
|
||||||
globalHookRunner = null;
|
const state = getHookRunnerGlobalState();
|
||||||
globalRegistry = null;
|
state.hookRunner = null;
|
||||||
|
state.registry = null;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user