From d34b6ebd26f946aaa25661865953527ef2b7878e Mon Sep 17 00:00:00 2001 From: os-zhuang Date: Wed, 5 Aug 2026 16:52:51 +0000 Subject: [PATCH] fix(client): compile the client test layer so its `@ts-expect-error` stops being a phantom check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `packages/client/tsconfig.json` excludes `**/*.test.ts` and the package's `typecheck` script was a bare `tsc --noEmit` reading that same config, so no gate anywhere read a client test file with a type checker. The one `@ts-expect-error` in the package — `client.test.ts:1283` — evaluated never: on `origin/main`, deleting the directive line left `pnpm --filter @objectstack/client typecheck` just as green as leaving it there. Compiled for the first time, that directive reports TS2578 "unused". It never had anything to suppress: `project(environmentId: string)` accepts `''`, which is a perfectly good `string`, and the directive's own comment already said what the test proves — the empty id is rejected at RUNTIME. So the repair is to delete the directive, not to keep it: the reverse verification is the mirror of the usual one, and RESTORING the line is what now goes red. - `packages/client/tsconfig.test.json`: a sibling of the build config (which keeps its exclusion — ci.yml gates that no test file reaches the published artifact) with vitest's module semantics (`module: esnext`, `moduleResolution: bundler`, ES2022 lib) and `rootDir` widened to the workspace root, since four test files deep-import sibling packages' route ledgers. Strictness flags are inherited, untouched. - 13 errors surfaced; eight were the tests' own and are fixed here — two unused imports, an unused parameter, two possibly-undefined reads on an optional `routes` map, an `unknown` payload now asserted with `toMatchObject` instead of cast, a `reference_to` key the field schema never had (the lookup declared no target at all), and the phantom pin. Re-spelling that key uncovered one more of the remaining kind; the six that stay are one producer-side defect (#5543) held per file in `test-typecheck-debt.json`, EXACT and shrink-only. - `scripts/check-test-typecheck.mts`: PROMOTED from `packages/spec/scripts/`, parameterized with `--package`, so client onboards by wiring its `typecheck` script rather than by copying 300 lines. spec's ledger and its 79 files / 691 errors are unchanged, and the generated `_comment` is byte-identical. - Both graduations the gates force: `@objectstack/client` leaves TEST_DEBT (its stale entry measured 15 files / 19 errors, five of them the inherited-rootDir TS6059 that were the check's own misconfiguration) and its `PHANTOM_PIN_DEBT` seed — the entry #5478 left addressed to this issue — is deleted. Fixes #5449 Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_016FNvXhtSdnEGEfLEsMmvxh --- AGENTS.md | 14 ++- packages/client/package.json | 5 +- .../src/client.batch-transaction.test.ts | 5 +- packages/client/src/client.hono.test.ts | 9 +- packages/client/src/client.test.ts | 19 +++- packages/client/test-typecheck-debt.json | 8 ++ packages/client/tsconfig.test.json | 62 +++++++++++ packages/spec/package.json | 4 +- pnpm-lock.yaml | 3 + .../check-test-typecheck.mts | 100 ++++++++++++------ scripts/check-type-check-coverage.mjs | 3 - 11 files changed, 180 insertions(+), 52 deletions(-) create mode 100644 packages/client/test-typecheck-debt.json create mode 100644 packages/client/tsconfig.test.json rename {packages/spec/scripts => scripts}/check-test-typecheck.mts (75%) diff --git a/AGENTS.md b/AGENTS.md index c95b6f1f0d..beb9349a76 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -46,11 +46,15 @@ never its own *strictness*: `strict` and friends are inherited, untouched. "tsc is the best sweeper" channel the spec-property-retirement playbook leans on: the directive is meant to go red the day a removed key comes back. Outside a program it evaluates never, and *deleting the directive leaves every gate just as green* — which is -how spec's 17 retirement pins across 5 files were found (#5286). Before writing one, -check the file is compiled. `packages/spec` additionally holds its test-layer residue in -a per-file, exactly-measured, shrink-only ledger (`packages/spec/test-typecheck-debt.json`, -`pnpm --filter @objectstack/spec gen:test-typecheck-debt`): a file not listed there may -have no type errors at all. +how spec's 17 retirement pins across 5 files were found (#5286), and the repo-wide sweep +that followed found the eighteenth in `packages/client` (#5449). Before writing one, +check the file is compiled. A package whose test layer still carries residue holds it in +a per-file, exactly-measured, shrink-only ledger next to its `tsconfig.test.json` +(`/test-typecheck-debt.json`, regenerated with +`pnpm --filter gen:test-typecheck-debt`): a file not listed there may have no +type errors at all. The gate behind both is one shared script, +`scripts/check-test-typecheck.mts --package ` — onboard a package by wiring its +`typecheck` script to it, never by copying it. One trap worth knowing before you read any of these counts: under `moduleResolution: NodeNext` a relative import missing its `.js` extension does not resolve, every symbol it diff --git a/packages/client/package.json b/packages/client/package.json index f5e3328756..1bafc8e318 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -16,7 +16,9 @@ "build": "tsup --config ../../tsup.config.ts", "test": "vitest run", "test:integration": "vitest run --config vitest.integration.config.ts", - "typecheck": "tsc --noEmit" + "check:test-typecheck": "tsx ../../scripts/check-test-typecheck.mts --self-test && tsx ../../scripts/check-test-typecheck.mts --package packages/client --project tsconfig.test.json", + "gen:test-typecheck-debt": "tsx ../../scripts/check-test-typecheck.mts --update --package packages/client --project tsconfig.test.json", + "typecheck": "tsc --noEmit && pnpm check:test-typecheck" }, "dependencies": { "@objectstack/core": "workspace:*", @@ -29,6 +31,7 @@ "@objectstack/objectql": "workspace:*", "@objectstack/plugin-hono-server": "workspace:*", "@objectstack/runtime": "workspace:*", + "tsx": "^4.23.1", "typescript": "^6.0.3", "vitest": "^4.1.10" }, diff --git a/packages/client/src/client.batch-transaction.test.ts b/packages/client/src/client.batch-transaction.test.ts index 88318182da..ae243f3fb7 100644 --- a/packages/client/src/client.batch-transaction.test.ts +++ b/packages/client/src/client.batch-transaction.test.ts @@ -94,7 +94,10 @@ describe('data.batchTransaction (live Hono, #1604)', () => { label: 'Task', fields: { title: { type: 'text', label: 'Title' }, - project: { type: 'lookup', reference_to: 'project', label: 'Project' }, + // `reference`, not `reference_to`: the latter is no key the field + // schema knows, so this lookup declared no target at all until a + // tsc program finally read the file (TS2561, #5449). + project: { type: 'lookup', reference: 'project', label: 'Project' }, }, }); // Objects registered AFTER bootstrap miss the boot-time schema sync, so diff --git a/packages/client/src/client.hono.test.ts b/packages/client/src/client.hono.test.ts index c35c9ef8ae..ab267c82a7 100644 --- a/packages/client/src/client.hono.test.ts +++ b/packages/client/src/client.hono.test.ts @@ -47,7 +47,7 @@ describe('ObjectStackClient (with Hono Server)', () => { // --- BROKER SHIM START --- // HttpDispatcher requires a broker to function. We inject a simple shim. (kernel as any).broker = { - call: async (action: string, params: any, opts: any) => { + call: async (action: string, params: any, _opts: any) => { const parts = action.split('.'); const service = parts[0]; const method = parts[1]; @@ -159,9 +159,12 @@ describe('ObjectStackClient (with Hono Server)', () => { // Discovery is REST's, computed from its registry (#4018 D12: declared // === enforced). Every route it advertises must actually answer. + // `routes` is optional on the discovery payload, so it is reached + // optionally and asserted — a missing map fails `toContain` rather than + // being waved through by a `!` or a `?? {}` default (#5449). const endpoints = client['discoveryInfo']!.routes; - expect(endpoints.data).toContain('/api/v1/data'); - expect(endpoints.metadata).toContain('/api/v1/meta'); + expect(endpoints?.data).toContain('/api/v1/data'); + expect(endpoints?.metadata).toContain('/api/v1/meta'); // Enforced, not just declared — the pairing #4018 exists to hold. expect((await fetch(`${baseUrl}/api/v1/meta/objects`)).status).not.toBe(404); diff --git a/packages/client/src/client.test.ts b/packages/client/src/client.test.ts index e00e806f86..65c2a3eaaf 100644 --- a/packages/client/src/client.test.ts +++ b/packages/client/src/client.test.ts @@ -1,5 +1,9 @@ import { describe, it, expect, vi } from 'vitest'; -import { ObjectStackClient, QueryBuilder, FilterBuilder, createQuery, createFilter } from './index'; +// `QueryBuilder` / `FilterBuilder` are named only by the `describe` blocks below; +// the suites build them through `createQuery` / `createFilter`, so importing the +// classes themselves left two unused bindings (TS6133) the moment this file +// entered a tsc program (#5449). +import { ObjectStackClient, createQuery, createFilter } from './index'; /** Helper: create a client with mocked fetch that returns the given response body */ function createMockClient(body: any, status = 200) { @@ -103,7 +107,11 @@ describe('ObjectStackClient', () => { const result = await client.meta.getItem('object', 'customer'); expect(fetchMock).toHaveBeenCalledWith('http://localhost:3000/api/v1/meta/object/customer', expect.any(Object)); - expect(result.name).toBe('customer'); + // `meta.getItem` has no declared return type (unlike the `getItems` + // beside it — #5545), so its unwrapped payload is `unknown`. Asserted + // structurally rather than cast: same assertion strength, without + // pretending this surface is typed (#5449). + expect(result).toMatchObject({ name: 'customer' }); }); it('meta.getView speaks the path-param dialect both surfaces accept (#3611)', async () => { @@ -1280,7 +1288,12 @@ describe('ScopedProjectClient', () => { it('throws when environmentId is missing', () => { const client = new ObjectStackClient({ baseUrl: 'http://localhost:3000' }); - // @ts-expect-error — empty string rejected at runtime + // No `@ts-expect-error` here, and that is the finding of #5449 rather than + // an omission. `project(environmentId: string)` accepts `''` — it is a + // perfectly good `string` — so the directive that sat on this line + // suppressed nothing and reported TS2578 ("unused") the first time a tsc + // program read the file. Its own comment said what the test actually + // proves: the empty id is rejected at RUNTIME, by the guard below. expect(() => client.project('')).toThrow(/environmentId is required/); }); diff --git a/packages/client/test-typecheck-debt.json b/packages/client/test-typecheck-debt.json new file mode 100644 index 0000000000..173f8c8d17 --- /dev/null +++ b/packages/client/test-typecheck-debt.json @@ -0,0 +1,8 @@ +{ + "_comment": "Per-file tsc error debt of the @objectstack/client 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/client gen:test-typecheck-debt", + "entries": { + "src/client.batch-transaction.test.ts": 3, + "src/client.environment-scoping.test.ts": 1, + "src/client.hono.test.ts": 2 + } +} diff --git a/packages/client/tsconfig.test.json b/packages/client/tsconfig.test.json new file mode 100644 index 0000000000..2ec76f91f8 --- /dev/null +++ b/packages/client/tsconfig.test.json @@ -0,0 +1,62 @@ +// The TEST-layer type-check program (#5449, the mechanism #5286/PR #5478 set +// for `packages/spec`). `tsconfig.json` above stays as it is: it is the BUILD +// config, and its `**/*.test.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 +// (`-p tsconfig.test.json`), because a config no script invokes is exactly the +// phantom this whole change is about. +// +// What differs from the build config, and what deliberately does NOT: +// - module semantics ONLY. The tests are written and executed as ESM by +// vitest (esbuild/vite), while `client` has no `"type": "module"`, so the +// build config's NodeNext compiles them as CJS and reports errors about the +// CHECK rather than the code (TS2835 extensionless relative imports, TS1470 +// `import.meta`, TS2550 lib). Matching vitest is fidelity. +// - `rootDir` widens to the workspace root. It steers emit layout only, and +// this program emits nothing; inherited as `./src` it reported TS6059 for +// the four route-ledger modules `client-url-conformance.test.ts` and the +// three `*-route-ledger-coverage.test.ts` files deep-import from sibling +// packages (`../../runtime/src/route-ledger`, …). Those five TS6059 are the +// bulk of this package's stale TEST_DEBT entry — a measurement of the +// misconfigured check, not of the tests. +// - STRICTNESS IS UNTOUCHED. `strict`, `noUnusedLocals`, `noUnusedParameters`, +// `noImplicitReturns` and the rest are inherited from the root config. +// Nothing here may loosen a type rule; if a test does not compile, that is +// the finding. +// +// `include` deliberately stops at `src`, matching the build config's root, and +// none of the files it leaves out carries a `@ts-expect-error`, so no pin is +// hiding there. `tests/integration/` — the suite `vitest.integration.config.ts` +// runs against a live server — is in no tsconfig at all: a second, +// differently-shaped hole (1 file / 3 errors, one of them a real API drift, the +// suite reading a `client.discovery` property `ObjectStackClient` does not +// have) that wants its own change rather than a rider on this one. Filed as +// #5544. +// +// The per-file ledger beside this config (`test-typecheck-debt.json`) is small +// on purpose. Under the repaired config the whole test layer came to 13 errors; +// eight were the tests' own and are fixed in this same change (two unused +// imports, an unused parameter, two possibly-undefined reads, an `unknown` +// payload asserted structurally, a `reference_to` key the field schema never +// had, and the phantom pin itself). Re-spelling that key uncovered one more of +// the remaining kind, and all six that stay are ONE producer-side defect +// wearing three files' clothes: objectql's `registerObject` takes the schema's +// OUTPUT type (`z.infer`) where it should take the INPUT one, so a perfectly +// good authored literal reads as missing nine defaulted keys (#5543). Holding +// them EXACT and shrink-only means fixing #5543 turns the ledger red until the +// entries are deleted, instead of letting it rot. Every file NOT listed there — +// `client.test.ts`, the pin file, first among them — must have no errors at +// all. +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "noEmit": true, + "rootDir": "../..", + "module": "esnext", + "moduleResolution": "bundler", + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "types": ["node"] + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist"] +} diff --git a/packages/spec/package.json b/packages/spec/package.json index 4946eb5482..e6673f0b03 100644 --- a/packages/spec/package.json +++ b/packages/spec/package.json @@ -217,8 +217,8 @@ "check:react-blocks": "tsx scripts/build-react-blocks-contract.ts --check", "check:react-declaration-parity": "tsx scripts/check-react-blocks-declaration-parity.ts", "check:skill-examples": "tsx scripts/check-skill-examples.ts", - "check:test-typecheck": "tsx scripts/check-test-typecheck.mts --self-test && tsx scripts/check-test-typecheck.mts --project tsconfig.test.json", - "gen:test-typecheck-debt": "tsx scripts/check-test-typecheck.mts --update --project tsconfig.test.json", + "check:test-typecheck": "tsx ../../scripts/check-test-typecheck.mts --self-test && tsx ../../scripts/check-test-typecheck.mts --package packages/spec --project tsconfig.test.json", + "gen:test-typecheck-debt": "tsx ../../scripts/check-test-typecheck.mts --update --package packages/spec --project tsconfig.test.json", "typecheck": "tsc --noEmit && pnpm check:test-typecheck" }, "keywords": [ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4222518c6e..fa99aaa4bc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -599,6 +599,9 @@ importers: '@objectstack/runtime': specifier: workspace:* version: link:../runtime + tsx: + specifier: ^4.23.1 + version: 4.23.1 typescript: specifier: ^6.0.3 version: 6.0.3 diff --git a/packages/spec/scripts/check-test-typecheck.mts b/scripts/check-test-typecheck.mts similarity index 75% rename from packages/spec/scripts/check-test-typecheck.mts rename to scripts/check-test-typecheck.mts index ec9637767d..effe57ff47 100644 --- a/packages/spec/scripts/check-test-typecheck.mts +++ b/scripts/check-test-typecheck.mts @@ -1,6 +1,6 @@ #!/usr/bin/env tsx -// check-test-typecheck — the spec test layer is compiled by tsc, and every -// error it still carries is a named, measured, shrink-only entry (#5286). +// check-test-typecheck — a package's TEST layer is compiled by tsc, and every +// error it still carries is a named, measured, shrink-only entry (#5286, #5449). // // WHY THIS EXISTS. `packages/spec/tsconfig.json` excluded `**/*.test.ts`, and // the package's `typecheck` script is `tsc --noEmit` against that very config. @@ -9,44 +9,52 @@ // second compile step. Seventeen `@ts-expect-error` retirement pins across five // files — the "tsc is the best sweeper" channel the spec-property-retirement // playbook leans on — were PHANTOM checks. Deleting a directive line left the -// suite green, which is the definition of a check that never ran. +// suite green, which is the definition of a check that never ran. The repo-wide +// sweep that finding triggered turned up an eighteenth pin in +// `packages/client` (#5449), which is why this gate is now shared rather than +// spec-local: the mechanism is one mechanism, named per package with +// `--package`, and every package that follows onboards by wiring its +// `typecheck` script instead of copying 300 lines. // // The repair is `tsconfig.test.json`: the same strictness flags (they are // inherited, untouched — this is fidelity, not loosening) with module semantics // that match how vitest actually executes the files (`module: esnext`, // `moduleResolution: bundler`, `lib` including ES2022). Under the build config's -// NodeNext, 108 of the 842 raw errors were the CHECK being misconfigured rather -// than the code being wrong (TS2835 x58 "dynamic import needs .js", TS1470 x24 -// `import.meta` in a CJS program, TS2307 x18, TS2550 x7 lib) — a config-tier -// pile that says nothing about the tests. Fixing the config first, then reading -// the residue, is the #4311 discipline this repo already writes down. +// NodeNext, 108 of spec's 842 raw errors were the CHECK being misconfigured +// rather than the code being wrong (TS2835 x58 "dynamic import needs .js", +// TS1470 x24 `import.meta` in a CJS program, TS2307 x18, TS2550 x7 lib) — a +// config-tier pile that says nothing about the tests. Client's five TS6059 were +// the same shape, from an inherited `rootDir`. Fixing the config first, then +// reading the residue, is the #4311 discipline this repo already writes down. // -// The residue is real and large (691 errors over 79 files at the baseline), -// overwhelmingly fixture object literals annotated with a schema's OUTPUT type -// (`z.infer`) while holding an authored INPUT literal — so every defaulted key -// reads as "missing". Hand-fixing 691 of those in the PR that opens the gate -// would bury the gate. They are ledgered per file instead, in -// `test-typecheck-debt.json`, and the ledger is EXACT: recorded must equal -// measured. That is what makes it shrink-only in practice — +// The residue is real (691 errors over 79 files for spec; 6 over 3 files for +// client), overwhelmingly fixture object literals annotated with a schema's +// OUTPUT type (`z.infer`) while holding an authored INPUT literal — so every +// defaulted key reads as "missing". Hand-fixing 691 of those in the PR that +// opens the gate would bury the gate. They are ledgered per file instead, in +// the package's own `test-typecheck-debt.json`, and the ledger is EXACT: +// recorded must equal measured. That is what makes it shrink-only in practice — // // • a file gains errors → red ("grew") // • a file loses errors → red ("shrank; re-record") — so the number // tracks reality downward instead of rotting // • a file reaches zero → red ("graduated; delete the entry") // • an unledgered file errors→ red — this is the everyday case, and it is -// why the five pin files carry NO entry: any -// error in them, including the TS2578 that a -// deleted `@ts-expect-error` produces, is red. +// why the pin files carry NO entry: any error in +// them, including the TS2578 that a deleted (or +// a never-applicable) `@ts-expect-error` +// produces, is red. // // Growing the ledger is possible (add the file and its count) but it is a // visible line in this repo's diff and needs the same justification any DEBT // entry needs — the idiom of `scripts/check-type-check-coverage.mjs`, applied // per file rather than per package. // -// Usage: -// tsx scripts/check-test-typecheck.mts # compile + judge -// tsx scripts/check-test-typecheck.mts --update # re-record the ledger -// tsx scripts/check-test-typecheck.mts --self-test # ledger semantics only +// Usage (`--package` is repo-relative and required for everything but +// `--self-test`, which judges the ledger semantics alone): +// tsx scripts/check-test-typecheck.mts --package packages/spec +// tsx scripts/check-test-typecheck.mts --package packages/spec --update +// tsx scripts/check-test-typecheck.mts --self-test import { spawnSync } from 'node:child_process'; import { createRequire } from 'node:module'; @@ -55,22 +63,46 @@ import path from 'node:path'; import url from 'node:url'; const HERE = path.dirname(url.fileURLToPath(import.meta.url)); -const SPEC = path.resolve(HERE, '..'); +const ROOT = path.resolve(HERE, '..'); +const SELF_TEST = process.argv.includes('--self-test'); + +/** A flag's value, or `undefined` when the flag is absent or trailing. */ +function flag(name: string): string | undefined { + const i = process.argv.indexOf(name); + return i !== -1 && process.argv[i + 1] && !process.argv[i + 1].startsWith('--') ? process.argv[i + 1] : undefined; +} + +// Repo-relative, and REQUIRED: a gate that guessed which package it was judging +// would report a clean run over whichever one it happened to find. +const PKG_DIR = ((): string => { + const value = flag('--package'); + if (!value) { + if (SELF_TEST) return 'packages/spec'; // only the ledger semantics run; nothing is read + throw new Error('check-test-typecheck: --package is required (e.g. --package packages/client).'); + } + return value.replace(/\/+$/, ''); +})(); +const PKG = path.resolve(ROOT, PKG_DIR); +const PKG_NAME = ((): string => { + try { + return JSON.parse(fs.readFileSync(path.join(PKG, 'package.json'), 'utf8')).name ?? PKG_DIR; + } catch { + if (SELF_TEST) return '@objectstack/spec'; + throw new Error(`check-test-typecheck: no readable package.json at ${PKG_DIR}.`); + } +})(); // Named on the command line rather than hardcoded, so the wiring is visible in // package.json: `check:type-check-coverage` reads the typecheck script chain to // decide whether a sibling test tsconfig is actually invoked, and a config no // script names is exactly the phantom this gate is about. -const PROJECT = ((): string => { - const i = process.argv.indexOf('--project'); - return i !== -1 && process.argv[i + 1] ? process.argv[i + 1] : 'tsconfig.test.json'; -})(); -const LEDGER_PATH = path.join(SPEC, 'test-typecheck-debt.json'); +const PROJECT = flag('--project') ?? 'tsconfig.test.json'; +const LEDGER_PATH = path.join(PKG, 'test-typecheck-debt.json'); const LEDGER_NAME = 'test-typecheck-debt.json'; const ISSUE = 'https://github.com/objectstack-ai/objectstack/issues/5286'; -const UPDATE_COMMAND = 'pnpm --filter @objectstack/spec gen:test-typecheck-debt'; +const UPDATE_COMMAND = `pnpm --filter ${PKG_NAME} gen:test-typecheck-debt`; const LEDGER_COMMENT = - 'Per-file tsc error debt of the @objectstack/spec TEST layer (#5286). ' + + `Per-file tsc error debt of the ${PKG_NAME} 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 ' + @@ -86,8 +118,8 @@ const DIAGNOSTIC = /^(\S[^(]*)\((\d+),(\d+)\): error (TS\d+): /; /** * Per-file error counts from a raw `tsc --noEmit --pretty false` transcript. - * Paths are normalised to posix and relative to the spec package, so the ledger - * reads the same on every platform. + * Paths are normalised to posix and relative to the package being judged, so + * the ledger reads the same on every platform. */ export function parseDiagnostics(output: string): Map { const counts = new Map(); @@ -170,7 +202,7 @@ function runTsc(): string { const require = createRequire(import.meta.url); const tsc = require.resolve('typescript/bin/tsc'); const result = spawnSync(process.execPath, [tsc, '--noEmit', '--pretty', 'false', '-p', PROJECT], { - cwd: SPEC, + cwd: PKG, encoding: 'utf8', maxBuffer: 64 * 1024 * 1024, env: { ...process.env, NODE_OPTIONS: process.env.NODE_OPTIONS ?? '--max-old-space-size=4096' }, @@ -300,6 +332,6 @@ if (problems.length) { const total = [...counts.values()].reduce((a, b) => a + b, 0); console.log( - `check:test-typecheck: OK — spec's test layer compiles under ${PROJECT}; ` + + `check:test-typecheck: OK — ${PKG_NAME}'s test layer compiles under ${PKG_DIR}/${PROJECT}; ` + `${counts.size} file(s) / ${total} error(s) held in ${LEDGER_NAME} (shrink-only, ${ISSUE}).`, ); diff --git a/scripts/check-type-check-coverage.mjs b/scripts/check-type-check-coverage.mjs index a72f3e68b7..6cdaa33a9e 100644 --- a/scripts/check-type-check-coverage.mjs +++ b/scripts/check-type-check-coverage.mjs @@ -251,7 +251,6 @@ const TEST_DEBT = { '@objectstack/driver-mongodb': { tests: 7, errors: 44, note: 'TS2345 x22, TS2591 x15 (`process` -- the test files need types:["node"] once included).' }, '@objectstack/lint': { tests: 39, errors: 26, note: 'TS7006 x20, TS2835 x6.' }, '@objectstack/plugin-security': { tests: 32, errors: 20, note: 'TS2739 x8, TS2740 x5 -- incomplete literals.' }, - '@objectstack/client': { tests: 15, errors: 19, note: 'TS6059 x5 (rootDir), TS2740 x5, TS6133 x3.' }, '@objectstack/formula': { tests: 13, errors: 12, note: 'TS2345 x3, TS2352 x3, TS2591 x3.' }, '@objectstack/trigger-record-change': { tests: 4, errors: 8, note: 'TS2353 x8 -- one unknown-property shape repeated.' }, '@objectstack/verify': { tests: 2, errors: 6, note: 'TS7006 x4, TS2835 x2.' }, @@ -277,8 +276,6 @@ const TEST_DEBT = { // `include`, or add a sibling `tsconfig.test.json` the typecheck script names) // -- and then delete the entry, which RECONCILED forces anyway. const PHANTOM_PIN_DEBT = { - 'packages/client/src/client.test.ts': - 'tsconfig.json excludes `**/*.test.ts` and the package has no sibling test config; also in TEST_DEBT (15 files / 19 errors). Onboarding it is #5449, not #5286 -- the two directives here pin retired client options.', 'packages/metadata-core/test/types.test.ts': 'Outside the program for a different reason, and one no exclusion names: `include` is `["src/**/*"]` while this file lives in a sibling `test/` tree, so TESTS_COVERED never saw it either (its testFiles count is 0). Repair is to widen `include` or add a test config; tracked by #5476, not by #5286 (which scoped itself to packages/spec).', };