Skip to content

[finding] SqlDriver.update() keeps an explicit Promise[any] while returning null on a miss — the same published-type mask #14434 removed from driver-memory, inherited by TursoDriver and SqliteWasmDriver #14438

Description

@os-musk

Blocked-by: #13878

Recorded by the domain:engine execution seat (session session_0112hMx9hjJ9BgB28X97DS68) from the #13878 dev report (comment 5505634328, "reported, not filed") — the dev left the routing to the seat because the file belonged to #13854 at dispatch time; #13854 has since closed (PR #14170 merged), so the coupling is gone and the observation gets its own card. Generic type arguments are written in SQUARE brackets (the body sanitizer eats the angle-bracket spelling).

The observation

packages/drivers/driver-sql/src/sql-driver.tsSqlDriver.update() (around :6820 on origin/main at ad54eb342; locate by symbol) is declared with an explicit Promise[any] and returns this.formatOutput(object, updated) || null on a missing id. So the published .d.ts of @objectstack/driver-sql says any where the behaviour is "record or null", and every consumer dereferences the result with zero compiler complaint — the same mask class #13878 measured on InMemoryDriver (there it was an INFERRED any through the store's type parameter; here it is written out).

Inherited unchanged by TursoDriver (extends SqlDriver, local branch super.update) and SqliteWasmDriver (extends SqlDriver).

Why it matters now

PR #14434 (#13878, ruling A) declares the not-found arm on IDataDriver.update()Promise[Record[string, unknown] | null] — and un-masks driver-memory's doors so a caller must narrow before reading. SqlDriver's explicit any still satisfies the widened contract structurally, so tsc says nothing, and the family is honest on one driver and masked on three.

Suggested shape (not a decision)

Replace the explicit Promise[any] on SqlDriver.update() with the contract's type Promise[Record[string, unknown] | null] (and check upsert() / the sibling doors in the same file for the same spelling); add the type-level pin driver-memory now carries (memory-update-declared-null.test.ts is the template: IsAny = false, Equals the contract shape) inside driver-sql's tsc program; then typecheck the prefix consumer closure — a reader that dereferenced the any result without narrowing will surface as a real error, which is the point. Changeset @objectstack/driver-sql: minor (published .d.ts narrows from any, the same convention PR #14434 used), plus whatever the turso/sqlite-wasm packages need if their emitted .d.ts re-declares the door.

Dedup

search_issues (semantic) for the mask on SqlDriver.update returned only #13878 (the driver-memory sibling, this finding's source) and #14428 (Mongo / RemoteTransport fabrication posture); a control query for driver-sql bulkUpdate no transaction returned #13854 — the channel fires. #14435 covers driver-memory's remaining any doors (find / findOne / create), not driver-sql.

Related: #13878 (ruling A, PR #14434) · #14428 · #14435 · #13854 (closed; PR #14170 — the transaction fix on bulkUpdate, whose if (updated) guard is the live consumer of the null arm).

Filed unassigned for triage.

Generated by Claude Code

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions