From 38b8058ee52c84d9efd7b8ed73f25a074b2e3969 Mon Sep 17 00:00:00 2001 From: Ryanba <92616678+Gujiassh@users.noreply.github.com> Date: Thu, 12 Mar 2026 17:22:23 +0800 Subject: [PATCH] fix(plugin-sdk): trim negotiated compatibility versions (#1305) --- Co-authored-by-agent: Sisyphus --- .../plugin-sdk/src/plugin-host/core.test.ts | 40 +++++++++++++++++++ packages/plugin-sdk/src/plugin-host/core.ts | 32 ++++++++++----- 2 files changed, 61 insertions(+), 11 deletions(-) diff --git a/packages/plugin-sdk/src/plugin-host/core.test.ts b/packages/plugin-sdk/src/plugin-host/core.test.ts index 0a765f938..62b5c1e52 100644 --- a/packages/plugin-sdk/src/plugin-host/core.test.ts +++ b/packages/plugin-sdk/src/plugin-host/core.test.ts @@ -449,6 +449,46 @@ describe('for PluginHost', () => { ])) }) + it('should trim whitespace in supported compatibility versions before negotiating', async () => { + const host = new PluginHost({ + runtime: 'electron', + transport: { kind: 'in-memory' }, + protocolVersion: 'v2', + apiVersion: 'v2', + supportedProtocolVersions: [' v1 '], + supportedApiVersions: [' v1 '], + }) + reportPluginCapability(host, { + key: providersCapability, + state: 'ready', + metadata: { source: 'test' }, + }) + + const session = await host.load(testManifest, { cwd: '' }) + const compatibilityEvents: Array<{ body?: Record }> = [] + session.channels.host.on(moduleCompatibilityResult, (payload) => { + compatibilityEvents.push(payload as unknown as { body?: Record }) + }) + + const initialized = await host.init(session.id, { + compatibility: { + supportedProtocolVersions: [' v1 '], + supportedApiVersions: [' v1 '], + }, + }) + + expect(initialized.phase).toBe('ready') + expect(compatibilityEvents).toEqual(expect.arrayContaining([ + expect.objectContaining({ + body: expect.objectContaining({ + protocolVersion: 'v1', + apiVersion: 'v1', + mode: 'downgraded', + }), + }), + ])) + }) + it('should reject initialization when compatibility has no overlap', async () => { const host = new PluginHost({ runtime: 'electron', diff --git a/packages/plugin-sdk/src/plugin-host/core.ts b/packages/plugin-sdk/src/plugin-host/core.ts index b78cf7e98..15d2167eb 100644 --- a/packages/plugin-sdk/src/plugin-host/core.ts +++ b/packages/plugin-sdk/src/plugin-host/core.ts @@ -350,35 +350,45 @@ function createModuleIdentity(name: string, index: number): ModuleIdentity { } // TODO: Maybe support more complex version formats. +function normalizeVersionList(versions: string[]) { + return [...new Set(versions.map(version => version.trim()).filter(Boolean))] +} + function resolveSupportedVersions(preferredVersion: string, supportedVersions?: string[]) { - const list = [preferredVersion, ...(supportedVersions ?? [])] - return [...new Set(list)] + return normalizeVersionList([preferredVersion, ...(supportedVersions ?? [])]) } function resolveNegotiatedVersion(preferredVersion: string, hostSupportedVersions: string[], peerSupportedVersions?: string[]) { - if (!peerSupportedVersions?.length) { - if (hostSupportedVersions.includes(preferredVersion)) { + const normalizedPreferredVersion = preferredVersion.trim() + const normalizedHostSupportedVersions = normalizeVersionList(hostSupportedVersions) + const normalizedPeerSupportedVersions = peerSupportedVersions && peerSupportedVersions.length > 0 + ? normalizeVersionList(peerSupportedVersions) + : undefined + + if (!normalizedPeerSupportedVersions?.length) { + if (normalizedHostSupportedVersions.includes(normalizedPreferredVersion)) { return { - acceptedVersion: preferredVersion, + acceptedVersion: normalizedPreferredVersion, exact: true, } } return { exact: false, - reason: `Host does not support preferred version "${preferredVersion}".`, + reason: `Host does not support preferred version "${normalizedPreferredVersion}".`, } } - if (peerSupportedVersions.includes(preferredVersion) && hostSupportedVersions.includes(preferredVersion)) { + if (normalizedPeerSupportedVersions.includes(normalizedPreferredVersion) + && normalizedHostSupportedVersions.includes(normalizedPreferredVersion)) { return { - acceptedVersion: preferredVersion, + acceptedVersion: normalizedPreferredVersion, exact: true, } } - for (const version of hostSupportedVersions) { - if (peerSupportedVersions.includes(version)) { + for (const version of normalizedHostSupportedVersions) { + if (normalizedPeerSupportedVersions.includes(version)) { return { acceptedVersion: version, exact: false, @@ -388,7 +398,7 @@ function resolveNegotiatedVersion(preferredVersion: string, hostSupportedVersion return { exact: false, - reason: `No overlapping supported versions. host=[${hostSupportedVersions.join(', ')}]; peer=[${peerSupportedVersions.join(', ')}].`, + reason: `No overlapping supported versions. host=[${normalizedHostSupportedVersions.join(', ')}]; peer=[${normalizedPeerSupportedVersions.join(', ')}].`, } }