test(plugins): lock package boundary bridges

This commit is contained in:
Peter Steinberger
2026-04-28 06:30:41 +01:00
parent 833654586e
commit 583b419827
5 changed files with 52 additions and 26 deletions

View File

@@ -1,2 +1,2 @@
95362e415e8400ab74d81938d0c6b57ba82ba1447826fe02bd9a62de932f0b7b plugin-sdk-api-baseline.json
fefd2248a26be6f563bb777aeb9cf73ae6a479f1ed16c74be93eef7b9e35356e plugin-sdk-api-baseline.jsonl
282897f1e044b79bd444e164646e492602f987fa0fefb20a3336aa0b2445e6b7 plugin-sdk-api-baseline.json
49cc5b3330e19a86a4884c71d08070034a39463d7e25ab8eca124f65028f1114 plugin-sdk-api-baseline.jsonl

View File

@@ -1,6 +1,3 @@
import { normalizeOptionalString } from "../../../src/shared/string-coerce.js";
import { isRecord } from "../../../src/utils.js";
export type JsonObject = Record<string, unknown>;
export type ExternalPluginCompatibility = {
@@ -25,6 +22,18 @@ export const EXTERNAL_CODE_PLUGIN_REQUIRED_FIELD_PATHS = [
"openclaw.build.openclawVersion",
] as const;
function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === "object" && value !== null && !Array.isArray(value);
}
function normalizeOptionalString(value: unknown): string | undefined {
if (typeof value !== "string") {
return undefined;
}
const trimmed = value.trim();
return trimmed ? trimmed : undefined;
}
function readOpenClawBlock(packageJson: unknown) {
const root = isRecord(packageJson) ? packageJson : undefined;
const openclaw = isRecord(root?.openclaw) ? root.openclaw : undefined;

View File

@@ -1,13 +1 @@
export { collectProviderDangerousNameMatchingScopes } from "../../../src/config/dangerous-name-matching.js";
export {
asObjectRecord,
hasLegacyAccountStreamingAliases,
hasLegacyStreamingAliases,
normalizeLegacyDmAliases,
normalizeLegacyStreamingAliases,
} from "../../../src/config/channel-compat-normalization.js";
export {
detectPluginInstallPathIssue,
formatPluginInstallPathIssue,
} from "../../../src/infra/plugin-install-path-warnings.js";
export { removePluginFromConfig } from "../../../src/plugins/uninstall.js";
export * from "../../../src/plugin-sdk/runtime-doctor.js";

View File

@@ -1,6 +1 @@
export {
normalizeLowercaseStringOrEmpty,
normalizeOptionalLowercaseString,
normalizeOptionalString,
readStringValue,
} from "../../../src/shared/string-coerce.js";
export * from "../../../src/plugin-sdk/text-runtime.js";

View File

@@ -1,5 +1,5 @@
import { existsSync, readFileSync } from "node:fs";
import { resolve } from "node:path";
import { existsSync, readdirSync, readFileSync } from "node:fs";
import { relative, resolve } from "node:path";
import { describe, expect, it } from "vitest";
import {
collectExtensionsWithTsconfig,
@@ -55,12 +55,38 @@ const MEMORY_HOST_SDK_EXPORTS = [
"./secret",
"./status",
] as const;
const MEMORY_HOST_SDK_ALLOWED_CORE_BRIDGE_FILES = [
"packages/memory-host-sdk/src/host/openclaw-runtime.ts",
] as const;
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Test helper lets assertions ascribe JSON file shape.
function readJsonFile<T>(relativePath: string): T {
return JSON.parse(readFileSync(resolve(REPO_ROOT, relativePath), "utf8")) as T;
}
function collectCodeFiles(relativeDir: string): string[] {
const dir = resolve(REPO_ROOT, relativeDir);
const files: string[] = [];
for (const entry of readdirSync(dir, { withFileTypes: true })) {
const nextPath = resolve(dir, entry.name);
if (entry.isDirectory()) {
files.push(...collectCodeFiles(relative(REPO_ROOT, nextPath).replaceAll("\\", "/")));
continue;
}
if (entry.isFile() && /\.(?:[cm]?ts|tsx|mts|cts)$/u.test(entry.name)) {
files.push(relative(REPO_ROOT, nextPath).replaceAll("\\", "/"));
}
}
return files.toSorted();
}
function collectCoreReferenceFiles(relativeDir: string): string[] {
return collectCodeFiles(relativeDir).filter((file) => {
const source = readFileSync(resolve(REPO_ROOT, file), "utf8");
return source.includes("../../../../src/") || source.includes("../../../src/");
});
}
describe("opt-in extension package boundaries", () => {
it("keeps path aliases in a dedicated shared config", () => {
const pathsConfig = readJsonFile<TsConfigJson>(EXTENSION_PACKAGE_BOUNDARY_PATHS_CONFIG);
@@ -209,5 +235,13 @@ describe("opt-in extension package boundaries", () => {
const source = readFileSync(resolve(REPO_ROOT, "packages/memory-host-sdk", target), "utf8");
expect(source, target).not.toContain("src/memory-host-sdk/");
}
expect(collectCoreReferenceFiles("packages/memory-host-sdk/src")).toEqual([
...MEMORY_HOST_SDK_ALLOWED_CORE_BRIDGE_FILES,
]);
});
it("keeps plugin-package-contract independent from core internals", () => {
expect(collectCoreReferenceFiles("packages/plugin-package-contract/src")).toEqual([]);
});
});