fix(spec): openapi 静态产物摘除 built-in 路由段,只保留契约半边 (#5744) - #5967
Merged
Conversation
#5588 裁定 C 第二棒。`build-openapi.ts` 手写的 `generateCrudPaths` / `generateMetadataPaths` / `generateDiscoveryPaths`(7 path / 10 operation,真实 boot 0/10 命中)整体摘除;`paths` 与三个只为命名它们而存在的 tag 一并消失。第 一棒 #5821 已让 rest 在 serve 期自产该段并丢弃静态旧段,故本次为零行为变化。 #5168 自恰门按新形状调整:如实标注 `assertRefsResolve` 今天空断言,保留它防的是 `z.toJSONSchema` 的 `#/$defs/…` 指针;反向验证的变异改注入 components 侧。新增 「产物不含路由段」「产物仍保留 spec 拥有的五个顶层键」两条钉子。`check:generated` 台账 `gen:openapi` 的 `why` 改写为真正剩下的缺口(时效性无门禁)。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011M7UwH25Unfi73UHim7ajY
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Contributor
📓 Docs Drift CheckNo hand-written docs reference the 0 changed package(s). ✅ |
qq9340100
marked this pull request as ready for review
August 6, 2026 13:41
This was referenced Aug 6, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #5744
#5588 维护者裁定 C 的第二棒(spec 半边)。第一棒 PR #5821 已合入
origin/main(75f82f3ac),registerOpenApiEndpoints流水线里enriched.paths = builtin.paths—— serve 期已由 rest 按自身路由表自产 built-in 段并整体丢弃静态产物的paths。因此本单是零行为变化的清理。前置复核(在
origin/main上)git merge-base --is-ancestor 75f82f3ac origin/main→ YESpackages/rest/src/rest-server.ts:3271enriched.paths = builtin.paths;(注释显式写 DISCARDED rather than merged)pnpm --filter @objectstack/spec gen:openapi→Paths: 7,9 个$ref全部在 paths 段内、components 外 0 个改了什么
packages/spec/scripts/build-openapi.tsgenerateCrudPaths/generateMetadataPaths/generateDiscoveryPaths三个函数、interface OpenApiPath、const basePath = '/api'及其调用;tags: [CRUD, Metadata, Discovery]—— 这三个 tag 只为命名被摘掉的三段而存在,任何文档里都没有 operation 携带它们,serve 期的 tag 列表由 rest 与路由段一起产出(enriched.tags = builtin.tags),所以移除同样是零行为变化;servers(rest 把它作为兜底项接在实时请求源之后,删了会改变服务出的文档)、security、info、components;Paths:行(Paths: 0会被读成「收集器什么都没产出」而不是「这份产物不描述路由」),换成一行明说路由段归属。paths是「键不存在」而不是paths: {}—— 这是本单唯一需要拍板的形状细节,理由写在文件头注释里:OpenAPI 3.1 里paths可省(paths/components/webhooks三者有其一即合法),而两种写法说的不是一件事。paths: {}断言「这个 API 什么都不服务」,是假的;键不存在则对路由不作任何断言,这才是这份产物有资格作出的声明。消费侧唯一的读取点是 rest 的spec.paths ?? {}与enriched.paths = builtin.paths,两种写法它都吃。产物新形状(顶层 5 键):
openapi/info/servers/components(schemas9 条 +securitySchemes)/security。#5168 自洽门:按新形状调整,不留活口
摘段后 9 个
$ref一个不剩 —— 它们全住在被摘掉的 operation 请求/响应体里。这正是「断言还绿着,是因为什么都没产出」那一类,所以按三条处理,而不是为了让门「有东西可断言」保留任何静态段:如实标注空断言:新增
emits a document with no $ref at all — so the ref gate is vacuous TODAY,把这个事实写在门旁边,而不是让下一位读者以为findDanglingRefs(doc) === []是覆盖。反向验证改指向幸存面:旧的两条变异改写
generateCrudPaths里的#/components/schemas/ApiError'字面量 —— 该字面量已随段消失,旧变异会撞上runGenerator的「mutation must actually change the source」守卫,变成报告测试坏了而不是报告门有效。新变异注入components:Broken: { $ref: '#/components/schemas/ApiErrorTypo' }(组件引用被改名的兄弟);Recursive: { ... $ref: '#/$defs/Recursive' }—— 门保留下来真正要防的活风险:z.toJSONSchema把复用/递归子 schema 放进它返回值根部的$defs,用根相对#/$defs/…指过去;九条各自独立转换后停在components.schemas[Name]下,该指针于是指向整份 OpenAPI 文档的根 —— 那里没有$defs。九条今天都不是递归形状,这条变异就是「哪天有一条变成递归」的预演。三条反向验证的预期方向都是 RED(非零退出),实测三条全 RED,并各自匹配到具体的报错文本。
补两条新钉子:产物不含路由段(七条幽灵路径 + 三个 tag 逐条断言不存在,
paths/tags键为 undefined),以及产物仍完整保留 spec 拥有的五个顶层键 —— 后者防的是把这次收缩做过头、连 rest serve 期要读的东西一起删掉。lib/openapi-self-consistency.ts的模块 TSDoc 同步补上「规则 1 今天为空断言、保留它是为了$defs」这一节。check:generated台账gen:openapi那条why原文是「no check gate compares it to the routes」。裁定 C 之下产物里没有路由,这句话名指的对账没有第二方。改写为真正剩下的缺口:没有任何东西把产物的components.schemas与src/api对账,产物过期不会让任何东西变红;自恰性由 #5168 在写盘前自检覆盖,时效性没有。(#5456 已依此关闭 not planned。)AGENTS.md里「Two generators have no gate at all — nothing verifies their output is current」未提路由,新现实下仍然成立,故未改。验收对照
gen:openapi绿、产物paths不含手写 built-in 段Components: 9/Route sections: none;"paths" in doc === false,七条幽灵路径与三个 tag 在整份 JSON 里 0 命中packages/rest全量 60 files / 833 tests 绿,含 #5588 那一组 7 条(publishes no path the server does not mount、documents the real CRUD surface, with the real verbs等)关于 #5821 那条 serve 期钉子(
discards the static artifact section even though spec still emits it)按派单要求如实报告:该钉子直接读本进程加载的静态产物做比对,摘段后它的输入集合
Object.keys(artifact.paths ?? {})变成空集,那个for循环因此成为空断言。从 rest 的解析路径实测:它仍然是绿的,而且没有整体退化为空断言 —— 同一条 test 里循环之后的三条断言仍然咬合:
components.schemas键集与产物逐字相等、securitySchemes深等、info.title相等。也就是说它现在钉的是「spec 拥有的那半边原样穿过 serve 期」,这半边我这次特意没动。⛔ 没有为了让它「有东西可断言」而保留任何静态段。该 test 的标题与注释(「even though spec still emits it」)现在措辞过期了 —— 属packages/rest,第一棒收官后本单不碰,已作为观察项记在报告里交 PM 决定。变更集
.changeset/openapi-static-artifact-contract-only.md,@objectstack/spec: major。判 major 而非 minor:@objectstack/spec/openapi.json是exports+files里的已发布导出,paths与tags两个顶层键消失(不是变空),直接doc.paths取值的外部消费者会拿到 undefined。被删内容本身 0/10 命中、照它生成的客户端每个数据调用都 404,但「消费者会看到破坏」这一事实与「被删的东西本来就是错的」是两回事,按前者定档。要一份带路由的文档,唯一正确来源是运行中的服务:GET {apiPath}/openapi.json。验证记录(全部前台阻塞执行)
生成物纪律:
packages/spec/json-schema/已被根.gitignore忽略,产物不入库;gen:schema(在pnpm --filter '@objectstack/rest^...' build里)跑完后已复核openapi.json仍在且为新形状(#5371 的 rmSync 陷阱)。🤖 Generated with Claude Code
https://claude.ai/code/session_011M7UwH25Unfi73UHim7ajY
Generated by Claude Code