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
57 changes: 57 additions & 0 deletions .changeset/driver-mongodb-test-tsc-program.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
---
"@objectstack/driver-mongodb": patch
---

fix(driver-mongodb): put the test layer in front of tsc, so the package's own typecheck reports a PASS and not a NUMBER (#14917)

`packages/drivers/driver-mongodb`'s `tsconfig.json` excluded `**/*.test.ts`, and
its `typecheck` script is `tsc --noEmit` against that very config. Measured at
`6ed4b811af` with the dependency closure built: that program admits **0** of the
package's 30 `src/**/*.test.ts` files while all **10** of its non-test `src/**`
files ARE there, so `pnpm --filter @objectstack/driver-mongodb typecheck`
exiting 0 was a true sentence carrying no information about any test file.

The filing's headline — that a compile-time `Equals` / `IsAny` pin here is
"checked by nothing" — is **false**, and the correction on the card is right: a
second program does compile these files. `check-type-check-coverage.mjs`'s
`remeasureProject` drops only the test glob and compares the result against its
`TEST_DEBT` ledger. Confirmed here by ablation rather than argued: a
deliberately false `Equals` pin added to `mongodb-driver.test.ts` takes that
program from 10 errors to 11, above the ledger's recorded 10, which reddens it.
The pins were never phantoms. What was true is narrower, and is what this change
closes: the only program reading this layer was a **debt ratchet** — an
instrument that reports a number and fails when the number moves, not a gate
that reports a pass.

Gives the package the #5286 sibling shape (`packages/rest`, `runtime`,
`objectql`, `core`): a `tsconfig.test.json` with module semantics only —
`esnext` / `bundler` / `lib: ES2022`, matching how vitest actually executes
these files — strictness inherited and untouched, named by the `typecheck`
script via `check:test-typecheck`.

Measured: **10** errors under the ratchet's shape (matching its recorded number,
and its recorded composition `TS1309 x7, TS2550 x3`, class for class), and **0**
under the split. All 10 were config-tier in full — 7 `TS1309` (`await` at module
scope in a program NodeNext compiles as CJS, because this package has no `"type":
"module"`) and 3 `TS2550` (`Array.prototype.at` against a `lib` older than
es2022). Neither class says anything about a test, and nothing was exposed
behind them: there was no unresolved-import cascade here to collapse, so there
is no `+n` term. `noUnusedLocals` / `noUnusedParameters` are live for this
package (unlike `driver-turso`, which switches both off) and neither fires.

