Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .changeset/20595-driver-mongodb-provenance-anchors.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
'@objectstack/driver-mongodb': patch
---

Provenance comments in `@objectstack/driver-mongodb` cite the commits that decided them, not tracker numbers that no longer resolve

Clause-②: no

Docblocks and comments across the package cited issue-tracker numbers that now answer 404 on GitHub.
Each one now cites the commit in this repository's history that made the decision it describes. One of
these docblocks sits on an exported member (`MongoDBDriver.update()`), so the reworded text appears in
the published `index.d.ts` / `index.d.mts`; that docblock and one more comment esbuild keeps appear in
the JavaScript output (`index.js` / `index.mjs`); the sourcemaps do not change.

Comment only: no export, type, error code, status, message text or runtime behaviour changes.
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@
* ## The ruling this suite pins
*
* - **`sum` / `avg` answer arithmetic** — `3` / `0.5` over a 3-true/3-false
* fixture. The #11065 family shape, landed on `driver-memory` and on every
* fixture. The commit 20950404c family shape, landed on `driver-memory` and on every
* SQL dialect (#11635).
* - **`min` / `max` answer `0` / `1`** — #11152 (maintainer 2026-08-28,
* applied on that card's comment 5448627494, ruling verbatim and
* landed as commit f6fa22ce1, ruling verbatim and
* untranslated: 「12745 A回,其他同意。」), SUPERSEDING #11249's
* `false` / `true`: booleans aggregate as NUMBERS on every face, with no
* per-aggregate exception, so one boolean column's aggregates answer in one
Expand Down
4 changes: 2 additions & 2 deletions packages/drivers/driver-mongodb/src/mongodb-aggregation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -646,15 +646,15 @@ export function buildAggregationPipeline(opts: {
* `$sum`'s identity `0` and averaged to `null` here, while `SUM(col)` /
* `AVG(col)` answer `3` / `0.5` over the same 3-true/3-false rows on every SQL
* dialect (#11635), `driver-memory` answers those numbers on both of its faces
* (#11065), and objectql's in-memory fallback answers them too because its
* (commit 20950404c), and objectql's in-memory fallback answers them too because its
* `toNumber` is `Number(v)` and `Number(true) === 1`. A rate measure over a
* flag column — an SLA-violation rate, a win rate — is the ordinary shape of
* that query, and the two answers are not two spellings of one: a dashboard
* tile bound to the measure renders a percentage under SQL and a blank here,
* indistinguishable from "no matching rows". `sum`'s `0` is the worse half,
* being a plausible number rather than a visible hole.
*
* The expression is the one #11065 landed on `driver-memory`'s analytics face
* The expression is the one commit 20950404c landed on `driver-memory`'s analytics face
* (`memory-analytics.ts`, `numericAggregandExpr`), reproduced rather than
* imported: this driver shares no line of code with that one, and the shared
* contract between them is the VALUES in `@objectstack/spec/data`, not a
Expand Down
2 changes: 1 addition & 1 deletion packages/drivers/driver-mongodb/src/mongodb-driver.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ describe.skipIf(!sharedMongod)('MongoDBDriver', () => {
it('should update a record and return updated data', async () => {
await driver.create('task', { id: 'upd-1', title: 'Original', status: 'new' });
const result = await driver.update('task', 'upd-1', { title: 'Updated', status: 'done' });
// `update()` declares `Record<string, unknown> | null` (#14428): a miss
// `update()` declares `Record<string, unknown> | null` (commit ca3fd4b1a): a miss
// answers `null`. This case is the FOUND arm, so pin that first and read
// the fields through it -- same idiom as `findOne` above.
expect(result).not.toBeNull();
Expand Down
2 changes: 1 addition & 1 deletion packages/drivers/driver-mongodb/src/mongodb-driver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -429,7 +429,7 @@ export class MongoDBDriver implements IDataDriver {
}

/**
* [#14428] A miss answers `null` — the arm `IDataDriver.update()` declares
* [commit ca3fd4b1a] A miss answers `null` — the arm `IDataDriver.update()` declares
* (#13878) and the one `InMemoryDriver`, `SqlDriver`, `SqliteWasmDriver` and
* `TursoDriver`'s local face already return.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.

/**
* [#13195] What `translateFilter` emits for `$exists`, and what MongoDB makes
* [commit 9dac1ae01] What `translateFilter` emits for `$exists`, and what MongoDB makes
* of it on a row with NO VALUE — measured, not read.
*
* ## The ruling, and why this driver was thought to be the hard half
Expand Down Expand Up @@ -93,7 +93,7 @@ function matchMongoDoc(row: Record<string, unknown>, doc: Record<string, unknown
if ((cond as Array<Record<string, unknown>>).some((b) => matchMongoDoc(row, b))) return false;
continue;
}
// [#13195] Modelled because the emitter now produces it: a lowered
// [commit 9dac1ae01] Modelled because the emitter now produces it: a lowered
// `$exists` whose key is already taken by a sibling operator is promoted to
// its own branch rather than merged over the sibling.
if (field === '$and') {
Expand Down Expand Up @@ -203,7 +203,7 @@ describe('[#13195] `$exists` translation and its answer on a no-value row', () =
});

/**
* [#13195] `$exists` SHARING a field constraint with another operator.
* [commit 9dac1ae01] `$exists` SHARING a field constraint with another operator.
*
* Not a cell the card or the ruling names — it is a consequence of the
* prescribed lowering, found by measuring it. `{$ne: null}` / `{$eq: null}`
Expand Down
2 changes: 1 addition & 1 deletion packages/drivers/driver-mongodb/src/mongodb-filter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ describe('MongoDB Filter Translator', () => {
});

it('translates $exists to the nullness test — has-value, not key-presence', () => {
// [#13195, ruled 2026-08-30] Was `{avatar: {$exists: true}}`, a
// [commit 9dac1ae01, ruled 2026-08-30] Was `{avatar: {$exists: true}}`, a
// passthrough, which is key-presence at the wire level. `$exists` means
// HAS A VALUE (`!= null`) — #5298 leg 3 / #5369 — and the lowering is the
// one the `$null` arm in the same file already emits.
Expand Down
12 changes: 6 additions & 6 deletions packages/drivers/driver-mongodb/src/mongodb-filter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1037,7 +1037,7 @@ function translateCondition(
// sibling operator on the same field. Merging one would drop a
// constraint silently, so each becomes its own `$and` branch — see
// `assembleLoweredWrites()`. This consumed a single `_presenceAnd`
// when the guard covered `$exists` alone (#13195); it is a LIST now
// when the guard covered `$exists` alone (commit 9dac1ae01); it is a LIST now
// because the class has several members and one field constraint
// can contest more than one key.
const extraAnd = translated._extraAnd as Record<string, unknown>[] | undefined;
Expand Down Expand Up @@ -1077,7 +1077,7 @@ function translateCondition(
* Read straight off the spec's `FILTER_OPERATORS` declaration order rather than
* hand-copied, so a seventeenth operator is ranked the day it is declared. The
* rank of `$exists` (last in that list) is what makes this generalisation emit,
* byte for byte, the documents #13195's guard already emits for the one
* byte for byte, the documents commit 9dac1ae01's guard already emits for the one
* operator it moved. `$like` / `$ilike` are declared but NOT translated by this
* driver — the `default:` arm refuses them before the assembly runs — so the
* fallback below is a totality floor, never a live path.
Expand Down Expand Up @@ -1146,7 +1146,7 @@ interface LoweredWrite {
*
* Free key → merge inline (the overwhelmingly common case). Taken key → the
* write becomes its own `$and` branch on the same field, where both constraints
* survive. That is exactly the guard #13195 landed for `$exists` alone,
* survive. That is exactly the guard commit 9dac1ae01 landed for `$exists` alone,
* generalised to every writer rather than restated once per operator.
* `driver-memory`'s reference matcher looped the operators and therefore could
* not express this defect at all; it was the oracle both drivers agreed with
Expand Down Expand Up @@ -1239,7 +1239,7 @@ function translateFieldOperators(
* {@link assembleLoweredWrites} after the loop. Collected rather than
* assigned because an arm cannot know whether the key it wants is already
* spoken for by a sibling operator the author wrote LATER — which is the
* whole of the defect this replaces. It subsumes #13195's single-operator
* whole of the defect this replaces. It subsumes commit 9dac1ae01's single-operator
* `presence` collection: `$exists` is one writer among the rest now.
*/
const writes: LoweredWrite[] = [];
Expand All @@ -1262,7 +1262,7 @@ function translateFieldOperators(
put(op, store(value));
break;

// [#13195] Value-independent — a presence predicate takes a boolean, not
// [commit 9dac1ae01] Value-independent — a presence predicate takes a boolean, not
// a comparand, so it is never coerced. And "present" means the field HAS
// A VALUE (`!= null`), never key presence: #5298 leg 3 / #5369, landed in
// PR #5962, ruled onto this driver by the maintainer on 2026-08-30.
Expand Down Expand Up @@ -1473,7 +1473,7 @@ function translateFieldOperators(

// [#13524] Assemble every lowered write, and do NOT let one clobber another.
//
// #13195 landed this rule for `$exists` alone and said in this spot that the
// Commit 9dac1ae01 landed this rule for `$exists` alone and said in this spot that the
// identical clobber was reachable through `$null` and `$between`. Enumerating
// the declared vocabulary instead of the noticed operators found the whole
// `$regex` string family too, which `driver-memory` had promoted for years
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ describe('[#5347] driver-mongodb refuses a non-boolean $null comparand', () => {
expect(translateFilter({ stage: 'won' })).toEqual({ stage: 'won' });
expect(translateFilter({ stage: { $in: ['won'] } })).toEqual({ stage: { $in: ['won'] } });
expect(translateFilter({ score: { $between: [1, 2] } })).toEqual({ score: { $gte: 1, $lte: 2 } });
// [#13195, ruled 2026-08-30] `$exists` lowers to the nullness test now —
// [commit 9dac1ae01, ruled 2026-08-30] `$exists` lowers to the nullness test now —
// has-value, not key-presence. The point of this line is unchanged: the
// operator is still ACCEPTED here, and only `$null`'s comparand is refused.
expect(translateFilter({ stage: { $exists: true } })).toEqual({ stage: { $ne: null } });
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.

/**
* [#14428] `MongoDBDriver.update()` answers a missing id with `null`, not with
* [commit ca3fd4b1a] `MongoDBDriver.update()` answers a missing id with `null`, not with
* a record it made up.
*
* # What was broken
Expand All @@ -13,7 +13,7 @@
* `updateOne({ id })` matching nothing and `findOne({ id })` coming back `null`
* still produced a row — the caller's own payload plus the `updated_at` this
* driver had just stamped, under an id that names no document. Since #13878
* (PR #14434) `IDataDriver.update()` declares `Promise[Record[string, unknown]
* (commit 93940d492) `IDataDriver.update()` declares `Promise[Record[string, unknown]
* | null]`, so "a row for an id that does not exist" is no longer a way of
* satisfying the declaration: it is a value the declaration distinguishes from.
* Four of six shipped implementations already answered `null`; this one and
Expand Down Expand Up @@ -69,7 +69,7 @@
* only `node_modules`/`dist`, lists 43). vitest transpiles without
* typechecking, and the root `tsconfig.json` excludes `packages` entirely,
* so neither of those picks it up either. That exclusion is itself a filed
* defect (#14917), not a design.
* defect (closed by commit a06faebbe), not a design.
* - **`pnpm check:type-check-debt` DOES compile it.** The ratchet's
* `--re-measure` leg generates a project that drops the test exclusion and
* runs `tsc` over this package with its tests un-hidden, then compares the
Expand Down
6 changes: 3 additions & 3 deletions packages/drivers/driver-mongodb/tsconfig.test.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// The TEST-layer type-check program (#14917), adopting the mechanism #5286 set
// The TEST-layer type-check program (commit a06faebbe), adopting the mechanism #5286 set
// for `packages/spec` and #5449 generalised — the route `packages/objectql`
// (#13676), `packages/runtime` (#14504) and `packages/core` (#14613) already
// (#13676), `packages/runtime` (#14504) and `packages/core` (commit 81208086a) already
// run. `tsconfig.json` beside this file stays exactly as it is: it is the BUILD
// config, and its `**/*.test.ts` exclusion has a reason. This sibling puts the
// excluded layer back in front of tsc, and `package.json`'s `typecheck` script
Expand All @@ -22,7 +22,7 @@
// FALSE, and the correction on the card is right: a second program does compile
// these files. `scripts/check-type-check-coverage.mjs`'s `remeasureProject`
// extends this package's tsconfig, drops only the test glob, and compares the
// result against its `TEST_DEBT` ledger. That is how CI caught PR #14914's
// result against its `TEST_DEBT` ledger. That is how CI caught commit ca3fd4b1a's
// three TS18047 errors, which this package's own `typecheck` could not see. So
// the pins were not phantoms. What was true is narrower and is what this file
// closes: the only program reading this layer was a DEBT RATCHET — an
Expand Down
Loading