refactor(client)!: subscribeMetadata 的 type 收窄为 MetadataEventSubject,订阅无合同类型改为编译报错 (#4627) - #6156
Merged
Merged
Conversation
…4627) #4602 已把生产端钉成 declared = enforced —— MetadataEventType 枚举外的 metadata 类型不发布任何 realtime 事件。消费端却仍是宽的 string,于是 subscribeMetadata('translation', cb) 编译全绿、运行永盲。 本次把消费端也钉上:新增 spec 派生类型 MetadataEventSubject(从 MetadataEventType 用模板字面量 + 分发式条件类型解出 {type} 半边,不是 重抄一份),并收窄三处签名 —— client 的 subscribeMetadata、client-react 的 useMetadataSubscription / useMetadataSubscriptionCallback。 轴 2(扩枚举覆盖面)不预答,枚举一个成员都没动。 Refs #4627 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 CheckThis PR changes 3 package(s): 115 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
qq9340100
marked this pull request as ready for review
August 7, 2026 04:06
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 #4627
执行维护者 2026-08-06 最终确认的裁决:只做轴 1(收窄消费端),轴 2(扩枚举覆盖面)不预答、不做。枚举本体一个成员都没动。
问题
MetadataEventType(packages/spec/src/api/events.zod.ts)是封闭枚举:13 个 metadata 类型 × 3 个动作。#4602 已经把生产端钉成 declared = enforced —— 枚举外的类型不发布任何 realtime 事件,因为不存在能合法交付给(event: MetadataEvent) => void回调的事件形状。消费端却一直是宽的
string。于是这行代码编译全绿、运行永盲,类型系统一个字都没说:translation不是杜撰的:它和datasource/page/hook/trigger/validation一样,都是DEFAULT_METADATA_TYPE_REGISTRY里可注册的真实类型。这正是 AI 写订阅代码最容易踩的形状 —— 它看起来订阅上了。改动
新增导出(
packages/spec/src/api/events.zod.ts,紧跟MetadataEventType,避开 #6072 在飞的kernel/events/*.zod.ts别名区):是派生,不是在旁边重抄一份 —— 枚举加一个成员,这个联合自动跟着长,两者不可能各说各话。中间那个类型参数是必需的:条件类型只对裸类型参数分发,直接把整个联合写进模式会得到一个答案而不是 13 个。两个 helper 都是模块私有(导出面只多这一个类型,见下)。
签名收窄(三处,全部只是
string→ 这个联合,无运行时改动):@objectstack/clientRealtimeAPI.subscribeMetadata(type, …)@objectstack/client-reactuseMetadataSubscription(type, …)@objectstack/client-reactuseMetadataSubscriptionCallback(type, …)两个 hook 是被倒逼改的,不是夹带:它们只是把实参转发给
subscribeMetadata,不能比它更松。顺带把实现里三条手拼的事件名从
string[]标注成MetadataEventType[]——type一旦收窄,这三条模板就是可证的枚举成员,说出来让 tsc 复核一遍。这条在反向验证里意外自己变成了第四个 pin(见下)。排查:仓内有没有枚举外订阅
零命中,因此无需修正或删除任何调用点。按要求做了邻近词反查 —— 扫全仓源码里所有
metadata.{type}.{action}形状的字面量:恰好 39 = 13 × 3,与枚举逐字相符,无一条枚举外的名字。本仓 6 处
subscribeMetadata/useMetadataSubscription调用点传的都是'object'/'view',零迁移。content/与examples/无任何相关示例(releases/ 未碰)。反向验证(方向事先声明)
声明的预期:把
type退回string→ 两条@ts-expect-error变成未使用(TS2578),且参数精确性 pin 解析为never(TS2322)。实测两条都落地:还有一条没预测到的红,如实记录而不是抹掉:实现文件自己也红了三处 ——
const eventTypes: MetadataEventType[]在type变宽后无法证明,`metadata.${string}.created`不可赋值给枚举(TS2322 × 3)。也就是说这次的 pin 不止在测试里,实现比签名低一层也自带一个。防 phantom check
@ts-expect-error单独用是不够的 —— 它对该行任何错误都放行。所以每条都由类型级断言夹逼,把「只可能是第一个实参错」坐实:MetadataEventSubject(Param extends …抓变宽,… extends Param抓过窄,单向都不是精确);spec 侧还钉了派生本身的忠实性,其中一条专防空洞通过:
MetadataEventSubject extends Covered在派生塌成never时会真空为真(never extends X恒真),所以反方向Covered extends MetadataEventSubject才是抓塌陷的那条,两条都留。另加一条回程精确性 —— subject × action 重组必须恰好等于枚举,于是「只加metadata.translation.created不加另两个」这种半拉扩展没法悄悄落地。两个 pin 文件在各自的
test-typecheck-debt.json里都没有条目,按该台账「未列出的文件必须零错误」的规则,零就是这些 pin 的可测基线。验证
pnpm --filter @objectstack/spec test→ 326 files / 8367 tests passedpnpm --filter @objectstack/client test→ 19 files / 241 tests passedpnpm --filter @objectstack/client-react test→ 3 files / 34 tests passedtypecheck全绿(spec / client 含check:test-typecheck,债务数字未变:spec 79 files / 691 errors,client 3 files / 6 errors)pnpm lint绿;node scripts/check-nul-bytes.mjs绿(5861 文件),改动文件另做了超出该门的控制字节自扫check:generated:check:api-surface报 0 breaking / 1 added,按纪律用gen:api-surface重生成(未手改),只动api-surface/api.json一行 —— spec 生成物按 category 分片:拆掉三个单体 ratchet 文件的合并队列串行税(维护者 2026-08-06 已拍板) #5837 承诺的分片 locality 成立;重跑后 10/10 绿changeset
spec: minor(纯新增导出)/client: major+client-react: major。标 major 的理由与 #5181 同一条先例:源码级破坏、运行时零变化仍走 major。FROM → TO 写明了「原来传string的代码怎么改」两种情形各自的一行修复。边界
未扩枚举、未改
MetadataEventType本体、未动发布端(#4602 已 pin)、未碰content/docs/releases/。Generated by Claude Code