Uh oh!
There was an error while loading. Please reload this page.
fix(metadata-protocol): getUiView 不再多发三个未声明键,响应体与 GetUiViewResponseSchema 对齐 (#5948) - #6271
Conversation
…hema 对齐 (#5948) GET /ui/view/:object/:type 由 getUiView 产出、REST 裸发(res.json,不套信封不校验)。 其声明是 GetUiViewResponseSchema(= ViewSchema),但实发 body 里的 list.object / form.object / form.label 三个键,ListViewSchema / FormViewSchema 这两个 strictObject 从未声明,实测 safeParse 判 unrecognized_keys 红。因为该 schema 全仓没有运行时读者, 这处分裂此前没有任何断言看得见。 - object 上移一层到容器(ViewSchema 一直在容器层声明它),成员层那份是冗余副本; - form.label(原 `Edit ${…}`)是渲染串而非元数据,直接摘除、不上移; - list.label 不动 —— ListViewSchema 正式声明了它。 packages/spec 零改动:本次是把实现修正到既有声明,而非改声明迁就实现。 常驻 pin 用生产端真实组装路径(实调 getUiView)喂 safeParse,而非手拼 fixture。 反向验证:恢复任一多发键 → pin 转红并点名该键。 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
|
📓 Docs Drift CheckThis PR changes 1 package(s): 3 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
qq9340100
commented
Aug 7, 2026
ACCEPT(spec 车道 PM 验收, 逐 job 结论亲读:23 checks 全 completed,21 success + 2 预期 skipped(Console Pin Gate / Build Docs),ESLint 与 TypeScript Type Check 均
已翻 ready + 挂 auto-merge。#6239(五休眠声明退役)按维护者裁决排 #6083 之后,由本席巡检跟进。 Generated by Claude Code |
Uh oh!
There was an error while loading. Please reload this page.
Fixes#5948
执行 2026-08-07 维护者重裁:选项 3(前提证伪后取代 03:19 的裁 A 字面落点)—— 生产端摘掉多发键,
packages/spec零改动,changesetpatch。一、前提修正(为什么落点和原单不同)
原单与裁 A 都称这条路由的 spec 声明是
GetViewResponseSchema—— 实测是张冠李戴,已在 issue 证据评论 逐条留档:getUiViewGetUiViewResponseSchema(protocol.zod.ts:363)=ViewSchema:1533protocol.ts:4179✅GET /ui/view/:object/:type✅getViewGetViewResponseSchema(:702)={object, view}:1560rest-server.ts:4895-4919调的是p.getUiView后res.json(view)。GetViewResponseSchema属于零实现零路由的 View-Management CRUD 块(已另立 #6239,维护者裁退役、排 #6083 后)。本 PR 不碰 spec,也不碰那五个休眠声明。真实缺陷在成员层:
ViewSchema容器本就声明list?/form?,但成员内多发的三个键被strictObject判红。二、判别键与真实形状(实测,非推测)
两条 return 的顶层键分别只有
list/form,无公共字面量判别键 —— 严格意义的z.discriminatedUnion在顶层不适用。这也是选项 3 更顺的原因之一:容器ViewSchema早已用「可选成员」表达了这个二选一,无需新建联合。改前实测(红色基线)
三、FROM → TO
「原来当 unknown 用的代码怎么改」:此前
client.meta.getView返回unknown,调用方要么不读、要么as any强读。改后 body 满足GetUiViewResponseSchema,即View:object的:body.list.object/body.form.object→body.object(相同的值上移一层,不是删除 ——ViewSchema一直在容器层声明object:「Object this container binds to」);body.form.label(原`Edit ${…}`)已摘除且不上移 —— 渲染串不属于元数据,任何 view schema 都没声明过它;调用方本就知道自己请求的对象,标题由 UI 自行拼;list.label的:不受影响,ListViewSchema正式声明了label,保持原样。消费面实测(定级 patch 的依据):
client-react的useView(metadata-hooks.tsx:229)把 body 当any透传(UseMetadataResult.data: any);objectui 全仓meta.getView零命中(其getView(objectName, viewId)走client.meta.getItem('view', …),另一条通路)。无编译期破坏面,无类型改判 ⇒patch。未验面:
cloud仓不在本次验证范围(按 #5540 口径如实标注)。若该仓直读body.list.object/body.form.object,按上面上移一层。四、常驻 pin 与反向验证
packages/metadata-protocol/src/protocol.ui-view-response-conformance.test.ts—— 刻意用生产端真实组装路径(实调getUiView)喂GetUiViewResponseSchema.safeParse,而不是手拼 fixture:手写 fixture 钉的是「本文件相信生产端发什么」,而那个相信恰恰就是此前错的东西。反向验证方向事先声明:本例是常规「红」向 —— pin 断言的是 full
safeParseGREEN 且 schema 为strictObject,故恢复任一多发键必然产出unrecognized_keys并点名。实跑结果与预测一致:恢复后 6/6 绿。key-vs-value 判据:本 pin 守的是键是否为该形状的合法成员,但断言取 full-parse-green 而非仅
unrecognized_keys缺席 —— 因为契约是「整个响应体满足声明」,只钉部分判据会放过其他漂移。五、验证
pnpm --filter @objectstack/metadata-protocol testpnpm --filter @objectstack/metadata-protocol build(含 DTS)dist/index.d.ts 197.37 KBcheck:route-envelopecheck:engine-double-contractcheck:type-check-coveragecheck:nul-bytes+ 改动文件自扫grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]'无命中)check:empty-changeseteslint(改动文件,--no-inline-config)runtime/rest侧全部以vi.fn().mockResolvedValue(...)整体替身,钉的是 dispatcher 透传而非生产端形状 ⇒ 无 fixture 需迁移;domain-handler-registry+error-passthroughtargeted 跑 62 passed六、对在飞单的影响
packages/spec,GetUiViewResponse/GetUiViewResponseParsed别名对与同构 pin 均未触及。view的 spec 校验闸门形同虚设:saveMetaItem({ item: { nope: 1 } })返回 success 并把{"nope":1}存成一个 active view #5599(ui/view.zod.ts校验闸门):零文件相交(被否的选项 2 才会撞该文件,本 PR 未采纳)。options[]该不该讲字段级的逐选项词汇(color/visibleWhen)? —— 三处形状,三种拼法 #5016 /composeStacks的i18n仍是 last-wins —— #5005 裁决否掉的那个形状,只剩这一个键还在用 #5051 /functions: { fn: { handler, effect: 'writes' } }cannot surviveobjectstack build— lowering emits a shape FlowFunctionEntrySchema rejects #4976 / [spec]HookContext.api声明为z.unknown()—— 按(ctx: HookContext)标类型的 hook 无法调用ctx.api.object(…),而文档/技能全在这么教 #5945 / SDUI props 声明与 renderer 不一致:6 处「renderer 兑现但 ComponentPropsMap 未声明」+ 2 处「声明了没人读」(#5068 error 升级的 spec 侧前置) #5775 / ADR-0049 enforce-or-remove: ui/widget.zod.ts 全文件 + ui/i18n.zod.ts 五个形状实测无门(#4001 批 16 改判) #5055:零文件相交。Generated by Claude Code