refactor(drivers)!: 五个驱动的 query 参数跟进 DriverQuery,休眠的类型谎言没有藏身处 (#6075) - #6210
Merged
Conversation
#5181 (PR #6076) 把 IDataDriver 的六个 query 方法收窄为 DriverQuery (Omit<QueryAST, 'object'>),但五个驱动的实现仍声明 query: QueryAST (turso 侧是 query: any)。方法参数按双变比较所以它不红,可调用方现在 有权省略 object —— 实现的类型说 query.object 是 string,运行期却可能 是 undefined。这是休眠的类型谎言,不是活体缺陷:实测五个驱动没有一个 读 query.object。 把六个契约方法 (find/findOne/count/updateMany/deleteMany/explain) 的 query 参数统一为 DriverQuery,并跟进少量只转发 where/orderBy/groupBy 的私有辅助方法。收窄后「驱动读 query.object」成为编译错误 (TS2339)。 零运行时改动:非测试改动 100% 是类型注解,无逻辑、无行为、无 emit 差异。 测试面按 #5181 的迁移说明逐处重拼 —— 删掉调用字面量里冗余的 object 键; syncSchemasBatch 条目与 distinct 的 QueryInput 不在收窄范围,保持原样。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WyvqvKMG6asi9aXjKE6xtx
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Contributor
📓 Docs Drift CheckThis PR changes 4 package(s): 15 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
This was referenced Aug 7, 2026
Closed
Open
上一版的消费半径扫描用了 `--filter 'pkg...'`(后缀省略号 = 该包 + 它
依赖的包,上游闭包),方向扫反了:签名收窄打到的是**下游**消费者。
真正该用的是前缀形 `--filter '...pkg'`,或者直接以全仓 `pnpm typecheck`
为准(CI 跑的就是全仓)。@objectstack/dogfood 因此没进上一轮扫描面。
全仓复扫命中一处真实编译错误:storage-growth.dogfood.test.ts:260 的
`cold.count(...)`,`cold` 是真实的 SqlDriver,内联字面量里的冗余
`object` 被收窄后的 DriverQuery 拒绝。
同包另有 9 处同形调用未被 tsc 标出 —— 它们走本地结构替身
`DriverLike.count(object, query?: Record<string, unknown>)`,任何键都收,
类型层看不见。一并按同一规矩删除,避免同一文件里两种写法并存。
删键用的是带反向引用的模式,只在 query 里的值与第一个实参**逐字相同**
时才匹配,等价性由模式本身保证;dogfood 包里其余 `{ object: ... }`
(权限授予、端点策略入参、report 条目、类型声明)因此一处未被波及。
⛔ 未放松任何驱动侧签名。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WyvqvKMG6asi9aXjKE6xtx
check:type-check-debt 的全仓 re-measure 报 plugin-auth 从 131 涨到 132
(+1)。plugin-auth 的 tsconfig 把 **/*.test.ts 排除在外,所以 per-package
`pnpm typecheck` 看不见它,只有把排除掀掉的全仓 re-measure 量得到。
定位到的就是一处:
src/auth-where-operator-coverage.test.ts(383,7): error TS2353:
Object literal may only specify known properties, and 'object'
does not exist in type 'DriverQuery'.
`driver.find('sys_user', { object: 'sys_user', fields: ['id'] } satisfies
QueryAST)` —— 删掉的值与第一个实参逐字相同,属重拼类。承载类型按第三类
处置改为 `satisfies DriverQuery`:该处上方注释明确写了「这里不能用 as any,
否则会被 check:query-options-erasure (#4674/#4918) 记一笔」,所以保留
satisfies、只换目标类型,正是它原本的意图。
⛔ 未抬台账数字:门禁原话「the ledger is a ratchet and may only shrink」
(#5278),本单是自己引入的 +1,属可约。改完复测回到 131,与台账记录一致。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WyvqvKMG6asi9aXjKE6xtx
os-zhuang
marked this pull request as ready for review
August 7, 2026 11:57
os-zhuang
pushed a commit
that referenced
this pull request
Aug 7, 2026
- `find()` 调用去掉 `object` 键:#6210 把驱动查询参数收窄为 `DriverQuery`, 对象名只由第一个实参给出。没有用 `as any` 绕过——拒收类测试一旦用越契约的 输入构造查询,就不再是在验证调用方真能发出的形状。 - 反向验证计数重测:LIKE 臂停用 → 8 failed / 40 passed;成员臂停用 → 4 failed / 44 passed(此前记的 32 / 36 是首写时对 40 个测试测的)。失败数 两次都不变,是承重的那一半;通过数随邻测增删而漂移,已在注释里说明。 - 补记 analytics 侧同法实测:两个门回退到 main 版本 → 17 failed / 41 passed, 而 `like-metacharacter-escape.test.ts` 在该回退下保持绿——它锁的是两包谓词 之间的一致,拒收文件锁的是门确实调用了谓词,两者都需要。 Refs #5234 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WyvqvKMG6asi9aXjKE6xtx
This was referenced Aug 7, 2026
This was referenced Aug 7, 2026
This was referenced Aug 7, 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 #6075
这单是什么
#5181(PR #6076)把
IDataDriver的六个 query 方法收窄为DriverQuery(Omit< QueryAST, 'object' >)。那条 changeset 自己写明了收尾未做:「把驱动签名一并迁到DriverQuery是后续的机械收尾」。这就是那次收尾。前提复核(实测 origin/main @
80f7dc6,行号以实测为准)issue 正文的行号已漂移,以下为实测:
packages/spec/src/contracts/data-driver.ts:40导出DriverQuery,:141:167:195:211:214:345六个方法都已声明它。explain是any)、turso 5 处any;sqlite-wasm 自身 0 处 ——SqliteWasmDriver extends SqlDriver,签名随基类走,本 PR 不需要动它的源码。git grep 'query\.object' origin/main -- 'packages/drivers/*/src'零命中。issue 要求复核的这一条仍然成立 ⇒ 今天没有人踩,这是休眠的类型谎言,不是活体缺陷。反向验证 —— 方向预判:红(预判写在动手之前)
本单的全部价值就是让「驱动读
query.object」变成编译错误,所以预判是标准的「把删掉的肢体装回去 ⇒ 变红」。两个方向都真跑了:after(收窄后),在
SqlDriver.find里临时写一行query.object:before(对照:把同一个签名改回
QueryAST,探针保留),TS2339 消失——谎言在收窄前编译得干干净净。该轮剩下的 134 条全部是TS2345,来自已重拼的 fixture 被改回去的宽签名拒绝,与探针无关。探针已删除(
git diff中零残留)。新增的 pin 文件
sql-driver-query-signature.test.ts也做了同样的非幻影验证:把find改回QueryAST后它四条断言全红,且第一条按方法逐个报名:改了什么
非测试改动 100% 是类型注解,无逻辑、无行为、无 emit 差异(
git diff里 4 个源文件共 33 增 32 删,逐行都是签名)。六个契约方法:
find/findOne/count/updateMany/deleteMany/explain。turso 的query: any一并收紧。跟着必须动的私有辅助方法(都只转发或读
where/orderBy/groupBy,本来就不读object;不动它们则公有方法传参处直接类型不通):buildFindOptions/buildSortSpecfields/orderByfindRows/orderKeysFororderBy/limit/offsettoRemoteQuery/toRemoteReadQuerywhere、拼orderByperformAggregationsql-driver.ts的QueryASTimport 收窄后已无使用者(余下两处是散文里的QueryASTSchema),一并删除。收窄不只是消除谎言:实测降了一批类型债
check:type-check-debt的全仓 re-measure 在本分支报出一批下降项。门禁明说下降不必付记账代价(「an improvement must not have to pay a bookkeeping toll to land」),所以台账未动;但这是本单的实际收益,值得记一笔:只有
driver-mongodb的 −33 做了归因验证,其余未归因(它们可能早于本分支就已下降,我没有度量,不替它们邀功)。mongodb 这一条是掀掉 tsconfig 的测试排除后直接对照量的:origin/main的mongodb-driver.ts(query: QueryAST)mongodb-driver.ts(query: DriverQuery)原因很直接:mongodb 的测试里本来就有一批只递
where/fields的 query 字面量,在QueryAST必填object的年代它们全是错误、被冻在 TEST_DEBT 里;收窄之后它们本来就该编译通过了。冻结面口径与一处需要维护者过目的判断
按认领评论的第三档口径,memory / mongodb 一并收紧,且硬约束是只允许改签名类型。全程遵守,但有一处值得单独说明,请复核:
memory-driver.ts的私有performAggregation(records, query: QueryInput)只解构{ groupBy, aggregations }、从不读object,但QueryInput把object列为必填,于是find收窄后传参报TS2345。处理方式是把它改成Omit< QueryInput, 'object' >—— 镜像掉同一个键,不改其余任何松紧度,因此它仍是纯签名类型改动,没有动逻辑、没有动行为、没有动断言,符合硬约束。之所以不写成DriverQuery:那会连带收紧groupBy的元素类型(输入形 vs 输出形),超出「只搬走object这一个键」的范围。若维护者认为冻结面连这一处都不该动,我可以把 memory 半边整体拆出。Fixture 三类处置(不是一把批量重拼)
收窄触发 399 处
TS2353,逐处按其守的是什么来判:driver.find(X, { object: X, where })里的object就是 [spec] IDataDriver 的 query 参数要求QueryAST.object与第一实参重复 —— 下游被迫as any(20 处实测),提议 Omit/optional 化 #5181 说的冗余——第一个实参已经是对象名。删键。每一处都核对过删掉的值与第一个实参相等(脚本比对,零处不等),所以不存在「测试原本靠 AST 的 object 覆盖实参」的情况。turso-driver.ts测试里syncSchemasBatch([{ object, schema }])的object,以及memory-driver.test.ts里distinct(obj, field, query: QueryInput)的object。这两个不是被收窄的六个方法,其中syncSchemasBatch的object是被真实读取的必填键。已逐处放回。turso-local-remote-null-parity.test.ts整份用as QueryAST拓宽字面量喂find/count,删键后该 cast 自相矛盾(TS2352)。cast 目标改为as DriverQuery,与形参对齐。remote-transport-text-predicates.test.ts的局部 helperwhere: unknown同理收成DriverQuery['where']。expand条目里的object命名的是关联对象、不是冗余,tsc 也不会标它(expand值仍是QueryAST)——已确认驱动测试里没有这类站点,未被波及。本 PR 第一版把消费半径扫反了方向,CI 因此真红过一次(
@objectstack/dogfood#typecheck)。记在这里,免得后来者照抄错命令。pnpm 的省略号是有方向的:
--filter 'pkg...'(后缀)--filter '...pkg'(前缀)签名收窄打到的是下游——那些拿具体驱动类、写内联
{ object, where }字面量的调用方。所以第一版报告里「25 个包全绿」扫的全是上游依赖,真正的消费者(@objectstack/dogfood等)一个都没进扫描面,结论完全不成立。正确做法,且以全仓为准(CI 跑的就是全仓,别再用局部闭包下结论):
(注意
typecheck在 turbo 里dependsOn: ["^build"],走 turbo 会自己把依赖建好;直接用 pnpm 递归则不会,新 worktree 里容易撞上 AGENTS.md §9 的陈旧产物陷阱。)pnpm typecheck仍然不够 —— 排除了测试的包要靠台账闸门才量得到修完 dogfood 之后全仓
pnpm typecheck是 125/125 绿的,CI 却仍红在check:type-check-debt:原因是
plugin-auth的 tsconfig 把**/*.test.ts排除掉了,per-packagetsc --noEmit根本看不见它的测试层;只有台账闸门的全仓 re-measure 会把排除掀掉重量。所以「驱动签名收窄」这类会波及测试字面量的改动,验证面必须是pnpm typecheck+pnpm check:type-check-debt两条,缺一条就会像这次一样漏。定位到的就是一处,改法照上面第 3 类:
保留
satisfies而不是改成as any,是尊重该处原有的意图:它上方的注释写明「这里不能用as any,否则会被check:query-options-erasure(#4674/#4918)记一笔」。只换承载类型,意图不变。⛔ 未抬台账数字。 门禁原话是「the ledger is a ratchet and may only shrink」(#5278),而这 +1 是本单自己引入的、属可约。改完复测回到 131,与台账记录一致,
check:type-check-debtEXIT=0。下游实际改了什么
packages/qa/dogfood/test/storage-growth.dogfood.test.ts:260,cold是真实SqlDriver),7 处走DriverLikepackages/qa/dogfood/test/group-key-read-shape-parity.test.tspackages/qa/dogfood/test/empty-group-bucket-parity.test.tspackages/plugins/plugin-auth/src/auth-where-operator-coverage.test.tsdogfood 那 9 处非强制的走本地结构替身
DriverLike.count(object, query?: Record< string, unknown >),任何键都收、类型层看不见;一并按同一规矩删掉,避免同一个文件里两种写法并存、诱导后人「修回去」。每处删键的相等性核对结论:11/11 全部相等,零处不等。 dogfood 那 10 处不是靠肉眼——用的是带反向引用的模式,只有 query 里的值与第一个实参逐字相同时才匹配:
等价性由模式本身保证,不相等的站点根本不会被匹配到。dogfood 包里其余的
{ object: ... }——权限授予(publicFormGrant)、端点策略入参(objectParams)、report.swept条目、以及interface里的类型声明——因此一处未被波及。plugin-auth 那处是单点,肉眼核对'sys_user'vs'sys_user'。⛔ 未为了让下游过而放松任何驱动侧签名。
顺带记一条反面教材(本 PR 未触,属其他包):
packages/objectql/src/engine-unknown-option.test.ts:183的engine.find('task', { object: 'person' } as any)是故意让两者不相等的拒绝测试。任何按「见object:就删」的批量清扫都会毁掉它——这正是必须逐处核对相等性、而不是无脑 sweep 的原因。验证
冻结面 memory / mongodb 测试全绿,即是零行为变化的证据。
验收 grep:
六个方法上归零。
aggregate不在IDataDriver里(distinct同理),不是本单范围。Changeset
.changeset/driver-query-signatures-follow-through.md,五个驱动包major,依据与 #5181 同一条:源码级破坏性(调用点内联字面量会TS2353),运行时行为零变化。sqlite-wasm 自身虽无源码改动,但它继承SqlDriver,公开类型面同样变了,故一并列入。check:api-surface只记录导出存在与否、不记录签名,所以这条 changeset 是该变更唯一的下游载体——这一点也是 #5181 的原话。@objectstack/dogfood是private的 QA 包、不发布,plugin-auth的改动只在测试文件、无公开面变化,两者都不进 changeset。顺带记录的发现(均未认领、observation-class)
DriverQuery收窄之外:aggregate/distinct/analyzeQuery等驱动自有查询方法仍要求把对象名写两遍 #6212 ——aggregate/distinct/analyzeQuery等驱动自有(非IDataDriver)查询方法仍要求把对象名写两遍,或干脆是query: any。DriverQuery已经存在,但几处驱动调用方仍在用as any/as QueryAST兜住冗余的object—— 连带把 where/orderBy/fields 的检查一起关掉 #6231 —— 几处调用方仍用as any/as QueryAST兜住冗余的object(metadata的database-loader、objectql的engine.ts/lifecycle-service.ts),连带把where/orderBy/fields的检查一起关掉。并行注记
PM 标注 #5907(聚合函数信封)同批在飞,触
sql-driver.ts的聚合区与remote-transport.ts。本 PR 的面是签名区与turso-driver.ts,remote-transport.ts一个字未动,零重叠;后落地者 rebase。