From 8b50b57f27ac936eab0ee145f478a02c5e95cad7 Mon Sep 17 00:00:00 2001 From: sunlei Date: Thu, 18 Jun 2026 02:44:59 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BD=BF=E7=94=A8=E8=A7=84=E8=8C=83?= =?UTF-8?q?=E5=8C=96=E6=8F=92=E4=BB=B6=E5=85=A5=E5=8F=A3=E8=B7=AF=E5=BE=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../package/plugin-package-source.service.ts | 28 ++------ .../plugin-package-source.spec.ts | 65 ++++++++++++++++++- 2 files changed, 67 insertions(+), 26 deletions(-) diff --git a/src/modules/qqbot/plugin-platform/infrastructure/integration/package/plugin-package-source.service.ts b/src/modules/qqbot/plugin-platform/infrastructure/integration/package/plugin-package-source.service.ts index a6f2717..8aa8187 100644 --- a/src/modules/qqbot/plugin-platform/infrastructure/integration/package/plugin-package-source.service.ts +++ b/src/modules/qqbot/plugin-platform/infrastructure/integration/package/plugin-package-source.service.ts @@ -54,14 +54,13 @@ export class QqbotPluginPackageSourceService { } const manifestLike = JSON.parse(readFileSync(manifestFile, 'utf8')); - const entry = this.readManifestEntry(manifestLike); - const entryFile = this.pathPolicy.resolveEntryFile( - controlledPackageRoot, - entry, - ); const manifest = parseQqbotPluginManifest(manifestLike, { pluginRoot: controlledPackageRoot, }); + const entryFile = this.pathPolicy.resolveEntryFile( + controlledPackageRoot, + manifest.entry, + ); const pluginKey = manifest.key; return { @@ -83,23 +82,4 @@ export class QqbotPluginPackageSourceService { .filter((entry) => entry.isDirectory()) .map((entry) => join(root, entry.name)); } - - /** - * Reads the raw entry string before parser normalization so path policy errors stay explicit. - * @param manifestLike - Parsed `plugin.json` content whose entry path belongs to the package. - * @returns Raw entry path or an empty value that the manifest parser will reject. - */ - private readManifestEntry(manifestLike: unknown): string { - if (!this.isRecord(manifestLike)) return ''; - return typeof manifestLike.entry === 'string' ? manifestLike.entry : ''; - } - - /** - * Narrows JSON values to object records before reading manifest fields. - * @param value - Parsed JSON value from a QQBot plugin package manifest. - * @returns Whether the value is a non-array object record. - */ - private isRecord(value: unknown): value is Record { - return !!value && typeof value === 'object' && !Array.isArray(value); - } } diff --git a/test/modules/qqbot/plugin-platform/plugin-package-source.spec.ts b/test/modules/qqbot/plugin-platform/plugin-package-source.spec.ts index f2688f1..33452ba 100644 --- a/test/modules/qqbot/plugin-platform/plugin-package-source.spec.ts +++ b/test/modules/qqbot/plugin-platform/plugin-package-source.spec.ts @@ -1,4 +1,10 @@ -import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from 'node:fs'; +import { + mkdtempSync, + mkdirSync, + readFileSync, + rmSync, + writeFileSync, +} from 'node:fs'; import { tmpdir } from 'node:os'; import { join, sep } from 'node:path'; @@ -88,7 +94,62 @@ describe('QqbotPluginPackageSourceService', () => { ); await expect(source.discoverPackages()).rejects.toThrow( - 'Plugin entry must stay inside the package root', + 'PATH_OUTSIDE_PLUGIN_ROOT', + ); + }); + + it('resolves entry files from parser-normalized package paths', async () => { + const packageRoot = join(tempRoot, 'windows-entry'); + mkdirSync(join(packageRoot, 'src'), { recursive: true }); + writeFileSync( + join(packageRoot, 'plugin.json'), + JSON.stringify({ + key: 'windows-entry', + name: 'Windows Entry', + version: '1.0.0', + entry: 'src\\\\index.ts', + runtime: { + workerType: 'thread', + timeoutMs: 5000, + memoryMb: 128, + maxConcurrency: 1, + }, + operations: [], + }), + 'utf8', + ); + + const pathPolicy = new QqbotPluginPackagePathPolicyService([tempRoot]); + const resolveEntryFileSpy = jest.spyOn(pathPolicy, 'resolveEntryFile'); + const source = new QqbotPluginPackageSourceService(pathPolicy); + + await expect(source.discoverPackages()).resolves.toEqual([ + expect.objectContaining({ + entry: 'src/index.ts', + entryFile: join(packageRoot, 'src', 'index.ts'), + pluginKey: 'windows-entry', + }), + ]); + expect(resolveEntryFileSpy).toHaveBeenCalledWith( + packageRoot, + 'src/index.ts', + ); + }); + + it('does not keep platform-side manifest transfer shims', () => { + const source = readFileSync( + join( + process.cwd(), + 'src/modules/qqbot/plugin-platform/infrastructure/integration/package/plugin-package-source.service.ts', + ), + 'utf8', + ); + + expect(source).not.toMatch( + /normalizeManifestForCurrentParser|attachPackageKeyAlias|readPackageKey/, + ); + expect(source).not.toMatch( + /thread[\s\S]{0,80}node-worker|node-worker[\s\S]{0,80}thread/, ); }); });