diff --git a/src/plugin-sdk/root-alias.cjs b/src/plugin-sdk/root-alias.cjs index 10c6f165c42..0d0dde974ce 100644 --- a/src/plugin-sdk/root-alias.cjs +++ b/src/plugin-sdk/root-alias.cjs @@ -113,7 +113,9 @@ function listPluginSdkExportedSubpaths() { const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8")); subpaths = Object.keys(packageJson.exports ?? {}) .filter((key) => key.startsWith("./plugin-sdk/")) - .map((key) => key.slice("./plugin-sdk/".length)); + .map((key) => key.slice("./plugin-sdk/".length)) + .filter((subpath) => /^[A-Za-z0-9][A-Za-z0-9_-]*$/.test(subpath)) + .toSorted(); } catch { subpaths = []; } diff --git a/src/plugins/contracts/plugin-sdk-root-alias.test.ts b/src/plugins/contracts/plugin-sdk-root-alias.test.ts index 95d86d63a2b..56a2f5bdee3 100644 --- a/src/plugins/contracts/plugin-sdk-root-alias.test.ts +++ b/src/plugins/contracts/plugin-sdk-root-alias.test.ts @@ -26,6 +26,7 @@ function loadRootAliasWithStubs(options?: { env?: Record; monolithicExports?: Record; aliasPath?: string; + packageExports?: Record; platform?: string; }) { let createJitiCalls = 0; @@ -63,6 +64,7 @@ function loadRootAliasWithStubs(options?: { JSON.stringify({ exports: { "./plugin-sdk/group-access": { default: "./dist/plugin-sdk/group-access.js" }, + ...options?.packageExports, }, }), existsSync: (targetPath: string) => { @@ -274,6 +276,35 @@ describe("plugin-sdk root alias", () => { }); }); + it("keeps bootstrap plugin-sdk aliases deterministic and ignores unsafe subpaths", () => { + const lazyModule = loadRootAliasWithStubs({ + distExists: true, + packageExports: { + "./plugin-sdk/zeta": { default: "./dist/plugin-sdk/zeta.js" }, + "./plugin-sdk/../escape": { default: "./dist/plugin-sdk/escape.js" }, + "./plugin-sdk/alpha": { default: "./dist/plugin-sdk/alpha.js" }, + }, + monolithicExports: { + slowHelper: (): string => "loaded", + }, + }); + + expect((lazyModule.moduleExports.slowHelper as () => string)()).toBe("loaded"); + const aliasKeys = Object.keys( + (lazyModule.createJitiOptions.at(-1)?.alias ?? {}) as Record, + ); + expect(aliasKeys).toEqual([ + "openclaw/plugin-sdk", + "@openclaw/plugin-sdk", + "openclaw/plugin-sdk/alpha", + "@openclaw/plugin-sdk/alpha", + "openclaw/plugin-sdk/group-access", + "@openclaw/plugin-sdk/group-access", + "openclaw/plugin-sdk/zeta", + "@openclaw/plugin-sdk/zeta", + ]); + }); + it("prefers hashed dist diagnostic events chunks before falling back to src", () => { const packageRoot = createPackageRoot(); const distAliasPath = createDistAliasPath();