尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Medusa HTTP 类型生成器实战指南:从 Zod 校验 Schema 自动生成与校验 TypeScript 类型

Medusa HTTP 类型生成器实战指南:从 Zod 校验 Schema 自动生成与校验 TypeScript 类型 Medusa HTTP 类型生成器实战指南从 Zod 校验 Schema 自动生成与校验 TypeScript 类型【免费下载链接】medusaThe worlds most flexible commerce platform for agents and developers项目地址: https://gitcode.com/GitHub_Trending/me/medusa导读本指南围绕 Medusa 开源仓库中的medusajs/http-types-generatorCLI 工具展开讲解如何将 API 路由层基于 Zod 编写的 validator schema 自动转换成 TypeScriptinterface声明HTTP 类型文件并通过结构兼容性校验确保手工维护或自动生成的类型始终与校验逻辑保持一致。读完本文你将掌握该工具在 Medusa monorepo 中的两条工作流命令generate与validate、全部 CLI 选项、http-types.config.json配置文件的每个字段以及它背后的源码级实现原理Schema 提取、类型解析、接口发射与文件合并可直接在本地复现并扩展使用。工具定位连接 Zod 校验层与 HTTP 类型层在 Medusa 2.x 的架构中HTTP API 层的数据校验由 packages/medusa/src/api 下的validators.ts文件完成——每个路由目录domain对应一个 validator 文件其中用 Zod 声明了请求参数、查询参数、请求体的 Schema。与此同时供 SDK 与内部代码消费的公共 HTTP 类型则集中在 packages/core/types/src/http。这两层之间存在明显的单点事实来源问题校验 Schema 与公开类型若由人手工同步极易出现漂移schema 改了、类型没改或反之。http-types-generator正是为此而生CLI tool that generates and validates TypeScript HTTP types from Zod validator schemas.它的工作方式见 packages/cli/http-types-generator/README.md可以概括为一条流水线扫描validator 文件按配置的 glob 模式提取文件内导出的 Zod schema解析这些 schema 对应的 TypeScript 类型_input/_output发射emit为interface声明写入输出文件可选地校验已有 HTTP 类型文件与对应 Zod schema 是否结构兼容。该工具以medusa-http-types为 bin 名对外暴露见 package.json 中的bin字段入口位于 src/index.ts内部使用commander注册generate与validate两个子命令。在 Medusa monorepo 中使用仓库根目录的 package.json 已注册两个 workspace 脚本分别桥接到medusajs/http-types-generator包的generate:http-types与validate:http-types。在仓库根目录直接运行即可# 为某个 domain 生成类型先 dry-run 预览 yarn generate:http-types --domain products --dry-run yarn generate:http-types --domain products # 校验全部类型 yarn validate:http-types # 校验单个 domain并输出详细信息 yarn validate:http-types --domain products --verbose其中--domain的值对应路由目录名route directory name例如products。根目录的 http-types.config.json 已为 Medusa 定制好全部路径无需额外配置{ outputBase: packages/core/types/src/http, tsconfig: _tsconfig.base.json, importSources: { commonRequest: packages/core/types/src/http/common, dal: packages/core/types/src/dal }, validatorGlobs: { admin: packages/medusa/src/api/admin/*/validators.ts, store: packages/medusa/src/api/store/*/validators.ts }, validatorPathPattern: /api/(admin|store)/([^/])/validators\\.ts$ }注意这里与 README 中的通用默认值的区别Medusa 的outputBase指向 monorepo 内的packages/core/types/src/http而importSources指向仓库内相对路径工具会将其转换为相对 import 路径validatorPathPattern的两个捕获组分别为(admin|store)与路由目录。generate命令从 Zod Schema 生成接口命令签名yarn generate:http-types [options]源码定义见 src/commands/generate.ts支持的选项如下Option描述默认值--area area要处理的 API 区域必须匹配validatorGlobs中的某个 keymonorepo 中为store或admin或传all处理所有区域all--domain domain只处理指定 domain路由目录名—--dry-run仅打印将要生成的内容不写文件false--force覆盖已有文件默认是合并而不是覆盖false--verbose打印每个被处理的 schemafalse底层执行流程对照源码generate的实际执行路径runGeneratesrc/commands/generate.ts是通过PathMapper.getValidatorGlobs(area)解析配置中的 glob 模式all会取全部区域的模式用glob库发现 validator 文件若指定了--domain再用PathMapper.filterValidatorsByDomain按路径正则过滤。找不到任何 validator 文件时输出黄色提示No validator files found.并给出--domain的排查 hint。用ProgramFactory.create(validatorFiles)基于配置的 tsconfig 创建 TypeScript 编译程序与类型检查器。依次执行SchemaExtractor 提取 → NameRegistry 名称解析 → NameClassifier 文件归类 → TypeResolver 类型解析 → TypeEmitter 接口发射。按输出目录分组交给FileMerger.resolveFileContent决定创建 / 合并 / 覆盖 / 跳过最后写入文件--dry-run时只打印不落盘。每次写入/更新后由IndexManager.updateIndexFiles同步维护index.ts桶文件。FileMergersrc/utils/file-merger.ts的合并语义很实用文件不存在 →created写入全部接口文件存在且--force→overwritten用生成结果整体替换文件存在、未传--force→updated只追加文件中尚未声明的接口按名字去重兼容export interface/export type/ 非导出的interface/type四种声明形式并合并 import 行同名源合并、去重排序。因此默认情况下重复运行 generate 是幂等的所有类型已存在时会输出Skip ... (all types already present)不会产生 diff 噪音。跳过机制不是每个导出的 schema 都会生成类型NameClassifiersrc/mapping/name-classifier.ts负责把 schema 名归类到payloads、queries或skip三类查询/过滤类进入queries.ts名字匹配/Params$/、/Filters?$/、/ListParams$/、/FilterFields$/、/^StoreGet/、/^AdminGet/请求体/负载类进入payloads.ts匹配/Create[A-Z]/、/Update[A-Z]/、/Batch[A-Z]/、/Import[A-Z]/、/Export[A-Z]/、/Link[A-Z]/、/[A-Z]Request$/、/[A-Z]Payload$/都不匹配时默认归入 payloads跳过skip名字不满足publicPrefixes前缀的、或匹配/ParamsFields$/、/ParamsDirectFields$/、/ParamsBase$/、/ParamsTransform$/、/Schema$/等中间/内部辅助 schema。此外NameRegistry.resolveHttpTypeNamesrc/mapping/name-registry.ts支持把某些导出映射为skip例如与列表参数重复的单条 select params、内嵌在 payload 中的 schema也可以在 validator 源码中用http-type-name注解覆盖输出类型名——SchemaExtractor会读取该 JSDoc 标签作为httpTypeName。类型解析的关键决策TypeResolver.resolveSchemaTypesrc/core/type-resolver.ts对取_input还是_output做了细致区分带.transform()ZodEffects的 schema → 取_input因为_input表示 HTTP 客户端实际发送transform 之前的数据普通 ZodObject、WithAdditionalData包裹的 payload schema → 取_outputcreateFindParams()生成的limit/offset等字段同样取_outputz.preprocess()的输入是unknown、输出才是number。针对applyAndAndOrOperators(...)引入的z.lazy()循环引用导致 TypeScript 无法完整求值的问题解析器会检测Zod 内部属性泄漏parse/safeParse/_output或_zod同时出现、只有$and/$or、以及0 个属性但有 baseFields三种降级信号回退到createFindParams链中基础字段 schema 的类型或ZodObject的第一个类型参数shape 参数继续解析并据此把$and/$or归入BaseFilterable处理。发射阶段src/core/type-emitter.ts会进一步做这些结构决策检测到FindParams字段fields、limit、offset、order、with_deleted至少出现 3 个或调用链中包含createFindParams→ 生成的接口extends FindParams并省略重复字段仅含SelectParams的fields→extends SelectParams含$and/$or→extends BaseFilterableSelfcreateOperatorMap()字段 → 发射为OperatorMapstring类型并自动从配置的dal模块 importBaseFilterable/OperatorMap从commonRequest模块 importFindParams/SelectParams。内联的import(...).TypeName形式会被hoistInlineImports提取到文件顶部按包名向上查找最近的package.json或相对路径生成整洁的import type语句。validate命令结构兼容性校验命令签名yarn validate:http-types [options]源码定义见 src/commands/validate.ts选项如下Option描述默认值--area area要校验的 API 区域all--domain domain只校验指定 domain—--changed-files paths逗号分隔的变更 validator 文件列表CI 增量优化—--lenient将T \| null \| undefined视为与T \| undefined兼容false--ci发现任何失败即退出码为 1false--verbose除失败外也展示通过的类型false校验的判定逻辑runValidatesrc/commands/validate.ts的工作方式与 generate 共享大部分组件确定待校验的 validator 文件优先使用--changed-files相对路径会基于项目根解析为绝对路径否则按--area的 glob 发现同时把 validator 文件与 HTTP 类型文件outputBase下所有*.ts放进同一个 TypeScript Program对每个 schema 解析出期望的 Zod 类型与payloads.ts/queries.ts中对应名字的接口组成CheckPair交给CompatibilityChecker.checksrc/core/compatibility-checker.ts做结构比较输出三类差异missingFields缺失字段、typeMismatchFields类型不匹配、extraFields多余字段。该检查器还维护了一张 Zod 内部属性名集合parse、transform、shape、_def等防止校验时把 Zod 库自身暴露的方法误判为 schema 字段。结果按domain/area分组打印末尾输出Passed: N Failed: N汇总。若存在失败提示先跑generate --dry-run预览正确类型应该长什么样提示用generate --force覆盖成生成版本在--ci模式下或环境变量CItrue/GITHUB_ACTIONStrue时自动启用见 src/commands/validate.ts以退出码 1终止从而让 CI 流水线失败拦截漂移。全部通过时输出All HTTP types are compatible with their Zod schemas.。通用安装与独立项目配置安装工具已发布为 npm 包可在任意项目中使用npm install --save-dev medusajs/http-types-generator # 或不安装直接运行 npx medusajs/http-types-generator generate配置文件http-types.config.json将配置文件放在项目根目录。所有字段都是可选的缺失项会与内置默认值做 deep-merge。完整示例{ validatorGlobs: { admin: src/api/admin/*/validators.ts, store: src/api/store/*/validators.ts }, outputBase: src/types/http, tsconfig: tsconfig.json, importSources: { commonRequest: medusajs/framework/types, dal: medusajs/framework/types }, validatorPathPattern: /api/([^/])/([^/])/validators\\.ts$, publicPrefixes: [Admin, Store] }各字段说明默认值见 src/config/index.ts 中的Config.DEFAULTSField描述默认值validatorGlobs按区域area名组织的 glob 模式相对项目根目录{ admin: **/api/admin/*/validators.ts, store: **/api/store/*/validators.ts }outputBase生成文件的根目录相对项目根目录src/types/httptsconfig项目根目录下用于创建 TypeScript Program 的 tsconfig 文件名tsconfig.jsonimportSources.commonRequest导出FindParams、SelectParams的模块medusajs/framework/typesimportSources.dal导出BaseFilterable、OperatorMap的模块medusajs/framework/typesvalidatorPathPattern不带/包裹的正则需含两个捕获组(area, routeDir)/api/([^/])/([^/])/validators\\.ts$publicPrefixes只有名字以这些前缀开头的 schema 才会被处理[Admin, Store]两个值得注意的源码细节配置发现机制Config.findConfigFile会从当前工作目录逐级向上查找最近的http-types.config.jsonsrc/config/index.ts。因此无论从项目根目录还是子目录调用 CLI 都能命中配置且找到的配置所在目录会被当作projectRoot用于解析所有相对路径。正则合法性校验validatorPathPattern在加载时会先new RegExp(pattern)试编译非法则直接抛错...is not a valid regex避免运行时静默失败。配置文件 JSON 解析失败时会打印警告并回退到默认配置。Validator 文件约定写出能被工具识别的 Schema要让工具正确处理validator 文件必须满足以下约定README 原文规则 源码印证1. 文件路径匹配validatorPathPattern且模式必须包含两个捕获组area 与路由目录。src/api/admin/products/validators.ts → areaadmin, routeDirproducts以真实文件 packages/medusa/src/api/admin/products/validators.ts 为例它导出了AdminGetProductParamscreateSelectParams()、AdminGetProductsParamscreateFindParams({offset: 0, limit: 50}).merge(...).transform(...)、AdminCreateProduct、AdminUpdateProduct等一系列以Admin前缀开头的 schema。2. 导出名必须以publicPrefixes中某个前缀开头默认Admin/Store否则被跳过。3. 导出名后缀决定输出到哪个文件匹配Params、Filters以及源码中更细的ListParams、FilterFields、^StoreGet、^AdminGet→ 写入queries.ts匹配Create、Update、Batch还有Import、Export、Link、Request、Payload→ 写入payloads.ts其余默认 →payloads.ts。4. 路由目录到类型目录的 domain 映射由PathMappersrc/mapping/path-mapper.ts完成大多数场景下通过对路由名最后一个连字符段做单数化得到 domainproducts → product、sales-channels → sales-channel少量历史遗留路由通过ENTITY_NAME_OVERRIDES显式映射例如addresses → customer、product-variants → product、payment-collections → payment、uploads → file、inventory-items → inventory、order-changes → order、plugins与stock-locations刻意保持复数。工具作者在注释中建议新增 schema 时应尽量让路由/domain 命名适配自动单数化逻辑避免扩充这个覆盖表。最终输出结构为{outputBase}/{domain}/{area}/payloads.ts与{outputBase}/{domain}/{area}/queries.ts。例如 Medusa 中产品域的实际生成产物位于 packages/core/types/src/http/product/admin/payloads.ts其中AdminBatchProductRequest就是通过extends BatchMethodRequestAdminCreateProduct, AdminBatchUpdateProduct表达createBatchBody语义的。复杂 Schema 模式的提取支持SchemaExtractorsrc/core/schema-extractor.ts除普通export const X z.object({...})外还专门处理三类非常规写法WithAdditionalData(InnerSchema)包裹提取时剥掉包裹层直接以内部 schema 的_output类型为准payload 不做 transform函数类型导出当 schema 是WithAdditionalData结果的别名时通过文件内符号表解析到内部真实 schema 类型createBatchBody(create, update, delete?)因为其签名是非泛型的z.ZodType参数TypeScript 会把数组元素类型解析成unknown提取器通过在调用点检查实参类型恢复每个 batch 属性的真实_output类型缺省参数回退到函数默认值例如未传deleteValidator时默认为z.string()供兼容性校验逐元素比对。常见用法速查与 CI 集成建议以下命令组合覆盖了日常开发到持续集成的完整链路# 预览某 domain 将要生成的类型不写盘 npx medusajs/http-types-generator generate --dry-run # 只生成某 domain 的类型 npx medusajs/http-types-generator generate --domain products # 全量校验 npx medusajs/http-types-generator validate # CI 中校验失败即非零退出 npx medusajs/http-types-generator validate --ci # PR 中只校验变更涉及的 validator增量提速 npx medusajs/http-types-generator validate --changed-files src/api/admin/products/validators.ts,src/api/store/products/validators.ts --ci实践要点总结提交或合入涉及 validator 的改动前先跑generate --dry-run预览确认生成的接口形状符合预期默认合并模式下反复 generate 不会产生重复接口名字去重 import 合并适合作为常规开发流程的一环CI 中建议使用validate --cimonorepo 根脚本 package.json 中的validate:http-types即带--ci让类型漂移直接导致流水线失败--changed-files可显著减少全量编译耗时历史遗留类型若因null/undefined可空性差异报错可在明确接受宽松语义的前提下使用--lenient若必须整体重生成类型文件使用generate --force覆盖但注意这会丢弃文件中手写的注释与扩展请谨慎评估后再执行。与仓库其他部分的配合该工具生成的类型文件是 Medusa 公共类型体系的一部分下游消费者包括 packages/core/types/src/http 下的各 domain 类型目录以及依赖它们的 JS SDKpackages/core/js-sdk与 Dashboardpackages/admin/dashboard。校验失败的常见修复路径——generate --dry-run预览、generate --force覆盖——正好与validate命令的失败提示形成闭环见 src/commands/validate.ts这让Zod 校验 Schema → HTTP 公开类型的单一事实来源得以在 monorepo 的日常迭代中持续成立。工具自身的正确性由 src/tests下的单元测试保障覆盖了配置加载config.spec.ts、路径映射path-mapper.spec.ts、名称分类name-classifier.spec.ts、文件合并file-merger.spec.ts、类型发射type-emitter.spec.ts、兼容性校验compatibility-checker.spec.ts等核心模块可作为理解各组件行为的可运行示例进行研读。【免费下载链接】medusaThe worlds most flexible commerce platform for agents and developers项目地址: https://gitcode.com/GitHub_Trending/me/medusa创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表