news 2026/9/12 8:21:21

Composio Node.js ESM 兼容性测试解析:@composio/core 在 ES Module 环境下的导入可用性验证

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Composio Node.js ESM 兼容性测试解析:@composio/core 在 ES Module 环境下的导入可用性验证

Composio Node.js ESM 兼容性测试解析:@composio/core 在 ES Module 环境下的导入可用性验证

【免费下载链接】composioComposio powers 1000+ toolkits, tool search, context management, authentication, and a sandboxed workbench to help you build AI agents that turn intent into action.项目地址: https://gitcode.com/GitHub_Trending/co/composio

@composio/core是 Composio 的 TypeScript 核心 SDK,承载Composio客户端类、OpenAIProvider提供器、AuthScheme认证辅助与各类工具 Schema 转换工具。本文以仓库中的 ts/e2e-tests/runtimes/node/esm-basic/README.md 为主体,结合 e2e.test.ts、fixtures/test.mjs 以及 @composio/core 的发布配置 与 核心导出入口,完整解析这套端到端(E2E)兼容性测试的设计动机、测试矩阵、fixture 实现与底层原理。读完本文,你将理解 ESM 双形态包(Dual Package)的导入陷阱,掌握如何验证 SDK 的「动态导入、具名导入、类实例化、枚举与工具函数导出」是否在多个 Node.js 大版本下稳定可用,并能在自己的项目里复刻这套 Docker 隔离的测试模式。

为什么需要 ESM 兼容测试:从 CommonJS 到 ES Module 的模块标准迁移

ESM(ECMAScript Module)是现代 JavaScript 的官方模块标准,通过import/export语法提供静态分析友好的模块图。Node.js 自 12 版本起逐步完善 ESM 支持,但生态中仍有大量以 CommonJS(require)形态发布的包。对 SDK 类库而言,模块形态直接决定用户能否以如下方式消费:

  • import('@composio/core')动态导入是否抛错;
  • import { Composio } from '@composio/core'具名导入是否可用;
  • 包的 ESM-only 入口点(entrypoint)能否被模块解析器正确命中。

这正是本测试套件存在的原因。从 package.json 可以看到,测试包自身声明了"type": "module",其脚本为"test:e2e": "bun test e2e.test.ts",即通过 Bun 测试运行时驱动 Docker 容器执行 ESM 脚本。整个套件要回答的问题非常聚焦:在纯 ESM 环境下,@composio/core是否「开箱即用」

测试套件结构:一套「声明 + 执行 + 隔离」的三层设计

该测试目录虽小,却完整覆盖了「测试声明、fixture 执行、运行时隔离」三个层次:

ts/e2e-tests/runtimes/node/esm-basic/ ├── README.md # 套件说明(本文主体文档) ├── package.json # @e2e-tests/node-esm-basic 包声明,type: module ├── tsconfig.json # 仅编译 e2e.test.ts,moduleResolution: bundler ├── e2e.test.ts # bun:test 测试声明,定义断言与版本矩阵 └── fixtures/ └── test.mjs # 独立 .mjs 脚本,真正在容器内执行 ESM 导入验证

其中 e2e.test.ts 通过@e2e-tests/utils提供的e2e(import.meta.url, {...})入口声明测试;fixtures/test.mjs 是独立于测试框架的裸 Node 脚本,它不依赖 bun:test,只依赖 Node 自身的assert模块——这保证被验证的是「用户视角的导入行为」而非测试框架的兼容性。

从更宏观的视角看,这个套件只是 Composio 庞大运行时矩阵中的一环。在 ts/e2e-tests/README.md 的目录结构中,runtimes/node/下并列着cjs-basic(Node.js 22 的require(esm)互操作测试)、esm-basictypescript-mjs-import-nodenext(TypeScriptmoduleResolution: nodenext测试)等;runtimes/deno/esm-basic则通过npm:specifier 验证 Deno 环境下的 ESM 导入。可见 ESM 兼容并非孤立的单个测试,而是覆盖 Node.js、Deno、Cloudflare Workers 全运行时的系统性工程。

测试矩阵:三个 Node.js 大版本 + Docker 隔离

README 明确标注了隔离工具与版本:

  • 隔离工具:Docker;
  • Node.js 版本矩阵:22.22.3、24.17.0、25.9.0。

这三个版本并非随手挑选。toolchain-versions.json 中node数组正是["22.22.3", "24.17.0", "25.9.0"],而 ts/e2e-tests/_utils/src/const.ts 将其导入为WELL_KNOWN_NODE_VERSIONS,同时附带'current'(指 mise.toml 中锁定的当前 Node 版本)。因此该矩阵同时覆盖:

  • 最低支持版本(@composio/coreengines.node声明为>=22.22.3,见 ts/packages/core/package.json);
  • 最新 LTS 之后的偶数版本;
  • 尚在快速迭代的最新奇数版本。

