diff --git a/src/plugins/runtime.channel-pin.test.ts b/src/plugins/runtime.channel-pin.test.ts index 62f7a6f59ec..0c88a2c6a09 100644 --- a/src/plugins/runtime.channel-pin.test.ts +++ b/src/plugins/runtime.channel-pin.test.ts @@ -1,6 +1,8 @@ import { afterEach, describe, expect, it } from "vitest"; +import { getChannelPlugin } from "../channels/plugins/registry.js"; import { createEmptyPluginRegistry } from "./registry-empty.js"; import { + getActivePluginRegistryVersion, getActivePluginChannelRegistry, pinActivePluginChannelRegistry, releasePinnedPluginChannelRegistry, @@ -34,6 +36,29 @@ describe("channel registry pinning", () => { expect(getActivePluginChannelRegistry()!.channels).toHaveLength(1); }); + it("re-pin invalidates cached channel lookups", () => { + const setup = createEmptyPluginRegistry(); + const setupPlugin = { id: "slack", meta: {} } as never; + setup.channels = [{ plugin: setupPlugin }] as never; + setActivePluginRegistry(setup); + pinActivePluginChannelRegistry(setup); + + expect(getChannelPlugin("slack")).toBe(setupPlugin); + + const full = createEmptyPluginRegistry(); + const fullPlugin = { id: "slack", meta: {} } as never; + full.channels = [{ plugin: fullPlugin }] as never; + setActivePluginRegistry(full); + + expect(getChannelPlugin("slack")).toBe(setupPlugin); + + const versionBeforeRepin = getActivePluginRegistryVersion(); + pinActivePluginChannelRegistry(full); + + expect(getActivePluginRegistryVersion()).toBe(versionBeforeRepin + 1); + expect(getChannelPlugin("slack")).toBe(fullPlugin); + }); + it("updates channel registry on swap when not pinned", () => { const first = createEmptyPluginRegistry(); setActivePluginRegistry(first); diff --git a/src/plugins/runtime.ts b/src/plugins/runtime.ts index a422956d452..c345f53c40e 100644 --- a/src/plugins/runtime.ts +++ b/src/plugins/runtime.ts @@ -106,16 +106,25 @@ export function resolveActivePluginHttpRouteRegistry(fallback: PluginRegistry): * gateway startup after the initial plugin load so that config-schema reads * and other non-primary registry loads cannot evict channel plugins. */ export function pinActivePluginChannelRegistry(registry: PluginRegistry) { + if (state.channelRegistry === registry && state.channelRegistryPinned) { + return; + } state.channelRegistry = registry; state.channelRegistryPinned = true; + state.version += 1; } export function releasePinnedPluginChannelRegistry(registry?: PluginRegistry) { if (registry && state.channelRegistry !== registry) { return; } + const nextChannelRegistry = state.registry; + if (state.channelRegistry === nextChannelRegistry && !state.channelRegistryPinned) { + return; + } state.channelRegistryPinned = false; - state.channelRegistry = state.registry; + state.channelRegistry = nextChannelRegistry; + state.version += 1; } /** Return the registry that should be used for channel plugin resolution.