fix: stabilize contract loader seams

This commit is contained in:
Peter Steinberger
2026-04-06 04:39:22 +01:00
parent 4a690b452a
commit 4aeabf95cc
7 changed files with 40 additions and 85 deletions

View File

@@ -1,4 +1,3 @@
// Keep this barrel helper-only so plugin-sdk facades do not pull the full // Keep this barrel helper-only so plugin-sdk facades do not pull the full
// channel plugin (and its runtime state) into tests or other shared surfaces. // channel plugin (and its runtime state) into tests or other shared surfaces.
export { mattermostPlugin } from "./src/channel.js";
export { isMattermostSenderAllowed } from "./src/mattermost/monitor-auth.js"; export { isMattermostSenderAllowed } from "./src/mattermost/monitor-auth.js";

View File

@@ -18,7 +18,7 @@ export default defineBundledChannelEntry({
description: "Mattermost channel plugin", description: "Mattermost channel plugin",
importMetaUrl: import.meta.url, importMetaUrl: import.meta.url,
plugin: { plugin: {
specifier: "./api.js", specifier: "./src/channel.js",
exportName: "mattermostPlugin", exportName: "mattermostPlugin",
}, },
runtime: { runtime: {

View File

@@ -0,0 +1 @@
export { isMattermostSenderAllowed } from "./src/mattermost/monitor-auth.js";

View File

@@ -3,7 +3,7 @@ import { defineBundledChannelSetupEntry } from "openclaw/plugin-sdk/channel-entr
export default defineBundledChannelSetupEntry({ export default defineBundledChannelSetupEntry({
importMetaUrl: import.meta.url, importMetaUrl: import.meta.url,
plugin: { plugin: {
specifier: "./api.js", specifier: "./src/channel.js",
exportName: "mattermostPlugin", exportName: "mattermostPlugin",
}, },
}); });

View File

@@ -41,10 +41,9 @@ const sendMessageMatrixMock = vi.hoisted(() =>
roomId: to.replace(/^room:/, ""), roomId: to.replace(/^room:/, ""),
})), })),
); );
const matrixRuntimeApiModuleId = new URL( const matrixRuntimeApiModuleId = vi.hoisted(
"../../../../extensions/matrix/runtime-api.js", () => new URL("../../../../extensions/matrix/runtime-api.js", import.meta.url).href,
import.meta.url, );
).href;
const lineContractApi = await importBundledChannelContractArtifact<{ const lineContractApi = await importBundledChannelContractArtifact<{
listLineAccountIds: () => string[]; listLineAccountIds: () => string[];

View File

@@ -1,11 +1,19 @@
// Manual facade. Keep loader boundary explicit. // Manual facade. Keep loader boundary explicit.
type FacadeModule = typeof import("@openclaw/mattermost/api.js"); type MattermostSenderAllowed = (params: {
senderId: string;
senderName?: string;
allowFrom: string[];
allowNameMatching?: boolean;
}) => boolean;
type FacadeModule = {
isMattermostSenderAllowed: MattermostSenderAllowed;
};
import { loadBundledPluginPublicSurfaceModuleSync } from "./facade-runtime.js"; import { loadBundledPluginPublicSurfaceModuleSync } from "./facade-runtime.js";
function loadFacadeModule(): FacadeModule { function loadFacadeModule(): FacadeModule {
return loadBundledPluginPublicSurfaceModuleSync<FacadeModule>({ return loadBundledPluginPublicSurfaceModuleSync<FacadeModule>({
dirName: "mattermost", dirName: "mattermost",
artifactBasename: "api.js", artifactBasename: "policy-api.js",
}); });
} }
export const isMattermostSenderAllowed: FacadeModule["isMattermostSenderAllowed"] = ((...args) => export const isMattermostSenderAllowed: FacadeModule["isMattermostSenderAllowed"] = ((...args) =>

View File

@@ -1,4 +1,16 @@
import { afterEach, beforeEach, describe, expect, it } from "vitest"; import { afterEach, beforeEach, describe, expect, it } from "vitest";
import { bluebubblesPlugin } from "../../../extensions/bluebubbles/api.js";
import {
discordPlugin,
discordThreadBindingTesting,
} from "../../../extensions/discord/test-api.js";
import { feishuPlugin, feishuThreadBindingTesting } from "../../../extensions/feishu/api.js";
import { imessagePlugin } from "../../../extensions/imessage/api.js";
import { matrixPlugin, setMatrixRuntime } from "../../../extensions/matrix/test-api.js";
import {
telegramPlugin,
resetTelegramThreadBindingsForTests,
} from "../../../extensions/telegram/test-api.js";
import { getSessionBindingContractRegistry } from "../../../src/channels/plugins/contracts/registry-session-binding.js"; import { getSessionBindingContractRegistry } from "../../../src/channels/plugins/contracts/registry-session-binding.js";
import type { ChannelPlugin } from "../../../src/channels/plugins/types.js"; import type { ChannelPlugin } from "../../../src/channels/plugins/types.js";
import { import {
@@ -13,10 +25,6 @@ import {
import { resetPluginRuntimeStateForTest } from "../../../src/plugins/runtime.js"; import { resetPluginRuntimeStateForTest } from "../../../src/plugins/runtime.js";
import { setActivePluginRegistry } from "../../../src/plugins/runtime.js"; import { setActivePluginRegistry } from "../../../src/plugins/runtime.js";
import type { PluginRuntime } from "../../../src/plugins/runtime/index.js"; import type { PluginRuntime } from "../../../src/plugins/runtime/index.js";
import {
loadBundledPluginPublicSurfaceSync,
loadBundledPluginTestApiSync,
} from "../../../src/test-utils/bundled-plugin-public-surface.js";
import { createTestRegistry } from "../../../src/test-utils/channel-plugins.js"; import { createTestRegistry } from "../../../src/test-utils/channel-plugins.js";
type DiscordThreadBindingTesting = { type DiscordThreadBindingTesting = {
@@ -25,109 +33,49 @@ type DiscordThreadBindingTesting = {
type ResetTelegramThreadBindingsForTests = () => Promise<void>; type ResetTelegramThreadBindingsForTests = () => Promise<void>;
let discordThreadBindingTestingCache: DiscordThreadBindingTesting | undefined;
let resetTelegramThreadBindingsForTestsCache: ResetTelegramThreadBindingsForTests | undefined;
let feishuApiPromise: Promise<typeof import("../../../extensions/feishu/api.js")> | undefined;
let matrixApiPromise: Promise<typeof import("../../../extensions/matrix/api.js")> | undefined;
let bluebubblesPluginCache: ChannelPlugin | undefined;
let discordPluginCache: ChannelPlugin | undefined;
let feishuPluginCache: ChannelPlugin | undefined;
let imessagePluginCache: ChannelPlugin | undefined;
let matrixPluginCache: ChannelPlugin | undefined;
let setMatrixRuntimeCache: ((runtime: PluginRuntime) => void) | undefined;
let telegramPluginCache: ChannelPlugin | undefined;
function getBluebubblesPlugin(): ChannelPlugin { function getBluebubblesPlugin(): ChannelPlugin {
if (!bluebubblesPluginCache) { return bluebubblesPlugin as unknown as ChannelPlugin;
({ bluebubblesPlugin: bluebubblesPluginCache } = loadBundledPluginPublicSurfaceSync<{
bluebubblesPlugin: ChannelPlugin;
}>({ pluginId: "bluebubbles", artifactBasename: "index.js" }));
}
return bluebubblesPluginCache;
} }
function getDiscordPlugin(): ChannelPlugin { function getDiscordPlugin(): ChannelPlugin {
if (!discordPluginCache) { return discordPlugin as unknown as ChannelPlugin;
({ discordPlugin: discordPluginCache } = loadBundledPluginTestApiSync<{
discordPlugin: ChannelPlugin;
}>("discord"));
}
return discordPluginCache;
} }
function getFeishuPlugin(): ChannelPlugin { function getFeishuPlugin(): ChannelPlugin {
if (!feishuPluginCache) { return feishuPlugin as unknown as ChannelPlugin;
({ feishuPlugin: feishuPluginCache } = loadBundledPluginPublicSurfaceSync<{
feishuPlugin: ChannelPlugin;
}>({ pluginId: "feishu", artifactBasename: "api.js" }));
}
return feishuPluginCache;
} }
function getIMessagePlugin(): ChannelPlugin { function getIMessagePlugin(): ChannelPlugin {
if (!imessagePluginCache) { return imessagePlugin as unknown as ChannelPlugin;
({ imessagePlugin: imessagePluginCache } = loadBundledPluginPublicSurfaceSync<{
imessagePlugin: ChannelPlugin;
}>({ pluginId: "imessage", artifactBasename: "api.js" }));
}
return imessagePluginCache;
} }
function getMatrixPlugin(): ChannelPlugin { function getMatrixPlugin(): ChannelPlugin {
if (!matrixPluginCache) { return matrixPlugin as unknown as ChannelPlugin;
({ matrixPlugin: matrixPluginCache, setMatrixRuntime: setMatrixRuntimeCache } =
loadBundledPluginTestApiSync<{
matrixPlugin: ChannelPlugin;
setMatrixRuntime: (runtime: PluginRuntime) => void;
}>("matrix"));
}
return matrixPluginCache;
} }
function getSetMatrixRuntime(): (runtime: PluginRuntime) => void { function getSetMatrixRuntime(): (runtime: PluginRuntime) => void {
if (!setMatrixRuntimeCache) { return setMatrixRuntime;
void getMatrixPlugin();
}
return setMatrixRuntimeCache!;
} }
function getTelegramPlugin(): ChannelPlugin { function getTelegramPlugin(): ChannelPlugin {
if (!telegramPluginCache) { return telegramPlugin as unknown as ChannelPlugin;
({ telegramPlugin: telegramPluginCache } = loadBundledPluginTestApiSync<{
telegramPlugin: ChannelPlugin;
}>("telegram"));
}
return telegramPluginCache;
} }
function getDiscordThreadBindingTesting(): DiscordThreadBindingTesting { function getDiscordThreadBindingTesting(): DiscordThreadBindingTesting {
if (!discordThreadBindingTestingCache) { return discordThreadBindingTesting;
({ discordThreadBindingTesting: discordThreadBindingTestingCache } =
loadBundledPluginTestApiSync<{
discordThreadBindingTesting: DiscordThreadBindingTesting;
}>("discord"));
}
return discordThreadBindingTestingCache;
} }
function getResetTelegramThreadBindingsForTests(): ResetTelegramThreadBindingsForTests { function getResetTelegramThreadBindingsForTests(): ResetTelegramThreadBindingsForTests {
if (!resetTelegramThreadBindingsForTestsCache) { return resetTelegramThreadBindingsForTests;
({ resetTelegramThreadBindingsForTests: resetTelegramThreadBindingsForTestsCache } =
loadBundledPluginTestApiSync<{
resetTelegramThreadBindingsForTests: ResetTelegramThreadBindingsForTests;
}>("telegram"));
}
return resetTelegramThreadBindingsForTestsCache;
} }
async function getFeishuThreadBindingTesting() { async function getFeishuThreadBindingTesting() {
feishuApiPromise ??= import("../../../extensions/feishu/api.js"); return feishuThreadBindingTesting;
return (await feishuApiPromise).feishuThreadBindingTesting;
} }
async function getResetMatrixThreadBindingsForTests() { async function getResetMatrixThreadBindingsForTests() {
matrixApiPromise ??= import("../../../extensions/matrix/api.js"); const matrixApi = await import("../../../extensions/matrix/api.js");
return (await matrixApiPromise).resetMatrixThreadBindingsForTests; return matrixApi.resetMatrixThreadBindingsForTests;
} }
function resolveSessionBindingContractRuntimeConfig(id: string) { function resolveSessionBindingContractRuntimeConfig(id: string) {