|
| 1 | +--- |
| 2 | +"@objectstack/objectql": patch |
| 3 | +--- |
| 4 | + |
| 5 | +fix(engine): `ObjectRepository` declares the `findOne` / `update` shapes it already published (#16786) |
| 6 | + |
| 7 | +`ObjectRepository.findOne` and `.update` declared `Promise<any>` and now declare |
| 8 | +what `IScopedObjectRepository` — the contract this class carries an `implements` |
| 9 | +clause for — has declared since #16231's ruling A landed (PR #16783): |
| 10 | + |
| 11 | +- `findOne` → `Promise<Record<string, any> | null>` |
| 12 | +- `update` → `Promise<Record<string, any> | number | null>` |
| 13 | + |
| 14 | +**Why this is a `patch` and not a `minor`.** Nothing is widened and no symbol is |
| 15 | +added. `packages/spec/src/contracts/scoped-context.ts` already publishes the |
| 16 | +narrower type, and `IDataEngine` — the call each of these two methods forwards to, |
| 17 | +one line down — already publishes it too. This class sat between two narrow |
| 18 | +declarations and re-widened the result back to `any` on the way out. `implements` |
| 19 | +does not catch that, because a WIDER declared return always satisfies a narrower |
| 20 | +one: `class ObjectRepository implements IScopedObjectRepository` compiled green |
| 21 | +the whole time while the members it published were `any`. So this is an |
| 22 | +implementation coming back to the declaration it had already published — the |
| 23 | +repo's `patch` rung — and not a contract that moved. The recorded **WHICH LEVEL** |
| 24 | +maintainer ruling of 2026-09-04 (decision batch #35, on #15294, recorded at |
| 25 | +`.github/workflows/pr-automation.yml`) puts *additive widening* of a published |
| 26 | +surface — a new exported symbol, a new accepted key or value — at `minor`; this |
| 27 | +PR does none of those, and adds no exported symbol. |
| 28 | + |
| 29 | +**Who has to change something, on the TYPE axis.** A TypeScript consumer that |
| 30 | +typed against the concrete `ObjectRepository` / `ScopedContext` class — rather |
| 31 | +than the `IScopedObjectRepository` contract, which already said this — and reads |
| 32 | +a field off `findOne`'s result without a null check, or off `update`'s result |
| 33 | +without separating the by-id record from the predicate-form count. Those call |
| 34 | +sites were reading `any`; they now read the declared shape and the compiler asks |
| 35 | +for the null check. Consumers already written against the contract, including |
| 36 | +every hook whose `ctx` is typed `HookContext` (`HookContext.api` has been |
| 37 | +`IScopedContext` since #5945), see no change: they were already narrow. |
| 38 | + |
| 39 | +The in-repo census for this change was one file, repaired here. |
0 commit comments