三个大版本同时通过,说明 ESM 行为与 Node 版本无关,属于包本身的稳定能力。执行时,e2e.ts 会从调用者的import.meta.url自动推断工作目录与套件名,runner.ts 则负责为每个版本预构建 Docker 镜像、创建卷、运行容器并收集 stdout/stderr。测试还通过WELL_KNOWN_ENV_VARSANTHROPIC_API_KEYCOMPOSIO_API_KEYCOMPOSIO_BASE_URLOPENAI_API_KEY)向容器透传环境变量,但本套件并不需要 API Key——它验证的是模块解析与导出形态,而非网络行为。

十项测试:逐一印证 @composio/core 的公开导出契约

fixtures/test.mjs 是套件的执行核心。它以console.log('🧪 Testing ESM compatibility for @composio/core...')开头,依次执行十项断言,任何一项失败都会process.exit(1),全部通过则打印🎉 All ESM compatibility tests passed!并以process.exit(0)结束。下表汇总了 README 中定义的测试清单及 fixture 中的实际断言:

测试验证内容fixture 中的断言方式
动态导入import('@composio/core')不抛错try/catch 包裹await import(...),失败即退出
Composio 类主类被导出且可构造assert.ok(module.Composio)typeof === 'function'
OpenAIProvider提供器类被导出且可实例化检查导出类型并new OpenAIProvider()
AuthScheme认证枚举/辅助类可访问assert.ok(module.AuthScheme)
ComposioError错误类被导出assert.ok(module.ComposioError)
jsonSchemaToZodSchema工具函数被导出typeof === 'function'
constants常量命名空间可访问assert.ok(module.constants)
loggerLogger 实例被导出assert.ok(module.logger)
具名导入解构导入可用const { Composio, OpenAIProvider } = await import(...)
完成标志全部通过打印完成横幅并以 0 退出

每一项都对应 ts/packages/core/src/index.ts 中的真实导出语句:

  • export { Composio } from './composio'——主类定义于 composio.ts;
  • export { OpenAIProvider } from './provider/OpenAIProvider'——定义于 OpenAIProvider.ts,继承自BaseNonAgenticProvider,是随 SDK 内置、无需单独安装的默认提供器;
  • export { AuthScheme } from './models/AuthScheme'——AuthScheme.ts 中是一个携带OAuth2OAuth1API_KEYBASICBEARER_TOKENGOOGLE_SERVICE_ACCOUNT等静态工厂方法的类(README 中称其为 "Auth enum",从源码结构看实际是静态方法集合类);
  • ComposioError类定义于 errors/ComposioError.ts,其下派生出ComposioAuthConfigNotFoundErrorComposioNoAPIKeyErrorComposioFileUploadError等完整错误家族;
  • jsonSchemaToZodSchema等函数从 utils/jsonSchema.ts 批量导出;
  • export * as constants from './utils/constants'export { default as logger } from './utils/logger'分别支撑第 8、9 项测试。

值得注意的是,fixture 对每个导出的检查都是类型级的:类必须满足typeof === 'function'(可new),工具函数必须满足typeof === 'function'。这说明测试的目标不仅是「符号存在」,更是「符号形态正确」——一个被错误包装成对象的类会在new时报错,一个被错误打包成 class 的函数会在调用时行为异常。

实例化测试的特殊价值:不依赖 API Key 的构造验证

第 4 项测试专门尝试new composioModule.OpenAIProvider()并断言实例存在。这在模块兼容测试中很有讲究:构造提供器实例会触发类的初始化路径(继承链、字段初始化、可能的模块级副作用),如果 ESM 转换过程中存在循环依赖(circular dependency)或初始化顺序错误,这一项会第一时间暴露。而 OpenAIProvider.ts 的构造函数为空实现(仅super()),因此实例化不需要任何凭据或网络,适合在无密钥的 CI 容器中执行。

发布形态与条件导出:ESM 兼容的底层支撑

ESM 测试能稳定通过,根因在于@composio/core的发布配置本身就是「ESM-first」的。ts/packages/core/package.json 关键字段如下:

{ "name": "@composio/core", "type": "module", "engines": { "node": ">=22.22.3" }, "main": "dist/index.mjs", "types": "dist/index.d.mts", "exports": { ".": { "types": "./dist/index.d.mts", "default": "./dist/index.mjs" }, "./experimental": { "types": "./dist/experimental/index.d.mts", "default": "./dist/experimental/index.mjs" } } }