The `TEST_DEBT` entry (10 errors) is **deleted**, not lowered — the graduation
this ratchet's invariant requires. No `test-typecheck-debt.json` is added:
residue is 0, so none is owed (#5286, maintainer-only to open). That leaves all
30 files unledgered, so any error any one of them gains is red on arrival.

`check:type-source-resolution` went red from onboarding the new program (the
documented onboarding-limb case, #11490): a registry entry is added rather than
`paths`, with its numbers stated in place — 123 tsc programs / 309 pairs before,
124 / 310 after. The single new pair is `@objectstack/objectql`, a devDependency
that no non-test file in `src/` imports.

No runtime code changes: not one test file and not one source file is edited, so
no shipped behaviour moves — the suite reports the same 552 passed / 147 skipped
across 30 files as before. The `patch` level reflects the published
`package.json` gaining `typecheck` / `check:test-typecheck` scripts and a `tsx`
devDependency.
4 changes: 3 additions & 1 deletion packages/drivers/driver-mongodb/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
"build": "tsup --config ../../../tsup.config.ts && node ../../../scripts/check-dts-emitted.mjs",
"dev": "tsc -w",
"test": "vitest run",
"typecheck": "tsc --noEmit"
"typecheck": "tsc --noEmit && pnpm check:test-typecheck",
"check:test-typecheck": "tsx ../../../scripts/check-test-typecheck.mts --self-test && tsx ../../../scripts/check-test-typecheck.mts --package packages/drivers/driver-mongodb --project tsconfig.test.json"
},
"dependencies": {
"@objectstack/core": "workspace:*",
Expand All @@ -29,6 +30,7 @@
"@objectstack/objectql": "workspace:*",
"@types/node": "^26.2.0",
"mongodb-memory-server": "^11.2.0",
"tsx": "^4.23.12",
"typescript": "^6.0.3",
"vitest": "^4.1.10"
},
Expand Down
91 changes: 91 additions & 0 deletions packages/drivers/driver-mongodb/tsconfig.test.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// The TEST-layer type-check program (#14917), adopting the mechanism #5286 set
// for `packages/spec` and #5449 generalised — the route `packages/objectql`
// (#13676), `packages/runtime` (#14504) and `packages/core` (#14613) 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
// NAMES it (via `check:test-typecheck --project`), because a config no script
// invokes is exactly the phantom this whole mechanism is about.
//
// BEFORE THIS FILE, NO tsc PROGRAM COMPILED A SINGLE TEST FILE HERE, and that
// is measured rather than read off the config. At 6ed4b811af with the
// dependency closure built first, `tsc --noEmit --listFiles -p tsconfig.json`
// puts **0** of this package's 30 `src/**/*.test.ts` files in the program —
// while all **10** of its non-test `src/**` files ARE there, so the zero is the
// `exclude` line and not a probe that sees nothing. Under this file the count is
// **30 of 30**, with the same 10 non-test files beside them.
// `pnpm --filter @objectstack/driver-mongodb typecheck` exiting 0 was a true
// sentence carrying no information about any test file in this package.
//
// ⚠️ WHAT THE CARD SAID, AND WHAT WAS ACTUALLY WRONG. The filing's headline —
// that a compile-time `Equals` / `IsAny` pin here is "checked by nothing" — is
// 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
// 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
// instrument that reports a NUMBER and fails when the number MOVES, not a gate
// that reports a pass.
//
// What differs from the build config, and what deliberately does NOT:
// - module semantics ONLY, plus `lib`. The tests are written and executed as
// ESM by vitest (esbuild/vite), while this package has no `"type":
// "module"`, so the build config's NodeNext compiles them as CJS. Measured
// cost of that mismatch here: ALL 10 of the raw diagnostics — TS1309 x7
// ("cannot use `await` at the top level" in a CJS program, one per suite
// that awaits `startMongod()` at module scope) and TS2550 x3 (all three the
// same `Array.prototype.at` message in `mongodb-findone-options.test.ts`,
// against a `lib` older than es2022). Those 10 are about the CHECK, never
// about the code. Matching vitest is fidelity, not laxity. No `DOM` in
// `lib`: nothing in this layer touches a browser global.
// - ⛔ STRICTNESS IS UNTOUCHED. `strict`, `noUnusedLocals`,
// `noUnusedParameters`, `noImplicitReturns`, `noFallthroughCasesInSwitch`
// are inherited from the root config, and `types: ["node"]` restates
// `tsconfig.json`'s — load-bearing for the same reason its comment gives,
// since the `.test.ts` files call the `setTimeout` / `console` users in
// `src/test-mongod.ts` and use those globals themselves. Nothing here may
// loosen a type rule; if a test does not compile, that is the finding.
// ⛔ Not one `any` and not one `@ts-expect-error` was added to any test file
// to open this gate — that shape is what turns a real gate into a phantom.
// - ⚠️ `noUnusedLocals` / `noUnusedParameters` are worth naming explicitly:
// they are `true` at the root and `false` in `driver-turso`'s own overrides
// but NOT in this package's, so this layer meets STRICTER settings than the
// sibling driver whose clean state might otherwise be read as a prediction.
// Measured: neither fires here, in either direction. Zero of the 10.
// - `rootDir` is INHERITED as `./src` and deliberately not widened. Like
// `packages/runtime` and unlike `objectql` / `spec`, this layer produces no
// TS6059: measured, every file this program admits is already under `src`
// (all 30 test files sit beside the sources they exercise). Widening it "to
// be safe" would admit files the build config does not and change what the
// gate judges, so it stays as the build config has it.
//
// ⭐ MEASURED at 6ed4b811af, dependency closure built first (an error count
// taken against an unbuilt closure is not a reading — unresolved-import
// cascades inflate it): this program reports **0 errors across 30 files**, from
// a raw 10 under the inherited NodeNext semantics. Every one of the 10 was
// config-tier and none survives; the residue is EMPTY. So unlike `objectql`,
// `runtime`, `spec` and `core`, this package needs **no**
// `test-typecheck-debt.json` at all, and carries none — which is the strongest
// form of this gate: all 30 files are unledgered, so any error any one of them
// ever gains is red on arrival, starting today.
//
// That empty residue is also why the `TEST_DEBT` entry in
// `scripts/check-type-check-coverage.mjs` GRADUATES in the same change rather
// than being paid down. Its recorded 10 and its recorded composition
// ("TS1309 x7, TS2550 x3") match this file's raw reading exactly, which is the
// cleanest possible confirmation that the ledger was measuring the CHECK's
// misconfiguration and never a defect in the tests.
{
"extends": "./tsconfig.json",
"compilerOptions": {
"noEmit": true,
"module": "esnext",
"moduleResolution": "bundler",
"lib": ["ES2022"],
"types": ["node"]
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
60 changes: 59 additions & 1 deletion pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

57 changes: 42 additions & 15 deletions scripts/check-type-check-coverage.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -1026,6 +1026,48 @@ const TEST_DEBT = {
// Measured: with `rootDir` inherited, the same program reports 116 additional
// TS6059 — a config-tier pile that says nothing about any test.

// ── #14917: `@objectstack/driver-mongodb` GRADUATED — and this one really is
// at ZERO, which the six above are not ────────────────────────────────────────
//
// `driver-mongodb` (10) left this ledger on 2026-09-04, by the same route as
// `runtime`, `cli`, `lint` and the three plugins: it now has a
// `tsconfig.test.json` its `typecheck` script NAMES, so `hidesTests` is false
// for it and this gate's per-PACKAGE approximation has nothing left to
// approximate. ⛔ This change repairs no test file and edits none.
//
// ⭐ WHAT IS DIFFERENT HERE, and it is the whole reading: those six graduated
// into a per-file `test-typecheck-debt.json` holding a residue. This one
// graduates into NO LEDGER AT ALL. Measured at 6ed4b811af with the dependency
// closure built, `tsc --noEmit -p tsconfig.test.json` reports **0 errors across
// all 30 test files**. The recorded 10 was config-tier IN FULL — the same
// dissolution measured for `lint`'s 16, but complete rather than partial.
//
// The attribution has no remainder in either direction. RECORDED here was
// `TS1309 x7, TS2550 x3`; re-measured on the way out at 6ed4b811af by dropping
// only the test glob (this gate's own `remeasureProject` shape), the raw
// program reports those same 10, class for class and file for file. All 7
// TS1309 are "cannot use `await` at the top level" — this package has no
// `"type": "module"`, so NodeNext compiles as CJS the suites that await
// `startMongod()` at module scope, which vitest executes as ESM. All 3 TS2550
// are the same `Array.prototype.at` message in `mongodb-findone-options.test.ts`
// against a `lib` older than es2022. Both classes are the CHECK being
// misconfigured; neither is a defect in a test. Under vitest-matching module
// semantics both dissolve and NOTHING is exposed behind them — there is no
// `+n` term here, because there was no unresolved-import cascade to collapse.
//
// ⚠️ So the shrink-only guarantee did not merely move to a finer instrument
// here, it STRENGTHENED twice over. All 30 files are unledgered, so any error
// any one of them gains is red on arrival. And what the package's own
// `typecheck` script reports about its test layer is now a PASS rather than a
// NUMBER — which was this card's actual finding, once its false headline (that
// the `Equals` / `IsAny` pins here were phantoms) had been corrected on the
// thread. The pins were always real; they were read by THIS ratchet, and a
// ratchet fails when a number moves, not when a type is wrong.
//
// ⚠️ PINS_CHECKED reported nothing for this package in either direction and
// still does not: measured on the way out, its test layer holds ZERO
// `@ts-expect-error` directives, so that half had no subject here either.

'@objectstack/mcp': {
errors: 53,
note: 'TS18046 x51 -- `json` is of type unknown, one `await res.json()` idiom repeated across four '
Expand Down Expand Up @@ -1056,21 +1098,6 @@ const TEST_DEBT = {
+ 're-confirmed at 53 at 62b2655d8, so the next new error in this package goes red on arrival -- '
+ 're-establishing a margin deliberately remains a maintainer call (#5278 option A).',
},
'@objectstack/driver-mongodb': {
errors: 10,
note: 'TS1309 x7, TS2550 x3. Was 43 (TS2345 x33 + these 10), measured at 5ab08428 and still exactly '
+ '43 at d367f03d6^ -- the commit immediately before PR #6210. That PR (#6075) narrowed this '
+ "driver's six IDataDriver query methods to `DriverQuery`, which is what retired all 33 TS2345: "
+ "they were this package's OWN test literals failing `Property 'object' is missing in type` "
+ 'against a `QueryAST` that still required it. The ledger was never ratcheted down, so 33 errors '
+ 'of slack sat here. #6212 batch C lowers it to the measured 10 because that slack made the batch '
+ "OWN change unpinnable: this package's tsconfig excludes `*.test.ts`, so `pnpm typecheck` cannot "
+ "see `aggregate`'s narrowing at all, and its only consumers are those excluded tests. Reverting "
+ '`aggregate(object, query: DriverQuery)` back to `QueryAST` measures 12 here -- which the old '
+ '43-ceiling would have swallowed in silence. At 10 it goes red, which is the whole point of a '
+ 'ratchet. Re-measured 10 at 2bc187641, and the pristine tree at that commit reports the same 10, '
+ "so none of the -33 is this PR's doing.",
},
'@objectstack/formula': {
errors: 17,
note: 'TS2591 x6 (`process`), TS2345 x3, TS2352 x3, TS1470 x2, TS2339 x2, TS2739 x1. Re-measured 17 '
Expand Down
Loading
Loading