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
5 changes: 4 additions & 1 deletion packages/rest/package.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,7 +17,9 @@
"build": "tsup --config ../../tsup.config.ts && node ../../scripts/check-dts-emitted.mjs",
"dev": "tsc -w",
"test": "vitest run",
"typecheck": "tsc --noEmit"
"check:test-typecheck": "tsx ../../scripts/check-test-typecheck.mts --self-test && tsx ../../scripts/check-test-typecheck.mts --package packages/rest --project tsconfig.test.json",
"gen:test-typecheck-debt": "tsx ../../scripts/check-test-typecheck.mts --update --package packages/rest --project tsconfig.test.json",
"typecheck": "tsc --noEmit && pnpm check:test-typecheck"
},
"dependencies": {
"@objectstack/core": "workspace:*",
Expand All@@ -40,6 +42,7 @@
"@objectstack/service-analytics": "workspace:*",
"@objectstack/service-datasource": "workspace:*",
"@types/node": "^26.2.0",
"tsx": "^4.23.12",
"typescript": "^6.0.3",
"vitest": "^4.1.10"
},
Expand Down
18 changes: 18 additions & 0 deletions packages/rest/test-typecheck-debt.json
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
{
"_comment": "Per-file tsc error debt of the @objectstack/rest TEST layer (#5286). `tsconfig.test.json` compiles `src/**/*.test.ts` — which `tsconfig.json` excludes and therefore no gate ever read — and every file below still carries errors from before that gate existed, almost all of them fixture literals annotated with a schema OUTPUT type (`z.infer`) while holding an authored INPUT literal. EXACT ratchet, judged by re-running tsc: a file that gains errors is red, a file that loses them is red until its number is re-recorded, a file that reaches zero is red until its entry is deleted, and a file NOT listed here may have no errors at all. Regenerate with: pnpm --filter @objectstack/rest gen:test-typecheck-debt",
"entries": {
"src/analytics-read-scope-refusal-envelope.test.ts": 1,
"src/export-integration.test.ts": 17,
"src/import-dryrun-parity.test.ts": 1,
"src/import-integration.test.ts": 3,
"src/import-job-integration.test.ts": 2,
"src/meta-public-book-grant.test.ts": 1,
"src/rest-batch-size-cap.test.ts": 1,
"src/rest-expected-error-logging.test.ts": 1,
"src/rest-meta-outage-vs-miss.test.ts": 1,
"src/rest-meta-save-receipt-envelope.test.ts": 3,
"src/rest-unclassified-fault-status.test.ts": 1,
"src/rest-write-response-formula.test.ts": 1,
"src/rest.test.ts": 4
}
}
77 changes: 77 additions & 0 deletions packages/rest/tsconfig.test.json
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
// The TEST-layer type-check program (#12542, adopting the mechanism #5286 set
// for `packages/spec` and #5449 generalised). `tsconfig.json` above stays as it
// is: it is the BUILD config, and its `**/*.test.ts` / `**/*.spec.ts` exclusion
// has a reason — ci.yml gates that no test file reaches the published artifact.
// 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 change is about.
//
// BEFORE THIS FILE, NO tsc PROGRAM COMPILED A SINGLE TEST FILE HERE. All 149 of
// them were named by the build config's `exclude`, and `typecheck` was
// `tsc --noEmit` against that very config — so `pnpm --filter @objectstack/rest
// typecheck` exiting 0 was a true sentence carrying no information about any
// test file in the package, and both `@ts-expect-error` directives in this
// layer (`src/rest.test.ts`, `src/rest-api-plugin-slot-lookups.test.ts`) were
// phantom checks that evaluated never. Under this program neither reports
// TS2578, so both are live and each is suppressing a real error.
//
// 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), and this package IS `"type": "module"`, so
// the build config's NodeNext compiles them as ESM too — and then demands
// explicit `.js` extensions on relative imports, which vitest does not.
// That one mismatch was 72 x TS2835 and, above each of them, the 49 x
// TS7006 they caused: an import that does not resolve makes every symbol it
// names `any`. `lib` inherits as ES2020 from the root config, which was 16 x
// TS2550 — all sixteen the same `Array.prototype.at` message. Those 137
// diagnostics were about the CHECK, never about the code. Matching vitest is
// fidelity, not laxity. No `DOM` in `lib`, unlike `packages/client`: 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"]` from
// `tsconfig.json`. Nothing here may loosen a type rule; if a test does not
// compile, that is the finding.
//
// ⚠️ WHAT THIS PROGRAM INHERITS FROM `tsconfig.json`, both of them load-bearing
// and neither of them re-declared here (read that file's #9960 comment first):
// - `rootDir: ".."`. Already widened to `packages/` there, as a CONSEQUENCE
// of the `paths` rule below rather than a preference. So the TS6059 pile
// that `packages/client`'s sibling config had to widen `rootDir` to clear
// does not arise here — it was paid for already, and this file changes
// nothing about it.
// - `paths: { "@objectstack/metadata-protocol": [".../src/index.ts"] }`. A
// child that declared its own `paths` would REPLACE this map rather than
// merge into it, silently sending that specifier back to `dist/`. This file
// declares no `paths` at all, so the rule stands and 22 of the producer's
// source files are in this program, exactly as they are in the build one.
//
// MEASURED at 5fbd58e0d, workspace closure built first: `--listFiles` puts 149
// `src/**/*.test.ts` in the program (489 files total), and the layer reports 37
// errors across 13 files — TS2554 x14 (wrong arity, all fourteen 'Expected 2-5
// arguments, but got 1'), TS18048 x13 (all in `src/export-integration.test.ts`),
// TS2345 x5, TS7006 x4, TS6133 x1. Every one of the 37 is pre-existing: this
// change edits no test file, and each error would have been reported on
// `origin/main` had this program always existed. They are ledgered per file in
// `test-typecheck-debt.json` beside this config, EXACT and shrink-only — a file
// that gains an error is red, one that loses one is red until re-recorded, one
// that reaches zero is red until its entry is deleted, and a file NOT listed
// there may have no errors at all.
//
// ⚠️ `src/rest.test.ts` (4 of the 37) is also held by PR #12421, and the two
// interact BY DESIGN. After that PR merges: an error it adds to that file reds
// the ledger on ITS run, and an error it removes reds the entry as stale on the
// next run. Both are the pin working, and both land on the change that caused
// them — but only if the next reader knows the coupling is there.
{
"extends": "./tsconfig.json",
"compilerOptions": {
"noEmit": true,
"module": "esnext",
"moduleResolution": "bundler",
"lib": ["ES2022"]
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
3 changes: 3 additions & 0 deletions pnpm-lock.yaml

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

78 changes: 20 additions & 58 deletions scripts/check-type-check-coverage.mjs
Original file line numberDiff line numberDiff line change
Expand Up@@ -746,6 +746,26 @@ const EXEMPT = {
// interchangeable only where the excluded tests import nothing the src layer
// does not, and that is a property to MEASURE per package, never to assume.
//
// `@objectstack/rest` GRADUATED from this ledger (#12542; entry: 155 raw,
// re-measured 37 raw across 13 files under the sibling program). It is worth a
// line because the gap between those two numbers is the whole argument for
// fixing the CONFIG before reading the residue, and this entry's own note had
// predicted it: 121 of the 155 were TS2835 plus the implicit-any pile it
// causes, and TS2550 x16 was one `Array.prototype.at` message against a `lib`
// older than es2022. Under `tsconfig.test.json`'s vitest-matching module
// semantics both classes go to ZERO -- TS2835 x72 -> 0, TS2550 x16 -> 0,
// TS7006 x49 -> 4 -- because this package is `"type": "module"`, so NodeNext
// was compiling extensionless relative imports as unresolvable ESM and every
// symbol they named was `any`. What is LEFT is a different shape from what the
// old tally described, and it grew in one place while collapsing in four:
// TS2554 x14 (unchanged to the unit), TS18048 x13 (a class the 155 never
// contained AT ALL -- 'possibly undefined' reads that only become visible once
// the imports above them resolve to real types), TS2345 x5, TS7006 x4,
// TS6133 x1 = 37. That is the #8612 lesson this ledger already carries,
// measured a second time: collapsing a cascade EXPOSES errors as well as
// removing them, and a note sized on the TS2835 line alone would have read as
// "155 minus 121 = 34" and been wrong in both directions.
//
// SINCE MEASURED, across this whole ledger rather than on that one package
// (#11491, at e47d5ef61, by dropping each entry's `"**/*.test.ts"` exclusion
// and reading `check:type-source-resolution`): 14 of the 18 entries that HAVE
Expand DownExpand Up@@ -872,64 +892,6 @@ const TEST_DEBT = {
+ 'RECORDED EXACTLY, no bootstrap margin: this layer has never been gated, so the first new error in '
+ 'it should go red rather than be absorbed.',
},
'@objectstack/rest': {
errors: 155,
note: 'RE-TALLIED from tsc at the 155 below (#10821), measured at 951b025e4 with the workspace closure '
+ 'built by the same command lint.yml runs before this gate, so the composition, the per-file split '
+ 'and the total are ONE measurement rather than a rescale: TS2835 x72 (NodeNext extensions), '
+ 'TS7006 x49, TS2550 x16 (`Array.prototype.at` against a `lib` older than es2022 -- all 16 are that '
+ 'one message), TS2554 x14 (wrong arity, all 14 \'Expected 2-5 arguments, but got 1\'), plus 4 '
+ 'singletons (TS2769 x2, TS2345, TS6133). 72+49+16+14+4 = 155, which is the recorded number '
+ 'EXACTLY -- this entry carries no remainder clause because it no longer needs one, and RECORDED '
+ 'still equals the measurement with no margin. '
+ 'WHAT THIS REPLACES, and why none of it was rescaled: the old tally read \'TS2835 x67, TS7006 x57, '
+ 'TS2554 x13, TS2550 x10 (composition as counted at 153; not re-tallied by class at the 155 '
+ 'below)\'. It summed to 147 with no clause for the remaining 6, so a reader could not tell an '
+ 'ABRIDGED tally from a SHORT one -- both readings were open and neither was checkable from the '
+ 'ledger. Both halves are settled here, and only one of them was ever answerable: (a) the 6 are NOT '
+ 'attributed and never will be. The per-class breakdown at e8db1a230 was not retained, so they are '
+ 'retired WITH the tally that carried them rather than invented, on this ledger\'s own rule that a '
+ 'made-up attribution is worse than an admitted gap. (b) 153-vs-155 was never one measurement '
+ 'disagreeing with itself: 153 was measured at e8db1a230, RECORDED was later lowered onto a '
+ 'DIFFERENT measurement (155 at 55da611, #6939), and the composition was never re-taken across that '
+ 'move -- which is precisely what the \'not re-tallied by class\' clause was saying out loud, and '
+ 'why it was honest rather than sloppy. Rescaling the old numbers onto 155 would have been wrong in '
+ 'any case, and the re-tally shows it: the shape moved in BOTH directions -- TS2835 67 -> 72, '
+ 'TS2550 10 -> 16 and TS2554 13 -> 14 rose while TS7006 57 -> 49 FELL -- and two classes the old '
+ 'tally never named (TS2769, TS2345) are in the pile now. '
+ 'Graduated from DEBT in #6905 -- this TEST_DEBT '
+ 'entry is now the sole gate on the package\'s test layer (src moved to `turbo run typecheck`). '
+ 'Measured '
+ '105 -> 136 (5ab08428) -> 143 (77adf29, hours later the same day) -> 153 (e8db1a230) -> 155 '
+ '(55da611) -> still 155 (951b025e4, the re-tally above). Read the '
+ 'top-of-ledger NodeNext note before sizing this one: TS2835 and the implicit-any pile it causes '
+ 'are 121 of the 155, and '
+ 'they are 72 IMPORT-LINE repairs, not 121: the 72 TS2835 sit on 72 distinct lines across 64 files '
+ 'but name only 11 distinct targets, and 49 of the 72 are the single `./rest-server` import. Every '
+ 'one of the 22 files carrying a TS7006 also carries a TS2835 -- no exceptions -- so there is no '
+ 'implicit-any in this layer without a broken import above it, and 31 of the 49 are one parameter '
+ 'name (`r`). Budget for the cli lesson (#8612) that collapsing a cascade can EXPOSE errors as well '
+ 'as remove them. Concentrated at the top and flat after: src/rest.test.ts x38 (TS7006 x21, '
+ 'TS2550 x11, TS2835 x4, TS2345 x1, TS6133 x1) and no other file above x5. '
+ 'Of the +10 that then bumped 153 to a bootstrap-margin RECORDED 163, '
+ '8 are attributable to three test files that '
+ 'window added -- rest-meta-save-receipt-envelope.test.ts x4 (#5265 / PR #5926), '
+ 'meta-item-envelope.test.ts x2 (#5563 / PR #5895), '
+ 'analytics-dataset-unlisted-refusal-envelope.test.ts x2; the remaining 2 landed in files that '
+ 'already existed and are NOT attributed further, because the pre-merge per-file counts were not '
+ 'retained and a made-up attribution is worse than an admitted gap. This is also the '
+ 'fastest-moving entry in either ledger, and it is '
+ 'the one that proved the gate works: #5278\'s own PR went red in CI on it, because a `pull_request` '
+ 'run builds the branch MERGED INTO main and three rest-touching PRs had landed since the sweep. A '
+ 'ledger number is always a number about a moment. RECORDED 163 stood as that bootstrap margin (+10 '
+ 'over 153 measured at e8db1a230 and re-confirmed at 153 an hour later at 77c7c884b) until #6939 '
+ '(a surplus finding filed right after the #6905 DEBT graduation) re-measured tsc at 155 -- an 8-error '
+ 'surplus the margin had been silently absorbing, and #7038 caught the note\'s "Also in DEBT." sentence '
+ 'going stale in the same graduation. Lowered 163 -> 155 at 55da611 (#6939, #7038): RECORDED now '
+ 'equals the exact measurement, no margin, so the next new error here goes red immediately -- a '
+ 'bootstrap margin can be re-established deliberately later if that slack is wanted again '
+ '(#5278 option A).',
},
'@objectstack/plugin-auth': {
errors: 97,
note: 'RE-TALLIED from tsc at the 97 below (#10615), measured at cea8c7d867 with the workspace closure '
Expand Down
30 changes: 28 additions & 2 deletions scripts/check-type-source-resolution.mjs
Original file line numberDiff line numberDiff line change
Expand Up@@ -319,9 +319,35 @@ const KNOWN_DIST_RESOLVED_TYPE_IMPORTS = {
'@objectstack/core', '@objectstack/formula', '@objectstack/metadata-core', '@objectstack/objectql',
'@objectstack/platform-objects', '@objectstack/spec', '@objectstack/types',
],
// #12542: `packages/rest` had NO tsc program compiling any of its 149 test
// files, and its new `tsconfig.test.json` (the #5286 sibling route) is the
// first one that does. The six deps after `@objectstack/core` here arrive
// from that program, exactly as `@objectstack/client`'s and
// `@objectstack/trigger-record-change`'s test-program deps do above.
//
// ⚠️ This is a program-set widening and its numbers are stated, per this
// registry's own rule: before, at 5fbd58e0d, `--list` reported 93 programs /
// 77 packages, 54 entries, 233 pairs; after, 94 programs / 77 packages, 54
// entries, 239 pairs. +1 program, +0 entries, +6 pairs, all six in this one
// package and all six reached only through `tsconfig.test.json`.
//
// Why the entry and not `paths` rules, which is what this gate's failure text
// asks for: MEASURED both ways on the same checkout. `paths` redirecting
// these six to source puts their `src` trees in the program and takes the
// test layer from 37 errors to 42 — the +5 being TS6133 in
// `../plugins/plugin-hono-server/src/{hono-plugin,current-user-endpoints}.ts`
// and `../drivers/driver-sql/src/sql-driver.ts`, i.e. OTHER packages' source
// billed to `packages/rest/test-typecheck-debt.json`, where they would then
// red on those packages' PRs. It would also make the type program diverge
// from the runtime one: `packages/rest/vitest.config.ts` aliases exactly two
// of the six (`plugin-hono-server`, `service-datasource`) to source and
// resolves the other four through `dist/`, so blanket `paths` here is not
// fidelity to vitest either.
'@objectstack/rest': [
'@objectstack/core', '@objectstack/metadata-core', '@objectstack/objectql',
'@objectstack/observability', '@objectstack/platform-objects', '@objectstack/service-package',
'@objectstack/core', '@objectstack/driver-sql', '@objectstack/metadata',
'@objectstack/metadata-core', '@objectstack/objectql', '@objectstack/observability',
'@objectstack/platform-objects', '@objectstack/plugin-hono-server', '@objectstack/plugin-security',
'@objectstack/service-analytics', '@objectstack/service-datasource', '@objectstack/service-package',
'@objectstack/spec', '@objectstack/types',
],
'@objectstack/runtime': [
Expand Down
Loading