其中每一项都直接服务于 ESM 消费场景:

  • "type": "module"声明包内.js文件按 ESM 解析;
  • main指向.mjs后缀产物(dist/index.mjs),types指向配套的.d.mts声明文件,保证类型与运行时形态一致;
  • exports字段采用条件导出(conditional exports),按"types""default"顺序解析。测试所验证的import('@composio/core')正是命中"."条件的default: ./dist/index.mjs分支;
  • 包内还针对workerd/edge-light/node等平台提供平台差异化条件导出(如./platform/node.mjs./models/Files.node.mjs),说明这套 ESM 导出是面向多运行时统一设计的。

正是因为发布产物以.mjs形态存在、且类型声明以.d.mts配套,动态导入与具名导入才能「既过运行时、又过类型检查」。而tsconfig.jsonmoduleResolution: "bundler"module: "esnext"的组合,则保证测试源码的导入解析方式贴近现代打包器与 Node 原生 ESM 的行为。

如何运行:从单套件到全量矩阵

README 给出的运行入口是:

pnpm test:e2e

在仓库根目录 package.json 中,这条命令被定义为turbo test:e2e --filter='@e2e-tests/*',即通过 Turborepo 并行调度所有@e2e-tests/*包的test:e2e脚本。若只想跑 Node.js 运行时矩阵,可用:

pnpm test:e2e:node

其定义为turbo test:e2e:node --filter='@e2e-tests/node-*',恰好匹配本套件的包名@e2e-tests/node-esm-basic。如需锁定特定 Node 版本,可按 ts/e2e-tests/README.md 的说明覆盖环境变量:

COMPOSIO_E2E_NODE_VERSION=22.22.3 pnpm test:e2e:node

默认情况下 Node 版本取自 mise.toml 中锁定的current版本。每次执行时,runner 会先beforeAll调用runFixture({ filename: 'test.mjs' })(超时上限为TIMEOUTS.FIXTURE,即 120 秒,见 const.ts),随后以约 5 秒的默认超时逐项断言输出中是否包含各条通过标记。

运行成功时的判定链是:fixture 退出码为 0 → stdout 含 10 条「Test N passed」→ 含最终「All ESM compatibility tests passed!」横幅。这保证了断言与 fixture 内部逻辑一一对应,任何一处回归都会在具体测试项上定位。

复刻模式:如何为自己的 SDK 编写 ESM 兼容测试

从本套件可以提炼出一套可移植的 ESM 兼容验证模式:

  1. fixture 与框架解耦:用纯 Node 的.mjs脚本(仅依赖node:assert)承载真正的导入验证,测试框架只负责断言 fixture 的 stdout 与退出码,避免「测试框架兼容性」污染「被测包兼容性」。
  2. 覆盖动态导入与具名导入两种语法import()动态导入覆盖运行时解析路径,解构具名导入覆盖导出命名空间,两者缺一不可。
  3. 对导出符号做类型级断言:类检查typeof === 'function',函数检查typeof === 'function',命名空间只检查存在性,并额外实例化无副作用依赖的类。
  4. 多版本矩阵 + 容器隔离:覆盖最低支持版本、LTS 与最新版三个梯度,用 Docker 消除宿主 Node 版本干扰。本仓库用versions: { node: [...] }配置矩阵,runner.ts负责逐版本构建镜像与运行。
  5. 断言与 fixture 输出强绑定:每一项断言都对应 fixture 中一条带编号的输出,失败时可精确定位是哪个导出出了问题。

小结

ESM 兼容性不是「能不能 import」这么简单,它牵涉发布产物的模块形态、条件导出映射、类型声明配套、最低 Node 版本承诺,以及多运行时的一致性。ts/e2e-tests/runtimes/node/esm-basic 用一份 88 行的 fixture、一份 73 行的测试声明和三层目录结构,把「@composio/core在 ESM 环境下十项导出契约均可用」这件事变成了 CI 中可重复验证的硬性承诺。对于任何面向 Node.js 生态发布的 TypeScript SDK,这套「fixture 裸脚本 + Docker 版本矩阵 + stdout 断言」的组合,都是一份可以直接借鉴的工程范本。

【免费下载链接】composioComposio powers 1000+ toolkits, tool search, context management, authentication, and a sandboxed workbench to help you build AI agents that turn intent into action.项目地址: https://gitcode.com/GitHub_Trending/co/composio

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/12 8:21:19

电子产品BOM清单管理:核心要素与应用实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/12 8:20:51

蓝桥杯JAVA竞赛核心考点与高效备赛指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华