diff --git a/packages/objectql/package.json b/packages/objectql/package.json index 7f169fa2ef3..1001384eea1 100644 --- a/packages/objectql/package.json +++ b/packages/objectql/package.json @@ -21,7 +21,7 @@ "build": "tsup && node ../../scripts/check-dts-emitted.mjs", "test": "vitest run --project local", "test:repo": "vitest run --project repo", - "typecheck": "tsc --noEmit && tsc --noEmit -p tsconfig.scripts.json && pnpm check:test-typecheck", + "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/objectql --project tsconfig.test.json", "gen:test-typecheck-debt": "tsx ../../scripts/check-test-typecheck.mts --update --package packages/objectql --project tsconfig.test.json" }, diff --git a/packages/objectql/scripts/dry-run-hash-compat.ts b/packages/objectql/scripts/dry-run-hash-compat.ts deleted file mode 100644 index 3819ef6e4ed..00000000000 --- a/packages/objectql/scripts/dry-run-hash-compat.ts +++ /dev/null @@ -1,330 +0,0 @@ -// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license. - -/** - * PR-10d.1 — Hash-compat dry-run probe. - * - * Read-only probe that audits a snapshot of `sys_metadata` rows and asks: - * - * 1. Is every row's `metadata` column valid JSON? - * 2. Is the parsed body an object (the shape SysMetadataRepository expects)? - * 3. Does `hashSpec(body)` produce a stable hash across a serialize → parse - * round-trip? (If not, flipping the write path will produce different - * hashes on subsequent reads — silent corruption.) - * 4. Are there any rows whose `(type, name, organization_id)` tuple is - * duplicated within the snapshot? (Would violate the overlay invariant - * once `SysMetadataRepository.put` enforces it.) - * - * Usage: - * - * pnpm tsx packages/objectql/scripts/dry-run-hash-compat.ts - * - * Where `` is an array of rows in the shape of `MetadataRecord`, - * obtained via `SELECT * FROM sys_metadata` (any driver) and exported as JSON. - * - * The script writes a structured report to stdout and exits 0 if the snapshot - * is compatible, 1 otherwise. No database is touched. - * - * Pair this with the test in `dry-run-hash-compat.test.ts` which exercises - * the probe against synthetic fixtures covering legacy edge cases. - */ - -import { realpathSync } from 'node:fs'; -import { join, resolve } from 'node:path'; -import { hashSpec } from '@objectstack/metadata-core'; - -export interface LegacyMetadataRow { - id?: string; - type?: string; - name?: string; - organization_id?: string | null; - metadata?: string | null; - state?: string; - version?: number | null; - /** - * Legacy `checksum` column (sha256 hex). PR-10d.2 reuses this column - * as the optimistic-locking token; pre-PR-10d rows have `null`. - */ - checksum?: string | null; - [k: string]: unknown; -} - -export interface RowFinding { - row: { id?: string; type?: string; name?: string; organization_id?: string | null }; - severity: 'error' | 'warning'; - code: - | 'invalid_json' - | 'non_object_body' - | 'unstable_hash' - | 'missing_metadata' - | 'duplicate_overlay_key' - | 'checksum_drift' - | 'checksum_missing'; - detail: string; -} - -export interface DryRunReport { - totalRows: number; - okRows: number; - findings: RowFinding[]; - typeDistribution: Record; - duplicateKeys: string[]; - /** Rows where `checksum` is NULL — eligible for lazy backfill on first write. */ - needsBackfill: number; - /** Rows where stored `checksum` disagrees with recomputed `hashSpec(body)`. */ - checksumDrift: number; - compatible: boolean; -} - -/** - * Run the probe over an in-memory snapshot. Pure function — does no I/O. - */ -export function runDryRun(rows: LegacyMetadataRow[]): DryRunReport { - const findings: RowFinding[] = []; - const typeDistribution: Record = {}; - const seen = new Map(); - const duplicateKeys = new Set(); - let okRows = 0; - let needsBackfill = 0; - let checksumDrift = 0; - - for (const row of rows) { - const tag = { - id: row.id, - type: row.type, - name: row.name, - organization_id: row.organization_id ?? null, - }; - - // 1. Missing metadata column. - if (row.metadata == null) { - findings.push({ - row: tag, - severity: 'error', - code: 'missing_metadata', - detail: 'metadata column is null/undefined', - }); - continue; - } - - // 2. Invalid JSON. - let body: unknown; - try { - body = JSON.parse(row.metadata); - } catch (e) { - findings.push({ - row: tag, - severity: 'error', - code: 'invalid_json', - detail: `JSON.parse failed: ${(e as Error).message}`, - }); - continue; - } - - // 3. Body must be a plain object — SysMetadataRepository.put rejects - // arrays/primitives. - if (body === null || typeof body !== 'object' || Array.isArray(body)) { - findings.push({ - row: tag, - severity: 'error', - code: 'non_object_body', - detail: `metadata body is ${Array.isArray(body) ? 'array' : typeof body}, not a plain object`, - }); - continue; - } - - // 4. Hash stability across serialize → parse round-trip. The repository - // will canonicalize on every put, so we must verify that - // hashSpec(JSON.parse(JSON.stringify(body))) === hashSpec(body) - // for every legacy row. - let h1: string; - let h2: string; - try { - h1 = hashSpec(body as Record); - const roundTrip = JSON.parse(JSON.stringify(body)); - h2 = hashSpec(roundTrip); - } catch (e) { - findings.push({ - row: tag, - severity: 'error', - code: 'unstable_hash', - detail: `hashSpec threw: ${(e as Error).message}`, - }); - continue; - } - if (h1 !== h2) { - findings.push({ - row: tag, - severity: 'error', - code: 'unstable_hash', - detail: `hash differs across round-trip: ${h1} vs ${h2}`, - }); - continue; - } - - // 5. Checksum reconciliation (PR-10d.2). The repository uses the - // `checksum` column as the optimistic-lock token. Pre-PR-10d - // rows have `null`. Eligible for lazy backfill on next write. - // If non-null, it MUST equal hashSpec(body) — otherwise legacy - // code wrote a checksum we can't reproduce. - if (row.checksum == null) { - needsBackfill += 1; - findings.push({ - row: tag, - severity: 'warning', - code: 'checksum_missing', - detail: 'legacy row with NULL checksum — will be backfilled on first put()', - }); - } else if (row.checksum !== h1) { - checksumDrift += 1; - findings.push({ - row: tag, - severity: 'error', - code: 'checksum_drift', - detail: `stored checksum ${row.checksum} != recomputed ${h1}`, - }); - continue; - } - - // 6. Duplicate (type, name, organization_id) — would break the unique - // overlay invariant. Only count active rows. - if (row.state === 'active' && row.type && row.name) { - const key = `${row.type}|${row.name}|${row.organization_id ?? '__env__'}`; - const prior = seen.get(key); - if (prior) { - duplicateKeys.add(key); - findings.push({ - row: tag, - severity: 'error', - code: 'duplicate_overlay_key', - detail: `duplicate active overlay key ${key} (conflicts with row id=${prior.id})`, - }); - continue; - } - seen.set(key, row); - } - - // 7. Distribution. - if (row.type) { - typeDistribution[row.type] = (typeDistribution[row.type] ?? 0) + 1; - } - okRows += 1; - } - - return { - totalRows: rows.length, - okRows, - findings, - typeDistribution, - duplicateKeys: Array.from(duplicateKeys), - needsBackfill, - checksumDrift, - compatible: findings.every((f) => f.severity !== 'error'), - }; -} - -/** - * Pretty-print a report. Returns a multi-line string suitable for stdout. - */ -export function formatReport(report: DryRunReport): string { - const lines: string[] = []; - lines.push('# Hash-compat dry-run report'); - lines.push(''); - lines.push(`Total rows: ${report.totalRows}`); - lines.push(`OK rows: ${report.okRows}`); - lines.push(`Findings: ${report.findings.length}`); - lines.push(`Needs backfill: ${report.needsBackfill} (NULL checksum)`); - lines.push(`Checksum drift: ${report.checksumDrift}`); - lines.push(`Compatible: ${report.compatible ? 'YES ✅' : 'NO ❌'}`); - lines.push(''); - lines.push('## Type distribution'); - const types = Object.entries(report.typeDistribution).sort((a, b) => b[1] - a[1]); - if (types.length === 0) lines.push(' (none)'); - for (const [t, n] of types) lines.push(` ${t}: ${n}`); - lines.push(''); - if (report.findings.length > 0) { - lines.push('## Findings'); - for (const f of report.findings) { - lines.push( - ` [${f.severity}] ${f.code} — id=${f.row.id ?? '?'} type=${f.row.type ?? '?'} name=${f.row.name ?? '?'} org=${f.row.organization_id ?? 'null'}`, - ); - lines.push(` ${f.detail}`); - } - lines.push(''); - } - if (report.duplicateKeys.length > 0) { - lines.push('## Duplicate overlay keys'); - for (const k of report.duplicateKeys) lines.push(` ${k}`); - lines.push(''); - } - return lines.join('\n'); -} - -// ─── entry guard ─────────────────────────────────────────────────────── -// ⛔ NOT ``import.meta.url === `file://${process.argv[1]}` ``. Node symlink-resolves -// `import.meta.url` but leaves `process.argv[1]` exactly as the caller typed it, and -// the template also skips the percent-encoding `pathToFileURL` applies — so that -// spelling goes INERT (exit 0, no output) through a symlink AND on any checkout path -// containing a character that needs encoding (a `#` in a parent directory name is -// enough, with no symlink involved). Compare RESOLVED PATHS, never URL strings. -// -// Same predicate as `packages/cli/src/utils/invocation.ts` (`isProcessEntry`) and -// `scripts/invoked-as.mjs` (`invokedAs`) — both legs identical: realpath for the -// symlink, directory resolution for `node `. Spelled out rather than imported -// because neither home is legally reachable from this file — the PR for #10269 -// carries the boundary measurement. ⚠️ Two predicates answering this question -// differently IS the defect this closes; change one, change all of them. -// -// ⚠️ ONE spelling DIVERGES from those two, and the divergence is FORCED — do not -// "restore consistency" here: the self-path seed is `__filename`, NOT -// `fileURLToPath(import.meta.url)`. `packages/objectql/package.json` declares no -// `"type"`, so under the repo-wide `module: NodeNext` every file in this package -// compiles as COMMONJS, and `import.meta` in a CommonJS-format file is a hard -// compile error (TS1470). This file IS inside a tsc program despite the package's -// own `include` naming only `src/**/*`: `src/dry-run-hash-compat.test.ts` imports -// it, and the TEST_DEBT re-measure in `scripts/check-type-check-coverage.mjs` -// type-checks the tests — so the ESM seed costs a ratchet failure on a ledger that -// may only shrink. The PREDICATE is untouched by this: `invokedAs(entryArg, -// selfPath)` is the shared core and it takes a PATH, `isEntrypoint(import.meta.url)` -// is merely the ESM way to seed it, and `__filename` is the CommonJS way — node's -// CJS loader hands it an absolute path that is ALREADY symlink-resolved and -// percent-decoded, which is exactly the property the guard rests on. -function isProcessEntry(): boolean { - const entryArg = process.argv[1]; - if (!entryArg) return false; // `node --eval` / the REPL - const self = resolve(__filename); - const entry = resolve(entryArg); - // `node ` gives the ENTRY ARGUMENT, and only it, directory resolution. - const candidates = [entry, join(entry, 'index.js'), join(entry, 'index.mjs'), join(entry, 'index.ts')]; - if (candidates.includes(self)) return true; - const realSelf = realOrSelf(self); - return candidates.some((candidate) => realOrSelf(candidate) === realSelf); -} - -/** `realpathSync`, degrading to the input for a path that cannot be read. */ -function realOrSelf(p: string): string { - try { - return realpathSync(p); - } catch { - return p; - } -} - -if (isProcessEntry()) { - const path = process.argv[2]; - if (!path) { - console.error('Usage: pnpm tsx packages/objectql/scripts/dry-run-hash-compat.ts '); - process.exit(2); - } - void (async () => { - const fs = await import('node:fs/promises'); - const raw = await fs.readFile(path, 'utf8'); - const rows: LegacyMetadataRow[] = JSON.parse(raw); - if (!Array.isArray(rows)) { - console.error(`Snapshot at ${path} is not a JSON array.`); - process.exit(2); - } - const report = runDryRun(rows); - console.log(formatReport(report)); - process.exit(report.compatible ? 0 : 1); - })(); -} diff --git a/packages/objectql/src/datasource-def-credentials-ref.pin.ts b/packages/objectql/src/datasource-def-credentials-ref.pin.ts index 38b69e4122b..86351001bcb 100644 --- a/packages/objectql/src/datasource-def-credentials-ref.pin.ts +++ b/packages/objectql/src/datasource-def-credentials-ref.pin.ts @@ -29,8 +29,8 @@ * * ⚠ It is NOT that a directive in a `*.test.ts` here would go unevaluated — * this docblock used to say so, and that is FALSE on this tree. This package's - * `typecheck` is `tsc --noEmit && tsc --noEmit -p tsconfig.scripts.json && - * pnpm check:test-typecheck`, and the last leg runs + * `typecheck` is `tsc --noEmit && pnpm check:test-typecheck`, and the last + * leg runs * `--project tsconfig.test.json`, whose `include` is `src/**\/*` with no test * exclusion: 299 of this package's `src` test files are in that program, * measured with `tsc --listFiles` (0 in the build program — the firing control diff --git a/packages/objectql/src/dry-run-hash-compat.test.ts b/packages/objectql/src/dry-run-hash-compat.test.ts deleted file mode 100644 index 37c692ac04a..00000000000 --- a/packages/objectql/src/dry-run-hash-compat.test.ts +++ /dev/null @@ -1,288 +0,0 @@ -// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license. - -import { describe, expect, it } from 'vitest'; -import { hashSpec } from '@objectstack/metadata-core'; -import { runDryRun, type LegacyMetadataRow } from '../scripts/dry-run-hash-compat'; - -/** - * PR-10d.1 tests — verify the dry-run probe correctly classifies the kinds - * of rows we expect to find in legacy `sys_metadata` data. - * - * Each fixture mirrors a real-world shape we know production has written via - * `protocol.ts:saveMetaItem` (line 1840+): `metadata` is `JSON.stringify(item)` - * where `item` is whatever the caller passed — typically a Zod-validated view - * or dashboard body. Shapes vary in nesting, key order, locale strings, etc. - */ - -const validView = (overrides: Record = {}): LegacyMetadataRow => { - const body = { - name: 'case_grid', - type: 'grid', - label: 'Cases', - columns: [ - { field: 'id', width: 80 }, - { field: 'title', width: 240 }, - ], - ...overrides, - }; - return { - id: 'r1', - type: 'view', - name: 'case_grid', - organization_id: 'org_alpha', - state: 'active', - metadata: JSON.stringify(body), - checksum: hashSpec(body), - }; -}; - -describe('runDryRun — happy path', () => { - it('returns compatible:true for a clean snapshot', () => { - const report = runDryRun([ - validView(), - { ...validView(), id: 'r2', name: 'case_kanban' }, - { ...validView(), id: 'r3', type: 'dashboard', name: 'home' }, - ]); - expect(report.compatible).toBe(true); - expect(report.okRows).toBe(3); - expect(report.findings).toHaveLength(0); - }); - - it('reports type distribution', () => { - const report = runDryRun([ - validView(), - { ...validView(), id: 'r2', name: 'a' }, - { ...validView(), id: 'r3', name: 'b' }, - { ...validView(), id: 'r4', type: 'dashboard', name: 'home' }, - ]); - expect(report.typeDistribution).toEqual({ view: 3, dashboard: 1 }); - }); - - it('handles legacy keys in non-canonical order (round-trip hash is stable)', () => { - // Production code does `JSON.stringify(item)` which preserves insertion - // order. Different writers produce different orderings. canonicalize() - // must absorb this. - const bodyA = { z: 1, a: 2, m: { y: 9, b: 8 } }; - const bodyB = { a: 2, m: { b: 8, y: 9 }, z: 1 }; - const a: LegacyMetadataRow = { - id: 'r1', - type: 'view', - name: 'case_grid', - organization_id: 'org_alpha', - state: 'active', - metadata: JSON.stringify(bodyA), - checksum: hashSpec(bodyA), - }; - const b: LegacyMetadataRow = { - id: 'r2', - type: 'view', - name: 'case_kanban', - organization_id: 'org_alpha', - state: 'active', - metadata: JSON.stringify(bodyB), - checksum: hashSpec(bodyB), - }; - const report = runDryRun([a, b]); - expect(report.compatible).toBe(true); - expect(report.findings).toHaveLength(0); - }); -}); - -describe('runDryRun — error classification', () => { - it('flags missing metadata column', () => { - const report = runDryRun([ - { id: 'r1', type: 'view', name: 'x', organization_id: null, state: 'active', metadata: null }, - ]); - expect(report.compatible).toBe(false); - expect(report.findings[0].code).toBe('missing_metadata'); - }); - - it('flags invalid JSON', () => { - const report = runDryRun([ - { - id: 'r1', - type: 'view', - name: 'x', - organization_id: null, - state: 'active', - metadata: '{not json', - }, - ]); - expect(report.compatible).toBe(false); - expect(report.findings[0].code).toBe('invalid_json'); - }); - - it('flags non-object body (array)', () => { - const report = runDryRun([ - { - id: 'r1', - type: 'view', - name: 'x', - organization_id: null, - state: 'active', - metadata: JSON.stringify([1, 2, 3]), - }, - ]); - expect(report.compatible).toBe(false); - expect(report.findings[0].code).toBe('non_object_body'); - }); - - it('flags non-object body (primitive)', () => { - const report = runDryRun([ - { - id: 'r1', - type: 'view', - name: 'x', - organization_id: null, - state: 'active', - metadata: JSON.stringify('a string'), - }, - ]); - expect(report.compatible).toBe(false); - expect(report.findings[0].code).toBe('non_object_body'); - }); - - it('flags duplicate overlay key on (type, name, organization_id)', () => { - const report = runDryRun([ - validView(), - { ...validView(), id: 'r2' }, // same type+name+org - ]); - expect(report.compatible).toBe(false); - expect(report.findings.some((f) => f.code === 'duplicate_overlay_key')).toBe(true); - expect(report.duplicateKeys).toContain('view|case_grid|org_alpha'); - }); - - it('does NOT flag duplicates across different organizations', () => { - const report = runDryRun([ - validView(), - { ...validView(), id: 'r2', organization_id: 'org_beta' }, - ]); - expect(report.compatible).toBe(true); - }); - - it('does NOT flag duplicates when one row is archived', () => { - const report = runDryRun([ - { ...validView(), state: 'archived' }, - { ...validView(), id: 'r2' }, - ]); - expect(report.compatible).toBe(true); - }); - - it('aggregates multiple findings across rows', () => { - const report = runDryRun([ - validView(), - { - id: 'r2', - type: 'view', - name: 'bad', - organization_id: null, - state: 'active', - metadata: '{broken', - }, - { - id: 'r3', - type: 'view', - name: 'arr', - organization_id: null, - state: 'active', - metadata: '[]', - }, - ]); - expect(report.totalRows).toBe(3); - expect(report.okRows).toBe(1); - expect(report.findings).toHaveLength(2); - expect(report.findings.map((f) => f.code).sort()).toEqual([ - 'invalid_json', - 'non_object_body', - ]); - }); -}); - -describe('runDryRun — checksum reconciliation (PR-10d.2)', () => { - it('warns (does not fail) when legacy row has NULL checksum', () => { - const report = runDryRun([ - { ...validView(), checksum: null }, - ]); - expect(report.compatible).toBe(true); // warning, not error - expect(report.needsBackfill).toBe(1); - expect(report.findings).toHaveLength(1); - expect(report.findings[0].code).toBe('checksum_missing'); - expect(report.findings[0].severity).toBe('warning'); - }); - - it('accepts row whose stored checksum matches recomputed hashSpec(body)', () => { - const body = { name: 'case_grid', type: 'grid', label: 'Cases' }; - const report = runDryRun([ - { - id: 'r1', - type: 'view', - name: 'case_grid', - organization_id: 'org_alpha', - state: 'active', - metadata: JSON.stringify(body), - checksum: hashSpec(body), - }, - ]); - expect(report.compatible).toBe(true); - expect(report.needsBackfill).toBe(0); - expect(report.checksumDrift).toBe(0); - expect(report.findings).toHaveLength(0); - }); - - it('flags drift when stored checksum disagrees with recomputed hash', () => { - const report = runDryRun([ - { ...validView(), checksum: 'sha256:deadbeef' }, - ]); - expect(report.compatible).toBe(false); - expect(report.checksumDrift).toBe(1); - expect(report.findings[0].code).toBe('checksum_drift'); - }); -}); - -describe('runDryRun — boundary conditions', () => { - it('handles empty snapshot', () => { - const report = runDryRun([]); - expect(report.compatible).toBe(true); - expect(report.totalRows).toBe(0); - expect(report.okRows).toBe(0); - expect(report.needsBackfill).toBe(0); - expect(report.checksumDrift).toBe(0); - }); - - it('handles row with deeply nested body', () => { - const deep = (() => { - let acc: any = { leaf: true }; - for (let i = 0; i < 20; i++) acc = { wrap: acc }; - return acc; - })(); - const report = runDryRun([ - { - id: 'r1', - type: 'view', - name: 'deep', - organization_id: null, - state: 'active', - metadata: JSON.stringify(deep), - }, - ]); - expect(report.compatible).toBe(true); - }); - - it('handles unicode and special chars in body', () => { - const report = runDryRun([ - { - id: 'r1', - type: 'view', - name: 'i18n', - organization_id: null, - state: 'active', - metadata: JSON.stringify({ - label: '案件视图', - description: 'スペシャル "quoted" chars \\ and newlines\n', - emoji: '🚀', - }), - }, - ]); - expect(report.compatible).toBe(true); - }); -}); diff --git a/packages/objectql/src/register-object-authored-shape.pin.ts b/packages/objectql/src/register-object-authored-shape.pin.ts index 74da0d31ba2..a77dd63c260 100644 --- a/packages/objectql/src/register-object-authored-shape.pin.ts +++ b/packages/objectql/src/register-object-authored-shape.pin.ts @@ -24,8 +24,8 @@ * * ⚠ It is NOT that a directive in a `*.test.ts` here would go unevaluated — * this docblock used to say so, and that is FALSE on this tree. This package's - * `typecheck` is `tsc --noEmit && tsc --noEmit -p tsconfig.scripts.json && - * pnpm check:test-typecheck`, and the last leg runs + * `typecheck` is `tsc --noEmit && pnpm check:test-typecheck`, and the last + * leg runs * `--project tsconfig.test.json`, whose `include` is `src/**\/*` with no test * exclusion: 299 of this package's `src` test files are in that program, * measured with `tsc --listFiles` (0 in the build program — the firing control diff --git a/packages/objectql/tsconfig.scripts.json b/packages/objectql/tsconfig.scripts.json deleted file mode 100644 index 28a4b91ab61..00000000000 --- a/packages/objectql/tsconfig.scripts.json +++ /dev/null @@ -1,33 +0,0 @@ -// The SCRIPTS-layer type-check program for @objectstack/objectql (#10756). -// -// `packages/objectql/scripts/` held a compatibility checker with a documented -// CLI -- `dry-run-hash-compat.ts`, which `src/dry-run-hash-compat.test.ts` -// imports `runDryRun` from -- and no tsc program this package's `typecheck` -// runs had ever read a line of it. `tsconfig.json` selects `src/**/*` and the -// directory is not under it, so the package passed `check:type-check-coverage` -// as COVERED with a whole source directory unchecked. Injecting a `number` -// initialised with a string into that file moved no gate. -// -// A SIBLING rather than a wider `include` on `tsconfig.json`, which is the -// distinction #5475 drew for `packages/spec` and it holds for the same reason -// here: that config emits (`rootDir: "src"`, `outDir: "dist"`, and `dev` runs -// `tsc -w` through it), so widening it to reach `scripts/` would put the -// directory in front of the emit and `rootDir` would reject it. This program -// emits nothing, so it can neutralise `rootDir` without touching what ships. -// -// STRICTNESS IS INHERITED and deliberately not relaxed: `strict`, -// `noUnusedLocals`, `noUnusedParameters`, `noImplicitReturns` and the rest come -// from the root config through `tsconfig.json`. The directory type-checks clean -// under them today -- it entered with ZERO recorded debt, and there is no -// ledger here to record any in. -{ - "extends": "./tsconfig.json", - "compilerOptions": { - "noEmit": true, - // `.` rather than the inherited `src`, because the files this program - // checks are the ones outside `src`. Safe precisely because nothing is - // emitted from here -- see the header. - "rootDir": "." - }, - "include": ["scripts/**/*"] -} diff --git a/packages/objectql/tsconfig.test.json b/packages/objectql/tsconfig.test.json index 032de0cb785..fc58972f840 100644 --- a/packages/objectql/tsconfig.test.json +++ b/packages/objectql/tsconfig.test.json @@ -38,12 +38,14 @@ // `packages/client`: nothing in this layer touches a browser global. // - `rootDir` widens to the PACKAGE, from the build config's `src`. Under // `noEmit` that setting carries no output meaning; all it does is constrain -// which files may enter the program. Measured: inherited as `src` it -// reports a TS6059 for `src/dry-run-hash-compat.test.ts` importing -// `scripts/dry-run-hash-compat.ts` — the SAME diagnostic #10779 retired -// from the TEST_DEBT ledger as "measuring the tape measure", i.e. the -// generated re-measure project billing this package for its own `rootDir`. -// Re-introducing it here would re-import a defect this repo already fixed. +// which files may enter the program. Inherited as `src` it reports a +// TS6059 for any test that imports a module outside `src` — the SAME +// diagnostic #10779 retired from the TEST_DEBT ledger as "measuring the +// tape measure", i.e. the generated re-measure project billing this +// package for its own `rootDir`. Re-introducing it here would re-import a +// defect this repo already fixed. (Measured on the one such import this +// package had, a `src` test reading `scripts/`; both were retired in +// #21853, so no `src` file imports from outside `src` today.) // - ⛔ STRICTNESS IS UNTOUCHED. `strict`, `noUnusedLocals`, // `noUnusedParameters`, `noImplicitReturns`, `noFallthroughCasesInSwitch` // are inherited from the root config, and `types: ["node"]` restates @@ -54,10 +56,7 @@ // // `include` deliberately stops at `src`, matching the build config's root and // covering all 251 test files (239 in `src`, plus `src/integrity`, -// `src/lifecycle`, `src/validation`). `scripts/` is the sibling -// `tsconfig.scripts.json` (#10756) and stays a separate program: it carries no -// ledger entries at all, so plain `tsc` is a stricter gate for it than this -// file's shrink-only debt list. +// `src/lifecycle`, `src/validation`). // // MEASURED at 2a181174a6, workspace closure built first (an error count taken // against an unbuilt closure is not a reading — unresolved-import cascades diff --git a/scripts/check-error-code-casing.mjs b/scripts/check-error-code-casing.mjs index c15849f37fe..f59e2778b3e 100644 --- a/scripts/check-error-code-casing.mjs +++ b/scripts/check-error-code-casing.mjs @@ -82,8 +82,6 @@ const EXEMPT_FILES = new Map([ ['packages/metadata-protocol/src/metadata-diagnostics.ts', 'D6c spec-validation diagnostics'], ['packages/objectql/src/build-probes.test.ts', 'D6c build-probe diagnostics tests'], ['packages/objectql/src/metadata-diagnostics.test.ts', 'D6c diagnostics tests'], - ['packages/objectql/scripts/dry-run-hash-compat.ts', 'D6c findings report of a dev script'], - ['packages/objectql/src/dry-run-hash-compat.test.ts', 'D6c findings report tests'], // Zod's own vocabulary, and this file's own samples ['packages/spec/src/shared/error-map.zod.ts', "Zod issue codes, not ours"], ['packages/spec/src/api/odata.zod.ts', "OData's own error vocabulary, a foreign protocol"], diff --git a/scripts/check-type-check-coverage.mjs b/scripts/check-type-check-coverage.mjs index 5f41a1857d9..5bb1d0c7f32 100644 --- a/scripts/check-type-check-coverage.mjs +++ b/scripts/check-type-check-coverage.mjs @@ -1396,7 +1396,8 @@ const PHANTOM_PIN_DEBT = {}; // 54 package-root tool configs -- `vitest.config.ts` x32, `tsup.config.ts` // x16, `objectstack.config.ts` x5, `vitest.integration.config.ts` x1 // 11 files in a SOURCE DIRECTORY -- `scripts/i18n-extract.config.ts` x8, -// `packages/objectql/scripts/dry-run-hash-compat.ts`, +// `packages/objectql/scripts/dry-run-hash-compat.ts` (deleted since, +// with its directory, in #21853), // `packages/plugins/plugin-auth/examples/basic-usage.ts`, // `packages/cli/test/helpers/serve-process.ts` // @@ -1425,11 +1426,12 @@ const PHANTOM_PIN_DEBT = {}; // trusts"). // // AGAINST -- a `vitest.config.ts` is unchecked TypeScript exactly as much as -// `scripts/dry-run-hash-compat.ts` is, and this line can be walked around by -// moving a file UP into the package root. That is a real hole and it is left -// open knowingly, not overlooked: closing it is a decision about 42 packages' -// conventions rather than about this gate, and it is filed as its own card -// rather than decided here by whoever happened to be holding this one. +// a file in an unread source directory is, and this line can be walked +// around by moving a file UP into the package root. That is a real hole and +// it is left open knowingly, not overlooked: closing it is a decision about +// 42 packages' conventions rather than about this gate, and it is filed as +// its own card rather than decided here by whoever happened to be holding +// this one. // // SHRINK-ONLY and CLOSED to new entries, like PHANTOM_PIN_DEBT above. A package // that grows a new unread source directory fails SOURCES_COVERED; it does not @@ -1440,8 +1442,10 @@ const PHANTOM_PIN_DEBT = {}; // // SEEDED AT 10, from the census above minus the one entry this gate's own PR // repaired: `packages/objectql/scripts` -- the directory the finding was filed -// about -- now has `packages/objectql/tsconfig.scripts.json` named in that -// package's `typecheck` script, and type-checks clean. Each remaining reason +// about -- got `packages/objectql/tsconfig.scripts.json` named in that +// package's `typecheck` script, and type-checked clean. Both are gone since: +// the directory's only file, a finished one-time audit, was deleted with them +// in #21853, so nothing there is left to read. Each remaining reason // carries what the directory MEASURES, taken with the package's own config and // `rootDir` neutralised (it emits nothing) against a built dependency closure on // main @ 5886ee6d22. Those counts are prose, deliberately: nothing here compares @@ -1492,8 +1496,9 @@ const PHANTOM_PIN_DEBT = {}; // `moduleResolution: bundler`, DOM `lib` and `exclude` are argued in its own // header as things that package needs; none of the eight needs any of them, // because these configs already spell their relative imports with `.js`. -// The shape these eight copy is the minimal one -- `packages/objectql` -// (#10756) and `packages/plugins/plugin-auth` (#10869). +// The shape these eight copy is the minimal one -- `packages/plugins/ +// plugin-auth` (#10869); the first instance, `packages/objectql`'s (#10756), +// was deleted with its directory in #21853. // // THE NINTH CONFIG JOINED THE EIGHT (#15050), completing what this section // used to say was deliberately deferred. `packages/services/