diff --git a/src/plugins/bundled-capability-metadata.test.ts b/src/plugins/bundled-capability-metadata.test.ts index 32104012ae8..683e471259f 100644 --- a/src/plugins/bundled-capability-metadata.test.ts +++ b/src/plugins/bundled-capability-metadata.test.ts @@ -5,41 +5,48 @@ import { BUNDLED_LEGACY_PLUGIN_ID_ALIASES, BUNDLED_PLUGIN_CONTRACT_SNAPSHOTS, } from "./contracts/inventory/bundled-capability-metadata.js"; - -function uniqueStrings(values: readonly string[] | undefined): string[] { - const result: string[] = []; - const seen = new Set(); - for (const value of values ?? []) { - const normalized = value.trim(); - if (!normalized || seen.has(normalized)) { - continue; - } - seen.add(normalized); - result.push(normalized); - } - return result; -} +import { uniqueStrings } from "./contracts/shared.js"; describe("bundled capability metadata", () => { it("keeps contract snapshots aligned with bundled plugin manifests", () => { const expected = listBundledPluginMetadata() .map(({ manifest }) => ({ pluginId: manifest.id, - providerIds: uniqueStrings(manifest.providers), - speechProviderIds: uniqueStrings(manifest.contracts?.speechProviders), + providerIds: uniqueStrings(manifest.providers, (value) => value.trim()), + speechProviderIds: uniqueStrings(manifest.contracts?.speechProviders, (value) => + value.trim(), + ), realtimeTranscriptionProviderIds: uniqueStrings( manifest.contracts?.realtimeTranscriptionProviders, + (value) => value.trim(), + ), + realtimeVoiceProviderIds: uniqueStrings( + manifest.contracts?.realtimeVoiceProviders, + (value) => value.trim(), ), - realtimeVoiceProviderIds: uniqueStrings(manifest.contracts?.realtimeVoiceProviders), mediaUnderstandingProviderIds: uniqueStrings( manifest.contracts?.mediaUnderstandingProviders, + (value) => value.trim(), ), - imageGenerationProviderIds: uniqueStrings(manifest.contracts?.imageGenerationProviders), - videoGenerationProviderIds: uniqueStrings(manifest.contracts?.videoGenerationProviders), - musicGenerationProviderIds: uniqueStrings(manifest.contracts?.musicGenerationProviders), - webFetchProviderIds: uniqueStrings(manifest.contracts?.webFetchProviders), - webSearchProviderIds: uniqueStrings(manifest.contracts?.webSearchProviders), - toolNames: uniqueStrings(manifest.contracts?.tools), + imageGenerationProviderIds: uniqueStrings( + manifest.contracts?.imageGenerationProviders, + (value) => value.trim(), + ), + videoGenerationProviderIds: uniqueStrings( + manifest.contracts?.videoGenerationProviders, + (value) => value.trim(), + ), + musicGenerationProviderIds: uniqueStrings( + manifest.contracts?.musicGenerationProviders, + (value) => value.trim(), + ), + webFetchProviderIds: uniqueStrings(manifest.contracts?.webFetchProviders, (value) => + value.trim(), + ), + webSearchProviderIds: uniqueStrings(manifest.contracts?.webSearchProviders, (value) => + value.trim(), + ), + toolNames: uniqueStrings(manifest.contracts?.tools, (value) => value.trim()), })) .filter( (entry) => diff --git a/src/plugins/contracts/inventory/bundled-capability-metadata.ts b/src/plugins/contracts/inventory/bundled-capability-metadata.ts index fc666a0b3b7..ca8dd9270cf 100644 --- a/src/plugins/contracts/inventory/bundled-capability-metadata.ts +++ b/src/plugins/contracts/inventory/bundled-capability-metadata.ts @@ -1,4 +1,5 @@ import { listBundledPluginMetadata } from "../../bundled-plugin-metadata.js"; +import { uniqueStrings } from "../shared.js"; // Build/test inventory only. // Runtime code should prefer manifest/runtime registry queries instead of these snapshots. @@ -19,20 +20,6 @@ export type BundledPluginContractSnapshot = { toolNames: string[]; }; -function uniqueStrings(values: readonly string[] | undefined): string[] { - const result: string[] = []; - const seen = new Set(); - for (const value of values ?? []) { - const normalized = value.trim(); - if (!normalized || seen.has(normalized)) { - continue; - } - seen.add(normalized); - result.push(normalized); - } - return result; -} - const BUNDLED_PLUGIN_METADATA_FOR_CAPABILITIES = listBundledPluginMetadata({ includeChannelConfigs: false, includeSyntheticChannelConfigs: false, @@ -41,20 +28,39 @@ const BUNDLED_PLUGIN_METADATA_FOR_CAPABILITIES = listBundledPluginMetadata({ export const BUNDLED_PLUGIN_CONTRACT_SNAPSHOTS: readonly BundledPluginContractSnapshot[] = BUNDLED_PLUGIN_METADATA_FOR_CAPABILITIES.map(({ manifest }) => ({ pluginId: manifest.id, - cliBackendIds: uniqueStrings(manifest.cliBackends), - providerIds: uniqueStrings(manifest.providers), - speechProviderIds: uniqueStrings(manifest.contracts?.speechProviders), + cliBackendIds: uniqueStrings(manifest.cliBackends, (value) => value.trim()), + providerIds: uniqueStrings(manifest.providers, (value) => value.trim()), + speechProviderIds: uniqueStrings(manifest.contracts?.speechProviders, (value) => value.trim()), realtimeTranscriptionProviderIds: uniqueStrings( manifest.contracts?.realtimeTranscriptionProviders, + (value) => value.trim(), ), - realtimeVoiceProviderIds: uniqueStrings(manifest.contracts?.realtimeVoiceProviders), - mediaUnderstandingProviderIds: uniqueStrings(manifest.contracts?.mediaUnderstandingProviders), - imageGenerationProviderIds: uniqueStrings(manifest.contracts?.imageGenerationProviders), - videoGenerationProviderIds: uniqueStrings(manifest.contracts?.videoGenerationProviders), - musicGenerationProviderIds: uniqueStrings(manifest.contracts?.musicGenerationProviders), - webFetchProviderIds: uniqueStrings(manifest.contracts?.webFetchProviders), - webSearchProviderIds: uniqueStrings(manifest.contracts?.webSearchProviders), - toolNames: uniqueStrings(manifest.contracts?.tools), + realtimeVoiceProviderIds: uniqueStrings(manifest.contracts?.realtimeVoiceProviders, (value) => + value.trim(), + ), + mediaUnderstandingProviderIds: uniqueStrings( + manifest.contracts?.mediaUnderstandingProviders, + (value) => value.trim(), + ), + imageGenerationProviderIds: uniqueStrings( + manifest.contracts?.imageGenerationProviders, + (value) => value.trim(), + ), + videoGenerationProviderIds: uniqueStrings( + manifest.contracts?.videoGenerationProviders, + (value) => value.trim(), + ), + musicGenerationProviderIds: uniqueStrings( + manifest.contracts?.musicGenerationProviders, + (value) => value.trim(), + ), + webFetchProviderIds: uniqueStrings(manifest.contracts?.webFetchProviders, (value) => + value.trim(), + ), + webSearchProviderIds: uniqueStrings(manifest.contracts?.webSearchProviders, (value) => + value.trim(), + ), + toolNames: uniqueStrings(manifest.contracts?.tools, (value) => value.trim()), })) .filter( (entry) => diff --git a/src/plugins/contracts/registry.ts b/src/plugins/contracts/registry.ts index b613a5465e7..323af38a82c 100644 --- a/src/plugins/contracts/registry.ts +++ b/src/plugins/contracts/registry.ts @@ -16,6 +16,7 @@ import type { WebSearchProviderPlugin, } from "../types.js"; import { BUNDLED_PLUGIN_CONTRACT_SNAPSHOTS } from "./inventory/bundled-capability-metadata.js"; +import { uniqueStrings } from "./shared.js"; import { loadVitestImageGenerationProviderContractRegistry, loadVitestMediaUnderstandingProviderContractRegistry, @@ -79,19 +80,6 @@ type ManifestContractKey = type ManifestRegistryContractKey = "webFetchProviders" | "webSearchProviders"; -function uniqueStrings(values: readonly string[]): string[] { - const result: string[] = []; - const seen = new Set(); - for (const value of values) { - if (seen.has(value)) { - continue; - } - seen.add(value); - result.push(value); - } - return result; -} - function resolveBundledManifestContracts(): PluginRegistrationContractEntry[] { if (process.env.VITEST) { return BUNDLED_PLUGIN_CONTRACT_SNAPSHOTS.map((entry) => ({ diff --git a/src/plugins/contracts/shared.ts b/src/plugins/contracts/shared.ts new file mode 100644 index 00000000000..46572fdeb34 --- /dev/null +++ b/src/plugins/contracts/shared.ts @@ -0,0 +1,16 @@ +export function uniqueStrings( + values: readonly string[] | undefined, + normalize: (value: string) => string = (value) => value, +): string[] { + const result: string[] = []; + const seen = new Set(); + for (const value of values ?? []) { + const normalized = normalize(value); + if (!normalized || seen.has(normalized)) { + continue; + } + seen.add(normalized); + result.push(normalized); + } + return result; +}