受限 profile 的 OpenAPI 校验与渲染工具,面向中央 OpenAPI YAML 单向生成 APIFox、CloudWeGo hz / kitex Thrift IDL,以及前端 API client 的工程链路。
它不是通用 OpenAPI codegen,也不会默默降级 unsupported schema;定位是一个对受限 OpenAPI profile 做显式、可审计、fail-fast 的校验和渲染入口。openapi-thrift CLI 名称仍保留为旧 Hz bootstrap 的别名,新命令面统一叫 openapi-render。
- 只接受已冻结的 OpenAPI Render profile,不做“看起来能转、实际上丢语义”的静默兼容
- 在 APIFox 渲染、Thrift IDL 投影、前端 API 生成前统一校验同一份 OpenAPI
- 对 unsupported 能力默认 fail-fast,避免把非法契约带进 APIFox、
hz/kitex或前端 client - 纯 TypeScript 实现,同一套逻辑可在浏览器、Node CLI、CI 中复用
- 当前内置 Thrift IDL 输出,便于继续接
hz update、kitex、模板化生成链路
npm install @sttot/openapi-render或:
pnpm add @sttot/openapi-render- 只支持 ISpark 短剧大师 已冻结的 OpenAPI profile
- 不做“万能转换器”
- 核心逻辑保持纯 TypeScript,便于浏览器和 CLI 复用
- 第一版内置 APIFox/HZ-Thrift profile 校验和
.thrift文本输出,不直接调用hz、kitex、APIFox 或前端生成器
validate 子命令执行 apifox-hz-thrift profile,当前覆盖:
- APIFox 作者侧规则:
requestBody.examples/schema.example(s)/ parameter example 的类型一致性,字段级x-apifox-mock非空表达式,mockScript非空且提示人工复核,JSON request body 必须使用application/json而不是 APIFox 导出风险较高的json简写。 - 数据模型引用规则:schema
$ref只允许#/components/schemas/*,requestBody$ref只允许#/components/requestBodies/*,response$ref只允许#/components/responses/*,parameter$ref会被判定为 Hz/Thrift 不可投影。 - Auth 规则:
securitySchemes只能使用标准apiKey/http/oauth2/openIdConnect,operation/rootsecurity必须引用已声明 scheme;公开接口使用security: []。 - Hz/Thrift 可表达范围:复用下方 “当前支持 / 当前不支持” 的受限投影规则,并在校验阶段额外执行一次真实 Thrift 投影探针。
OpenAPI 3.0 JSONoperationId;若缺失,可通过--idl-dir从既有 Thrift 路由索引回填方法名application/json主 request/response body,以及 APIFox 导出的jsoncontent-type 别名- 单一非 JSON success response:投影为带
api.raw_body=""的响应包装结构 application/x-www-form-urlencoded请求体 ->api.formmultipart/form-data的普通字段 ->api.formpath/query/header/cookie参数- 单一
204/ 空 success response ->EmptyResponse - query array 参数的默认
form + explode=true(重复 key)序列化 - object / array / scalar / 有限
map<string, T> - 顶层 scalar requestBody 会退化为单字段
api.raw_body=""请求结构 - 组件 schema 本地引用:
#/components/schemas/* - 组件 requestBody 本地引用:
#/components/requestBodies/* required/default/enum到go.tag validate/default的有限投影minimum/maximum->gte/lteminLength/maxLength->min/maxminItems/maxItems->min/max- 白名单
format->validatetag:base64 / date / date-time / email / e164 / hexcolor / hostname / ipv4 / ipv6 / json / jwt / uri / url / ulid / uuid / uuid3 / uuid4 / uuid5 - 标准标量格式:
integer:int32 / int64number:float / doublestring:binary
- nullable union:
anyOf [T, null] - 可组合对象
allOf的最小支持(当前已覆盖 JobSeek 出现的单ref包装) - 可通过 vendor extension 显式接管少量不支持的字段校验:
x-dramawork-allow-unsupported-validation: truex-dramawork-validate: "custom_rule"或string[]
oneOf- 非 nullable 的
anyOf - 复杂
allOf继承拼装 pattern(当前会显式报错;如确有统一 validator,可用x-dramawork-validate显式接管)multipleOf(当前会显式报错;如确有统一 validator,可用x-dramawork-validate显式接管)additionalProperties: false(当前会显式报错;这属于 binder/decoder 级约束,不能通过字段 validator 接管)- 未列入白名单的 string
format(当前会显式报错,要求手写 validator 或收紧 schema) GET/DELETE/HEAD/OPTIONSrequestBodymultipart/form-data文件字段(单文件和多文件当前都会显式报错;APIFox 可导出string/binary,但 Hertz 官方文档说明 IDL 场景不支持文件绑定)- object query 参数
deepObjectqueryspaceDelimited/pipeDelimitedquery- 非默认
form + explode=true的 query array 序列化 parameter.contentparameter $ref- 多个
2xxsuccess response - 并行多
content-type(当前 APIFox 导入/导出工作流也会把它收敛为单一application/json) oneOf + discriminator- 非 JSON success response 对应的 object/array schema
additionalProperties: true- 包含空白字符的 string enum
当某个字段使用了当前不支持自动投影的规则,但你已经在 Hertz/validator 侧准备好了手写校验器,可以显式写:
{
"type": "string",
"pattern": "^1\\d{10}$",
"x-dramawork-allow-unsupported-validation": true,
"x-dramawork-validate": "cn_mobile"
}或者:
{
"type": "number",
"multipleOf": 0.5,
"x-dramawork-allow-unsupported-validation": true,
"x-dramawork-validate": ["half_step", "gte=0"]
}这条能力的含义是“我明确知道这里不是自动生成,而是人工接管”。如果只写允许开关、不提供 x-dramawork-validate,converter 会直接报错。
注意:
- 这条逃生口只适用于“字段 validator 能感知到的约束”,例如
pattern、multipleOf、部分未白名单format additionalProperties: false不属于这一类;JSON 绑定完成后,多余字段通常已经被丢弃,单靠字段 validator 无法恢复这条语义
当前 APIFox 真实导出、converter 支持面和 profile 结论见:
当前还额外固化了两份 APIFox 真实导出 followup fixture:
apifox-boundary-lab.followup.export.openapi.json- 确认
204/ 空 success response 会稳定保留 - 确认并行多
content-type在当前 APIFox 导入/导出工作流里会被收敛成单一application/json
- 确认
apifox-boundary-lab.header-cookie.export.openapi.json- 确认
header/cookie参数会稳定保留为标准 parameter - 确认 header 名大小写按导出结果原样保留,例如
X-Request-ID
- 确认
仓内开发先构建:
pnpm --dir standard/openapi-thrift build如果从 npm 安装,建议直接用 npx:
npx @sttot/openapi-render validate --input ./project.openapi.json
npx @sttot/openapi-render thrift --input ./project.openapi.json --output ./idl/project.thrift再运行 CLI:
node standard/openapi-thrift/dist/cli.js \
validate \
--input ./project.openapi.json生成 Thrift:
node standard/openapi-thrift/dist/cli.js \
thrift \
--input ./project.openapi.json \
--namespace dramawork.project \
--service-name ProjectService带既有 IDL 路由索引一起运行:
node standard/openapi-thrift/dist/cli.js \
thrift \
--input ./project.openapi.json \
--idl-dir D:/Developer/JobSeek/tutoring-demand/idl \
--output ./idl/project.thrift输出到文件:
node standard/openapi-thrift/dist/cli.js \
thrift \
--input ./project.openapi.json \
--output ./idl/project.thrift以库方式调用:
import {
assertOpenApiRenderDocument,
convertOpenApiToThrift,
} from "@sttot/openapi-render"
assertOpenApiRenderDocument(openApiDocument)
const result = convertOpenApiToThrift(openApiDocument, {
namespace: "dramawork.project",
serviceName: "ProjectService",
})
console.log(result.thrift)pnpm --dir standard/openapi-thrift typecheck
pnpm --dir standard/openapi-thrift lint
pnpm --dir standard/openapi-thrift build
pnpm --dir standard/openapi-thrift test