You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 3cbcedb
Browse filesBrowse the repository at this point in the historyBrowse files
feat(driver-sql,driver-turso): the remaining IDataDriver doors publish their declared types, not any (#15267) (#17258)
* feat(driver-sql,driver-turso): the remaining IDataDriver doors publish their declared types, not any (#15267)
`SqlDriver` published an explicit `Promise<any>` over five doors the contract
had already declared narrower — `findOne`, `create`, `bulkCreate`, `execute`
and `explain` — so the emitted `.d.ts` erased every one of them. `TursoDriver`
overrides four of the same five with its own `Promise<any>`, which no
driver-sql fix reaches.
Each annotation is replaced with the type
`packages/spec/src/contracts/data-driver.ts` already declares for that door,
and each is pinned at the type level inside its own package's tsc program.
No runtime behaviour changes.
Claude-Session: https://claude.ai/code/session_01XTBcV7zZHmokdyQgXjbyEU
Co-authored-by: Claude <noreply@anthropic.com>
* fix(driver-sql,driver-turso): narrow the consumer sites the declared doors surfaced (#15267)
The narrowing surfaced 68 un-narrowed dereferences, every one in the two
packages' own tests: 64 reads of a `findOne()` result with no `null` check
(`expect(...).not.toBeNull()` does not narrow), and four reads through the
`unknown` that `bulkCreate()`, `explain()` and `findOne()` now resolve to.
Each positive control asserts the row arm with vitest's `assert()` — a
narrowing assertion, not a `!` and not a cast — and each `unknown` read names
what it reads. The not-found controls keep their `toBeNull()` and gain nothing.
Claude-Session: https://claude.ai/code/session_01XTBcV7zZHmokdyQgXjbyEU
Co-authored-by: Claude <noreply@anthropic.com>
* fix(driver-sqlite-wasm): narrow the inherited-door consumer sites (#15267)
`SqliteWasmDriver` overrides none of the five doors and inherits every one, so
the driver-sql narrowing reaches its callers through that package's `.d.ts`.
Eight positive controls assert the row arm; one `create()` read names the
string it collects. No source change in this package.
Claude-Session: https://claude.ai/code/session_01XTBcV7zZHmokdyQgXjbyEU
Co-authored-by: Claude <noreply@anthropic.com>
* fix(runtime): read the record number as a string in the autonumber parity probe (#15267)
`SqlDriver.create()` declares the contract's `Record<string, unknown>` now, so
the cross-side parity probe's `rec_no` read is `unknown` where it was reached
through an `any`. It converts to the string the probe compares.
Caught by this package's `check:test-typecheck` gate, not by `tsc --noEmit` —
the file is in the checked test zone and the ledger does not cover it.
Claude-Session: https://claude.ai/code/session_01XTBcV7zZHmokdyQgXjbyEU
Co-authored-by: Claude <noreply@anthropic.com>
* chore(changeset): declare the driver-sql / driver-turso door narrowing (#15267)
Both graded `minor` and marked `type-surface-only` under ADR-0087, matching
the landed precedent PR #15280 for `update()` on the same classes.
Claude-Session: https://claude.ai/code/session_01XTBcV7zZHmokdyQgXjbyEU
Co-authored-by: Claude <noreply@anthropic.com>
* chore(changeset): name only predicate-4-verifiable symbols in the ADR-0087 markers (#15267)
`isErasedType` counts `unknown` as erased by design (pinned TSO-U6), so the
`execute` / `explain` doors — which move onto the contract's own `unknown` —
cannot serve as predicate-4 evidence. The markers name the three doors that
move onto concrete shapes and state the rest in prose; the disposition is
identical for every door.
Claude-Session: https://claude.ai/code/session_01XTBcV7zZHmokdyQgXjbyEU
Co-authored-by: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
feat(driver-sql): the five remaining `IDataDriver` doors publish their honest types — the contract's own, not `any` (#15267)
6
+
7
+
**BREAKING** for TypeScript consumers — a published TYPE-surface narrowing, shipped as `minor` under the launch-window convention (the one PR #14434 set for the same class of change on `@objectstack/driver-memory`, and PR #15280 followed for `update()` on this very class). `SqlDriver` carried an EXPLICIT `Promise<any>` on five doors that `IDataDriver` had already declared narrower: `findOne()` (`Record<string, unknown> | null` — it has always answered `results[0] || null`), `create()` (`Record<string, unknown>`), `bulkCreate()` (`Record<string, unknown>[]`), `execute()` (`unknown`) and `explain()` (`unknown`). An explicit `any` satisfies all five structurally, so `tsc` said nothing while the emitted `.d.ts` told every consumer that `findOne()` never returns `null` and that `create()` returns whatever they like. #15280 un-masked `update()` and filed the census of what was left; this is that remainder.
8
+
9
+
Each door is now declared as the contract declares it. A caller that read fields off `findOne()` through the `any` now narrows the `null` arm first; a caller that leaned on `any` to read undeclared members off `create()` / `bulkCreate()`, or to dereference a raw `execute()` / `explain()` result, now types what it reads. No runtime behaviour changes.
10
+
11
+
`@objectstack/driver-sqlite-wasm` overrides none of these five and re-declares no member of its own, so it carries no entry: the narrowing reaches its consumers through this package's `.d.ts`. `@objectstack/driver-turso` overrides four of the five and carries its own entry.
12
+
13
+
Out of scope and deliberately unmoved: `analyzeQuery()` (not an `IDataDriver` member) and `aggregate()` keep their annotations.
14
+
15
+
<!-- adr-0087: not-required (type-surface-only packages/drivers/driver-sql/src/sql-driver.ts#findOne, packages/drivers/driver-sql/src/sql-driver.ts#create, packages/drivers/driver-sql/src/sql-driver.ts#bulkCreate) Published driver methods' declared returns move off an explicit `any` onto the contract's own shapes. No metadata key is removed, renamed or re-shaped, `packages/spec` is untouched, and nothing exists for `objectstack migrate meta`, `spec-changes.json` or the upgrade guide to rewrite; the obligation is a TypeScript narrowing at the consumer's own call site, delivered by the compiler. The same change to `execute` and `explain` is not named above because their destination is the contract's own `unknown`, which `isErasedType` counts as erased (TSO-U6), so predicate 4 cannot read them as narrowed-from-erased; they carry the identical disposition and the body states them in full. -->
feat(driver-turso): the overridden `IDataDriver` doors publish their honest types, not `any` (#15267)
6
+
7
+
**BREAKING** for TypeScript consumers — a published TYPE-surface narrowing, shipped as `minor` under the launch-window convention. `TursoDriver` does not merely inherit these doors from `SqlDriver` — it OVERRIDES `findOne()`, `create()`, `bulkCreate()` and `execute()`, and each override was written out with its own explicit `Promise<any>`. So this package's emitted `.d.ts` re-declared four of the five doors as `any` on its own and would NOT have picked up the `@objectstack/driver-sql` narrowing — the same shape PR #15280 had to fix separately for `update()`.
8
+
9
+
Both branches of every one of the four already answered the contract's type: the local branch forwards to `SqlDriver`'s door (narrowed alongside, #15267) and the remote branch passes `RemoteTransport`'s result — already declared `Record<string, unknown> | null`, `Record<string, unknown>`, `Record<string, unknown>[]` and `unknown` respectively — through the generic `formatRemoteRow` / `formatRemoteRows`. Each override now declares what it has always answered. A caller that read fields off `findOne()` through the `any` now narrows the `null` arm first. No runtime behaviour changes.
10
+
11
+
`explain()` is not overridden here and reaches these consumers through `@objectstack/driver-sql`. Out of scope and deliberately unmoved: `upsert()`, `aggregate()` and `beginTransaction()` keep their annotations.
12
+
13
+
<!-- adr-0087: not-required (type-surface-only packages/drivers/driver-turso/src/turso-driver.ts#findOne, packages/drivers/driver-turso/src/turso-driver.ts#create, packages/drivers/driver-turso/src/turso-driver.ts#bulkCreate) Published driver method overrides' declared returns move off an explicit `any` onto the contract's own shapes; no metadata key moves, `packages/spec` is untouched, and the obligation is a TypeScript narrowing at the consumer's own call site, delivered by the compiler. The same change to `execute` is not named above because its destination is the contract's own `unknown`, which `isErasedType` counts as erased (TSO-U6), so predicate 4 cannot read it as narrowed-from-erased; it carries the identical disposition and the body states it in full. -->
0 commit comments