fix(driver-turso): narrow every override's options from any to DriverOptions (#6402) - #6755
Merged
Merged
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Contributor
📓 Docs Drift CheckThis PR changes 1 package(s): 6 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
This was referenced Aug 8, 2026
os-zhuang
marked this pull request as ready for review
August 8, 2026 15:20
os-zhuang
pushed a commit
that referenced
this pull request
Aug 9, 2026
`SqlDriver` 的 `create/find/update/delete` 声明的是 `options?: DriverOptions`, `bypassTenantAudit` 是 `DriverOptionsSchema` 上的合法键(spec 17.0.0 起, #4311),所以这些转型早就不需要了。它们不是无害的:转型关掉的是**整个选项 对象**的检查,于是这些站点上任何键名拼错都不报错。 `TursoDriver` 一并纳入:PR #6755(#6402)已把每个 override 的 `options?: any` 收窄成 `options?: DriverOptions`(实测 `options?: any` 计数为 0),turso 当初被 单独拆出去的唯一理由已经消失;#6394 也已落地,`bypassTenantAudit` 上的 `as never` 残留为零。留着这 4 处就没有第二个人来收。 逐包实测(依赖闭包先构建,避免读到过期的 `dist/*.d.ts`): | 包 | 剥掉 | 文件 | |---|---:|---:| | driver-sql | 11 | 6 | | driver-sqlite-wasm | 19 | 8 | | driver-turso | 4 | 2 | | **合计** | **34** | **16** | `driver-sqlite-wasm` 实为 19 而非单据说的 17/18:第 19 处是 `sqlite-wasm-pagination-conformance.test.ts:34`,形状是 `as unknown as Parameters<SqliteWasmDriver['find']>[2]`,单据的窄 grep 匹配 不到。`driver-memory` / `driver-mongodb` 的转型站点为 0。 两处具名 const(`sql-driver-limit-zero-presence.test.ts` 的 `READ`、 `sqlite-wasm-pagination-conformance.test.ts` 的 `READ_OPTIONS`)只删转型**不 足以**换回检查:超额属性检查只对直接位置上的新鲜字面量生效。实测确认, `const READ = { tenantId: 't1', bypassTenantAuditt: true }` 在只删转型的情况下 EXIT=0、零报错 —— 拼错被静默吞掉。因此这两处改为 `: DriverOptions` 标注,让 字面量在声明处受检。 ⛔ 未触碰 `packages/objectql/src/engine-unknown-option.test.ts` 的 2 处:另一个 包、另一种形状(转型套在传给 `engine.find(...)` 的查询对象上,不是 `DriverOptions` 参数),且 `bypassTenantAudit` 出现在该文件自己的 `PASSTHROUGH` 漂移钉里 —— 剥掉等于删掉被测对象。属 engine-core 车道。 同样保留:两处 `find(..., {} as any, ...)` 里的**查询**参数转型 —— 被测对象是 查询而非选项,属 #4918 的擦除家族,不在本卡范围。 纯测试改动,无 changeset(走 `skip-changeset` 标签,route 2)。 Refs #6754, #6394, #6402, #6755 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MVa7roTgq6gGty2bG2KiEf
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 #6402
What was open
TursoDriveroverrides methods that take anoptionsargument and declared every one of themoptions?: any, while the base they forward to (SqlDriver, and behind it theIDataDrivercontract) declaredDriverOptions. The keysDriverOptionsnames —bypassTenantAudit,tenantId,transaction,accessible_org_ids,skipCache,timeout, … — were unchecked at every one of those doors.The argument is #5181's, one axis over. An internal caller that misspells
bypassTenantAuditgets no runtime complaint: the typo'd key is simply never read, the write proceeds unaudited, and nothing anywhere says so.tscis the only channel that ever objects, andanyhad switched it off.Nothing is known to have gone wrong through this gap. It is closed because the door was open, not because someone walked through it — the issue was filed and triaged as observational.
The issue's count is wrong, and the correction changes the scope rather than a detail. #6402 names five overrides (
update,upsert,delete,count,aggregate) — the CRUD block its author was reading while #6212 batch B was in flight. Measured againstmain,turso-driver.tscarries 17options?: anysites:find,findOne,create,update,upsert,delete,count,aggregatebulkCreate,bulkUpdate,bulkDelete,updateMany,deleteManyexecutesyncSchema,syncSchemasBatch,dropTableThis matters because of the issue's own binding rule: an identical shape must not be partially narrowed, since a subset reads to the next person as a verdict on the rest. Narrowing the five named would have reproduced that exact anti-pattern at larger scale — leaving twelve character-identical
anydoors behind, in the very PR filed to stop that from happening. All 17 are narrowed here, so no half-narrowed state exists to be misread.The dispatch ruling scoped this to "all five together, or a reasoned do-not-narrow verdict, never a partial narrowing". Narrowing all 17 satisfies both the letter (the five are included) and the intent (no residue). Flagging the scope change explicitly since it exceeds the literal ruling.
Also measured:
turso-driver.tswas the only driver in the repo with this shape. Every other driver already declaredDriverOptions. The remaining repo-wideoptions?: anyhits are narrow structural port interfaces inobjectql/service-job/service-queue/service-automation, which is a different axis and out of scope.The measurement the issue asked for
#6402 explicitly made this a precondition: "我没有量过:这五处 override 的实际调用方是否都已经在传
DriverOptions形状的值 … 收窄前应先跑一遍这个量。"Run, on all 17:
turbo run typecheckis 125/125 green, unchanged. No caller in this repo passes an off-contract value, so the narrowing is annotation-only — the same result #6210 measured on thequeryaxis. No call site needed a cast, and none was added.Reverse verification — direction predicted per channel BEFORE it was run
Reverted all 17 signatures to
options?: anyand measured both channels:pnpm typecheckTS1360, one per table row, + 1 ×TS2578✓pnpm testThat split is the finding, not an inconvenience: this defect has no runtime face at all, which is why it sat unnoticed 17-for-17. It also means the pin had to be built as a compile-time check to have any teeth.
One prediction was written imprecisely and corrected against the measurement: the doc comment first said the revert fails with an assignability error; the actual code is
TS1360(does not satisfy the expected type), because the rows usesatisfies. The comment now quotes the real message.Tests
New
turso-driver-options-door.test.tspins all 17 doors.Door<T>reports'any'for a widened door and'DriverOptions'only for an exact match; each row thensatisfiesit, so reverting any single signature failstscon that row, naming the method that drifted.A mutual-
extendscheck could not do this —anysatisfies both directions and would have reported green. TheIsAny<T> = 0 extends 1 & Tdetector is what makes the pin real, and the revert above is the evidence it works rather than an assumption that it does.The suite also carries a positive control (declared keys still fit, so the pin is not green merely because nothing fits) and a
@ts-expect-errorcase on the exact misspelling the issue argued about —bypassTenantAdit. That directive trippingTS2578under revert is what proves the excess-property check is live.No existing test turned red, so nothing needed fixing and no cast was introduced to silence anything.
Verification
pnpm --filter @objectstack/driver-turso test— 840/840 passed, 28 filespnpm --filter @objectstack/driver-turso typecheck— cleanturbo run typecheck(packages, packages/*/*, apps) — 120/120; full monorepo 125/125pnpm --filter './examples/*' run typecheck— clean (4/4)turbo run build(packages + examples deps) — 70/70 and 66/66.github/workflows/lint.yml— run one by one, everycheck:*in the ESLint and TypeScript Type Check jobs. All pass, includingcheck:query-options-erasure(turso is not baselined — that ratchet targets engine query-options; the new test adds no erasure and leavestestSurface.sitesalone),check:driver-conformance,check:driver-memory-census,check:type-check-debt,check:exported-any,check:published-files.pnpm lint— cleanRe-run in full after merging
origin/mainat64d764e.Notes
origin/main64d764e. The diff is 3 files —turso-driver.ts, the new test, and the changeset.remote-transport.tsis untouched, keeping clear of in-flight PR fix(drivers): text-operator case folding is the contract's answer, not the dialect's (#6518) #6706.patch, not docs-only: the narrowed signatures are public, so a downstream TypeScript consumer holding aTursoDriver-typed reference and passing a non-DriverOptionsoptionswill now see a compile error where it previously saw none. That error is the point — the value was already being ignored by the driver.countand [finding][drivers]DriverQuery收窄之外:aggregate/distinct/analyzeQuery等驱动自有查询方法仍要求把对象名写两遍 #6212 onaggregateare now discharged and replaced with a block comment abovefind()recording why the sweep was all-or-nothing, so the next reader does not re-derive it.Generated by Claude Code