From 8b2ad0abcb518a7a52323129719f777e1f2d69ed Mon Sep 17 00:00:00 2001 From: carlos-alm <127798846+carlos-alm@users.noreply.github.com> Date: Tue, 24 Mar 2026 15:43:10 -0600 Subject: [PATCH 01/15] feat(types): migrate test suite and config files to TypeScript (Phase 6) Rename all 114 test files and 2 helpers from .js to .ts, plus vitest.config and commitlint.config. Add type annotations to fix noImplicitAnyLet lint errors. Remove jsToTsResolver vite plugin (no longer needed). Drop allowJs/checkJs from tsconfig. --- .npmignore | 2 +- commitlint.config.js => commitlint.config.ts | 0 ...k.test.js => resolution-benchmark.test.ts} | 8 +-- ...ct-files.test.js => collect-files.test.ts} | 2 +- .../{context.test.js => context.test.ts} | 0 ...changes.test.js => detect-changes.test.ts} | 2 +- .../{pipeline.test.js => pipeline.test.ts} | 2 +- ...parity.test.js => dataflow-parity.test.ts} | 4 +- .../{parity.test.js => parity.test.ts} | 4 +- ...rity.test.js => query-walk-parity.test.ts} | 2 +- .../algorithms/{bfs.test.js => bfs.test.ts} | 0 ...{centrality.test.js => centrality.test.ts} | 0 .../{leiden.test.js => leiden.test.ts} | 0 .../{louvain.test.js => louvain.test.ts} | 0 ...est-path.test.js => shortest-path.test.ts} | 0 .../{tarjan.test.js => tarjan.test.ts} | 0 ...{dependency.test.js => dependency.test.ts} | 0 .../{structure.test.js => structure.test.ts} | 0 .../{temporal.test.js => temporal.test.ts} | 0 .../{risk.test.js => risk.test.ts} | 0 .../{roles.test.js => roles.test.ts} | 0 .../graph/{cycles.test.js => cycles.test.ts} | 0 .../graph/{export.test.js => export.test.ts} | 0 tests/graph/{model.test.js => model.test.ts} | 0 .../graph/{viewer.test.js => viewer.test.ts} | 0 tests/helpers/{fixtures.js => fixtures.ts} | 0 .../{node-version.js => node-version.ts} | 0 .../{cache.test.js => cache.test.ts} | 2 +- ...al.test.js => watcher-incremental.test.ts} | 4 +- .../integration/{ast.test.js => ast.test.ts} | 2 +- .../{audit.test.js => audit.test.ts} | 2 +- .../{batch.test.js => batch.test.ts} | 2 +- ...compare.test.js => branch-compare.test.ts} | 2 +- ...ld-parity.test.js => build-parity.test.ts} | 4 +- .../{build.test.js => build.test.ts} | 10 ++-- .../integration/{cfg.test.js => cfg.test.ts} | 2 +- .../{check.test.js => check.test.ts} | 2 +- .../integration/{cli.test.js => cli.test.ts} | 4 +- .../{cochange.test.js => cochange.test.ts} | 4 +- ...ommunities.test.js => communities.test.ts} | 2 +- ...{complexity.test.js => complexity.test.ts} | 2 +- .../{context.test.js => context.test.ts} | 2 +- .../{dataflow.test.js => dataflow.test.ts} | 4 +- .../{exports.test.js => exports.test.ts} | 2 +- .../{flow.test.js => flow.test.ts} | 2 +- ...ations.test.js => implementations.test.ts} | 2 +- ...edge-gap.test.js => incr-edge-gap.test.ts} | 0 ...est.js => incremental-edge-parity.test.ts} | 8 +-- ...ity.test.js => incremental-parity.test.ts} | 6 +- .../{manifesto.test.js => manifesto.test.ts} | 2 +- .../{owners.test.js => owners.test.ts} | 2 +- ...{pagination.test.js => pagination.test.ts} | 2 +- ...-names.test.js => qualified-names.test.ts} | 4 +- .../{queries.test.js => queries.test.ts} | 2 +- .../{roles.test.js => roles.test.ts} | 2 +- ...rebuild.test.js => scoped-rebuild.test.ts} | 2 +- .../{sequence.test.js => sequence.test.ts} | 4 +- .../{structure.test.js => structure.test.ts} | 2 +- .../{triage.test.js => triage.test.ts} | 2 +- ...ebuild.test.js => watcher-rebuild.test.ts} | 6 +- ...ll-langs.test.js => ast-all-langs.test.ts} | 4 +- .../{ast-nodes.test.js => ast-nodes.test.ts} | 4 +- ...ll-langs.test.js => cfg-all-langs.test.ts} | 8 +-- .../{csharp.test.js => csharp.test.ts} | 2 +- ...csharp.test.js => dataflow-csharp.test.ts} | 2 +- ...ataflow-go.test.js => dataflow-go.test.ts} | 2 +- ...low-java.test.js => dataflow-java.test.ts} | 2 +- ...pt.test.js => dataflow-javascript.test.ts} | 4 +- ...aflow-php.test.js => dataflow-php.test.ts} | 2 +- ...python.test.js => dataflow-python.test.ts} | 2 +- ...low-ruby.test.js => dataflow-ruby.test.ts} | 2 +- ...low-rust.test.js => dataflow-rust.test.ts} | 2 +- ...d-kinds.test.js => extended-kinds.test.ts} | 16 ++--- tests/parsers/{go.test.js => go.test.ts} | 2 +- tests/parsers/{java.test.js => java.test.ts} | 2 +- ...{javascript.test.js => javascript.test.ts} | 2 +- tests/parsers/{php.test.js => php.test.ts} | 2 +- tests/parsers/{ruby.test.js => ruby.test.ts} | 2 +- tests/parsers/{rust.test.js => rust.test.ts} | 2 +- .../{unified.test.js => unified.test.ts} | 0 .../{colors.test.js => colors.test.ts} | 0 ...ueries-cli.test.js => queries-cli.test.ts} | 2 +- ...atter.test.js => result-formatter.test.ts} | 2 +- .../{table.test.js => table.test.ts} | 0 .../{parity.test.js => parity.test.ts} | 0 ...search.test.js => embedder-search.test.ts} | 4 +- ...n.test.js => embedding-regression.test.ts} | 2 +- ...egy.test.js => embedding-strategy.test.ts} | 4 +- ...{boundaries.test.js => boundaries.test.ts} | 2 +- .../unit/{builder.test.js => builder.test.ts} | 2 +- tests/unit/{cfg.test.js => cfg.test.ts} | 16 ++--- ...journal.test.js => change-journal.test.ts} | 2 +- ...{complexity.test.js => complexity.test.ts} | 6 +- tests/unit/{config.test.js => config.test.ts} | 6 +- .../{constants.test.js => constants.test.ts} | 0 tests/unit/{db.test.js => db.test.ts} | 8 +-- tests/unit/{errors.test.js => errors.test.ts} | 0 ...y.test.js => in-memory-repository.test.ts} | 0 ...-exports.test.js => index-exports.test.ts} | 0 .../unit/{journal.test.js => journal.test.ts} | 2 +- tests/unit/{logger.test.js => logger.test.ts} | 2 +- tests/unit/{mcp.test.js => mcp.test.ts} | 0 ...ymbol.test.js => normalize-symbol.test.ts} | 0 tests/unit/{owners.test.js => owners.test.ts} | 2 +- tests/unit/{parser.test.js => parser.test.ts} | 0 ...install.test.js => prompt-install.test.ts} | 8 +-- ...urge-files.test.js => purge-files.test.ts} | 0 ...ries-unit.test.js => queries-unit.test.ts} | 2 +- ...-builder.test.js => query-builder.test.ts} | 2 +- .../{registry.test.js => registry.test.ts} | 4 +- ...rity.test.js => repository-parity.test.ts} | 6 +- ...{repository.test.js => repository.test.ts} | 2 +- .../unit/{resolve.test.js => resolve.test.ts} | 12 ++-- tests/unit/{roles.test.js => roles.test.ts} | 2 +- .../{snapshot.test.js => snapshot.test.ts} | 4 +- .../{structure.test.js => structure.test.ts} | 2 +- ...ate-check.test.js => update-check.test.ts} | 4 +- .../unit/{visitor.test.js => visitor.test.ts} | 2 +- tsconfig.json | 4 -- vitest.config.js | 60 ------------------- vitest.config.ts | 31 ++++++++++ 121 files changed, 182 insertions(+), 215 deletions(-) rename commitlint.config.js => commitlint.config.ts (100%) rename tests/benchmarks/resolution/{resolution-benchmark.test.js => resolution-benchmark.test.ts} (98%) rename tests/builder/{collect-files.test.js => collect-files.test.ts} (99%) rename tests/builder/{context.test.js => context.test.ts} (100%) rename tests/builder/{detect-changes.test.js => detect-changes.test.ts} (99%) rename tests/builder/{pipeline.test.js => pipeline.test.ts} (99%) rename tests/engines/{dataflow-parity.test.js => dataflow-parity.test.ts} (99%) rename tests/engines/{parity.test.js => parity.test.ts} (99%) rename tests/engines/{query-walk-parity.test.js => query-walk-parity.test.ts} (99%) rename tests/graph/algorithms/{bfs.test.js => bfs.test.ts} (100%) rename tests/graph/algorithms/{centrality.test.js => centrality.test.ts} (100%) rename tests/graph/algorithms/{leiden.test.js => leiden.test.ts} (100%) rename tests/graph/algorithms/{louvain.test.js => louvain.test.ts} (100%) rename tests/graph/algorithms/{shortest-path.test.js => shortest-path.test.ts} (100%) rename tests/graph/algorithms/{tarjan.test.js => tarjan.test.ts} (100%) rename tests/graph/builders/{dependency.test.js => dependency.test.ts} (100%) rename tests/graph/builders/{structure.test.js => structure.test.ts} (100%) rename tests/graph/builders/{temporal.test.js => temporal.test.ts} (100%) rename tests/graph/classifiers/{risk.test.js => risk.test.ts} (100%) rename tests/graph/classifiers/{roles.test.js => roles.test.ts} (100%) rename tests/graph/{cycles.test.js => cycles.test.ts} (100%) rename tests/graph/{export.test.js => export.test.ts} (100%) rename tests/graph/{model.test.js => model.test.ts} (100%) rename tests/graph/{viewer.test.js => viewer.test.ts} (100%) rename tests/helpers/{fixtures.js => fixtures.ts} (100%) rename tests/helpers/{node-version.js => node-version.ts} (100%) rename tests/incremental/{cache.test.js => cache.test.ts} (99%) rename tests/incremental/{watcher-incremental.test.js => watcher-incremental.test.ts} (98%) rename tests/integration/{ast.test.js => ast.test.ts} (99%) rename tests/integration/{audit.test.js => audit.test.ts} (99%) rename tests/integration/{batch.test.js => batch.test.ts} (99%) rename tests/integration/{branch-compare.test.js => branch-compare.test.ts} (99%) rename tests/integration/{build-parity.test.js => build-parity.test.ts} (98%) rename tests/integration/{build.test.js => build.test.ts} (98%) rename tests/integration/{cfg.test.js => cfg.test.ts} (99%) rename tests/integration/{check.test.js => check.test.ts} (99%) rename tests/integration/{cli.test.js => cli.test.ts} (99%) rename tests/integration/{cochange.test.js => cochange.test.ts} (99%) rename tests/integration/{communities.test.js => communities.test.ts} (99%) rename tests/integration/{complexity.test.js => complexity.test.ts} (99%) rename tests/integration/{context.test.js => context.test.ts} (99%) rename tests/integration/{dataflow.test.js => dataflow.test.ts} (99%) rename tests/integration/{exports.test.js => exports.test.ts} (99%) rename tests/integration/{flow.test.js => flow.test.ts} (99%) rename tests/integration/{implementations.test.js => implementations.test.ts} (99%) rename tests/integration/{incr-edge-gap.test.js => incr-edge-gap.test.ts} (100%) rename tests/integration/{incremental-edge-parity.test.js => incremental-edge-parity.test.ts} (99%) rename tests/integration/{incremental-parity.test.js => incremental-parity.test.ts} (98%) rename tests/integration/{manifesto.test.js => manifesto.test.ts} (99%) rename tests/integration/{owners.test.js => owners.test.ts} (99%) rename tests/integration/{pagination.test.js => pagination.test.ts} (99%) rename tests/integration/{qualified-names.test.js => qualified-names.test.ts} (99%) rename tests/integration/{queries.test.js => queries.test.ts} (99%) rename tests/integration/{roles.test.js => roles.test.ts} (99%) rename tests/integration/{scoped-rebuild.test.js => scoped-rebuild.test.ts} (99%) rename tests/integration/{sequence.test.js => sequence.test.ts} (99%) rename tests/integration/{structure.test.js => structure.test.ts} (99%) rename tests/integration/{triage.test.js => triage.test.ts} (99%) rename tests/integration/{watcher-rebuild.test.js => watcher-rebuild.test.ts} (98%) rename tests/parsers/{ast-all-langs.test.js => ast-all-langs.test.ts} (99%) rename tests/parsers/{ast-nodes.test.js => ast-nodes.test.ts} (99%) rename tests/parsers/{cfg-all-langs.test.js => cfg-all-langs.test.ts} (99%) rename tests/parsers/{csharp.test.js => csharp.test.ts} (99%) rename tests/parsers/{dataflow-csharp.test.js => dataflow-csharp.test.ts} (99%) rename tests/parsers/{dataflow-go.test.js => dataflow-go.test.ts} (99%) rename tests/parsers/{dataflow-java.test.js => dataflow-java.test.ts} (99%) rename tests/parsers/{dataflow-javascript.test.js => dataflow-javascript.test.ts} (99%) rename tests/parsers/{dataflow-php.test.js => dataflow-php.test.ts} (99%) rename tests/parsers/{dataflow-python.test.js => dataflow-python.test.ts} (99%) rename tests/parsers/{dataflow-ruby.test.js => dataflow-ruby.test.ts} (99%) rename tests/parsers/{dataflow-rust.test.js => dataflow-rust.test.ts} (99%) rename tests/parsers/{extended-kinds.test.js => extended-kinds.test.ts} (99%) rename tests/parsers/{go.test.js => go.test.ts} (99%) rename tests/parsers/{java.test.js => java.test.ts} (99%) rename tests/parsers/{javascript.test.js => javascript.test.ts} (99%) rename tests/parsers/{php.test.js => php.test.ts} (99%) rename tests/parsers/{ruby.test.js => ruby.test.ts} (99%) rename tests/parsers/{rust.test.js => rust.test.ts} (99%) rename tests/parsers/{unified.test.js => unified.test.ts} (100%) rename tests/presentation/{colors.test.js => colors.test.ts} (100%) rename tests/presentation/{queries-cli.test.js => queries-cli.test.ts} (99%) rename tests/presentation/{result-formatter.test.js => result-formatter.test.ts} (99%) rename tests/presentation/{table.test.js => table.test.ts} (100%) rename tests/resolution/{parity.test.js => parity.test.ts} (100%) rename tests/search/{embedder-search.test.js => embedder-search.test.ts} (99%) rename tests/search/{embedding-regression.test.js => embedding-regression.test.ts} (99%) rename tests/search/{embedding-strategy.test.js => embedding-strategy.test.ts} (99%) rename tests/unit/{boundaries.test.js => boundaries.test.ts} (99%) rename tests/unit/{builder.test.js => builder.test.ts} (99%) rename tests/unit/{cfg.test.js => cfg.test.ts} (99%) rename tests/unit/{change-journal.test.js => change-journal.test.ts} (99%) rename tests/unit/{complexity.test.js => complexity.test.ts} (99%) rename tests/unit/{config.test.js => config.test.ts} (99%) rename tests/unit/{constants.test.js => constants.test.ts} (100%) rename tests/unit/{db.test.js => db.test.ts} (99%) rename tests/unit/{errors.test.js => errors.test.ts} (100%) rename tests/unit/{in-memory-repository.test.js => in-memory-repository.test.ts} (100%) rename tests/unit/{index-exports.test.js => index-exports.test.ts} (100%) rename tests/unit/{journal.test.js => journal.test.ts} (99%) rename tests/unit/{logger.test.js => logger.test.ts} (99%) rename tests/unit/{mcp.test.js => mcp.test.ts} (100%) rename tests/unit/{normalize-symbol.test.js => normalize-symbol.test.ts} (100%) rename tests/unit/{owners.test.js => owners.test.ts} (99%) rename tests/unit/{parser.test.js => parser.test.ts} (100%) rename tests/unit/{prompt-install.test.js => prompt-install.test.ts} (98%) rename tests/unit/{purge-files.test.js => purge-files.test.ts} (100%) rename tests/unit/{queries-unit.test.js => queries-unit.test.ts} (99%) rename tests/unit/{query-builder.test.js => query-builder.test.ts} (99%) rename tests/unit/{registry.test.js => registry.test.ts} (99%) rename tests/unit/{repository-parity.test.js => repository-parity.test.ts} (99%) rename tests/unit/{repository.test.js => repository.test.ts} (99%) rename tests/unit/{resolve.test.js => resolve.test.ts} (99%) rename tests/unit/{roles.test.js => roles.test.ts} (99%) rename tests/unit/{snapshot.test.js => snapshot.test.ts} (99%) rename tests/unit/{structure.test.js => structure.test.ts} (99%) rename tests/unit/{update-check.test.js => update-check.test.ts} (99%) rename tests/unit/{visitor.test.js => visitor.test.ts} (99%) delete mode 100644 vitest.config.js create mode 100644 vitest.config.ts diff --git a/.npmignore b/.npmignore index 733a7a49..3977f0ea 100644 --- a/.npmignore +++ b/.npmignore @@ -7,5 +7,5 @@ docs/ *.db .codegraphrc.json .versionrc.json -commitlint.config.js +commitlint.config.ts codegraph-improvements.md diff --git a/commitlint.config.js b/commitlint.config.ts similarity index 100% rename from commitlint.config.js rename to commitlint.config.ts diff --git a/tests/benchmarks/resolution/resolution-benchmark.test.js b/tests/benchmarks/resolution/resolution-benchmark.test.ts similarity index 98% rename from tests/benchmarks/resolution/resolution-benchmark.test.js rename to tests/benchmarks/resolution/resolution-benchmark.test.ts index fa7c248d..00d4d99e 100644 --- a/tests/benchmarks/resolution/resolution-benchmark.test.js +++ b/tests/benchmarks/resolution/resolution-benchmark.test.ts @@ -242,10 +242,10 @@ describe('Call Resolution Precision/Recall', () => { for (const lang of languages) { describe(lang, () => { - let fixtureDir; - let resolvedEdges; - let expectedEdges; - let metrics; + let fixtureDir: string; + let resolvedEdges: any[]; + let expectedEdges: any[]; + let metrics: any; beforeAll(async () => { fixtureDir = copyFixture(lang); diff --git a/tests/builder/collect-files.test.js b/tests/builder/collect-files.test.ts similarity index 99% rename from tests/builder/collect-files.test.js rename to tests/builder/collect-files.test.ts index 4d4955c5..8cc0c937 100644 --- a/tests/builder/collect-files.test.js +++ b/tests/builder/collect-files.test.ts @@ -8,7 +8,7 @@ import { afterAll, beforeAll, describe, expect, it } from 'vitest'; import { PipelineContext } from '../../src/domain/graph/builder/context.js'; import { collectFiles } from '../../src/domain/graph/builder/stages/collect-files.js'; -let tmpDir; +let tmpDir: string; beforeAll(() => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-stage-collect-')); diff --git a/tests/builder/context.test.js b/tests/builder/context.test.ts similarity index 100% rename from tests/builder/context.test.js rename to tests/builder/context.test.ts diff --git a/tests/builder/detect-changes.test.js b/tests/builder/detect-changes.test.ts similarity index 99% rename from tests/builder/detect-changes.test.js rename to tests/builder/detect-changes.test.ts index 2555b097..0d13fd72 100644 --- a/tests/builder/detect-changes.test.js +++ b/tests/builder/detect-changes.test.ts @@ -10,7 +10,7 @@ import { PipelineContext } from '../../src/domain/graph/builder/context.js'; import { detectChanges } from '../../src/domain/graph/builder/stages/detect-changes.js'; import { writeJournalHeader } from '../../src/domain/graph/journal.js'; -let tmpDir; +let tmpDir: string; beforeAll(() => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-stage-detect-')); diff --git a/tests/builder/pipeline.test.js b/tests/builder/pipeline.test.ts similarity index 99% rename from tests/builder/pipeline.test.js rename to tests/builder/pipeline.test.ts index 76624d61..350808fb 100644 --- a/tests/builder/pipeline.test.js +++ b/tests/builder/pipeline.test.ts @@ -11,7 +11,7 @@ import { afterAll, beforeAll, describe, expect, it } from 'vitest'; import { buildGraph } from '../../src/domain/graph/builder/pipeline.js'; const FIXTURE_DIR = path.join(import.meta.dirname, '..', 'fixtures', 'sample-project'); -let tmpDir; +let tmpDir: string; beforeAll(() => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-pipeline-')); diff --git a/tests/engines/dataflow-parity.test.js b/tests/engines/dataflow-parity.test.ts similarity index 99% rename from tests/engines/dataflow-parity.test.js rename to tests/engines/dataflow-parity.test.ts index 0ed2d996..ce1c314d 100644 --- a/tests/engines/dataflow-parity.test.js +++ b/tests/engines/dataflow-parity.test.ts @@ -16,8 +16,8 @@ import { createParsers, getParser } from '../../src/domain/parser.js'; import { extractDataflow } from '../../src/features/dataflow.js'; import { isNativeAvailable } from '../../src/infrastructure/native.js'; -let native; -let parsers; +let native: any; +let parsers: any; let nativeHasDataflow = false; /** diff --git a/tests/engines/parity.test.js b/tests/engines/parity.test.ts similarity index 99% rename from tests/engines/parity.test.js rename to tests/engines/parity.test.ts index ef966af3..d90fd42d 100644 --- a/tests/engines/parity.test.js +++ b/tests/engines/parity.test.ts @@ -23,8 +23,8 @@ import { } from '../../src/domain/parser.js'; import { isNativeAvailable } from '../../src/infrastructure/native.js'; -let native; -let parsers; +let native: any; +let parsers: any; function wasmExtract(code, filePath) { const parser = getParser(parsers, filePath); diff --git a/tests/engines/query-walk-parity.test.js b/tests/engines/query-walk-parity.test.ts similarity index 99% rename from tests/engines/query-walk-parity.test.js rename to tests/engines/query-walk-parity.test.ts index 05335c5c..2b1f3e78 100644 --- a/tests/engines/query-walk-parity.test.js +++ b/tests/engines/query-walk-parity.test.ts @@ -12,7 +12,7 @@ import { beforeAll, describe, expect, it } from 'vitest'; import { createParsers, getParser, parseFileAuto } from '../../src/domain/parser.js'; import { extractSymbols } from '../../src/extractors/javascript.js'; -let parsers; +let parsers: any; beforeAll(async () => { parsers = await createParsers(); diff --git a/tests/graph/algorithms/bfs.test.js b/tests/graph/algorithms/bfs.test.ts similarity index 100% rename from tests/graph/algorithms/bfs.test.js rename to tests/graph/algorithms/bfs.test.ts diff --git a/tests/graph/algorithms/centrality.test.js b/tests/graph/algorithms/centrality.test.ts similarity index 100% rename from tests/graph/algorithms/centrality.test.js rename to tests/graph/algorithms/centrality.test.ts diff --git a/tests/graph/algorithms/leiden.test.js b/tests/graph/algorithms/leiden.test.ts similarity index 100% rename from tests/graph/algorithms/leiden.test.js rename to tests/graph/algorithms/leiden.test.ts diff --git a/tests/graph/algorithms/louvain.test.js b/tests/graph/algorithms/louvain.test.ts similarity index 100% rename from tests/graph/algorithms/louvain.test.js rename to tests/graph/algorithms/louvain.test.ts diff --git a/tests/graph/algorithms/shortest-path.test.js b/tests/graph/algorithms/shortest-path.test.ts similarity index 100% rename from tests/graph/algorithms/shortest-path.test.js rename to tests/graph/algorithms/shortest-path.test.ts diff --git a/tests/graph/algorithms/tarjan.test.js b/tests/graph/algorithms/tarjan.test.ts similarity index 100% rename from tests/graph/algorithms/tarjan.test.js rename to tests/graph/algorithms/tarjan.test.ts diff --git a/tests/graph/builders/dependency.test.js b/tests/graph/builders/dependency.test.ts similarity index 100% rename from tests/graph/builders/dependency.test.js rename to tests/graph/builders/dependency.test.ts diff --git a/tests/graph/builders/structure.test.js b/tests/graph/builders/structure.test.ts similarity index 100% rename from tests/graph/builders/structure.test.js rename to tests/graph/builders/structure.test.ts diff --git a/tests/graph/builders/temporal.test.js b/tests/graph/builders/temporal.test.ts similarity index 100% rename from tests/graph/builders/temporal.test.js rename to tests/graph/builders/temporal.test.ts diff --git a/tests/graph/classifiers/risk.test.js b/tests/graph/classifiers/risk.test.ts similarity index 100% rename from tests/graph/classifiers/risk.test.js rename to tests/graph/classifiers/risk.test.ts diff --git a/tests/graph/classifiers/roles.test.js b/tests/graph/classifiers/roles.test.ts similarity index 100% rename from tests/graph/classifiers/roles.test.js rename to tests/graph/classifiers/roles.test.ts diff --git a/tests/graph/cycles.test.js b/tests/graph/cycles.test.ts similarity index 100% rename from tests/graph/cycles.test.js rename to tests/graph/cycles.test.ts diff --git a/tests/graph/export.test.js b/tests/graph/export.test.ts similarity index 100% rename from tests/graph/export.test.js rename to tests/graph/export.test.ts diff --git a/tests/graph/model.test.js b/tests/graph/model.test.ts similarity index 100% rename from tests/graph/model.test.js rename to tests/graph/model.test.ts diff --git a/tests/graph/viewer.test.js b/tests/graph/viewer.test.ts similarity index 100% rename from tests/graph/viewer.test.js rename to tests/graph/viewer.test.ts diff --git a/tests/helpers/fixtures.js b/tests/helpers/fixtures.ts similarity index 100% rename from tests/helpers/fixtures.js rename to tests/helpers/fixtures.ts diff --git a/tests/helpers/node-version.js b/tests/helpers/node-version.ts similarity index 100% rename from tests/helpers/node-version.js rename to tests/helpers/node-version.ts diff --git a/tests/incremental/cache.test.js b/tests/incremental/cache.test.ts similarity index 99% rename from tests/incremental/cache.test.js rename to tests/incremental/cache.test.ts index 46ee6290..0cfc2b92 100644 --- a/tests/incremental/cache.test.js +++ b/tests/incremental/cache.test.ts @@ -10,7 +10,7 @@ import { isNativeAvailable, loadNative } from '../../src/infrastructure/native.j const hasNative = isNativeAvailable(); describe.skipIf(!hasNative)('ParseTreeCache', () => { - let cache; + let cache: any; beforeEach(() => { const native = loadNative(); diff --git a/tests/incremental/watcher-incremental.test.js b/tests/incremental/watcher-incremental.test.ts similarity index 98% rename from tests/incremental/watcher-incremental.test.js rename to tests/incremental/watcher-incremental.test.ts index 8c273203..e8220d5d 100644 --- a/tests/incremental/watcher-incremental.test.js +++ b/tests/incremental/watcher-incremental.test.ts @@ -17,8 +17,8 @@ import { isNativeAvailable } from '../../src/infrastructure/native.js'; const hasNative = isNativeAvailable(); describe.skipIf(!hasNative)('Watcher incremental flow', () => { - let tmpDir; - let cache; + let tmpDir: string; + let cache: any; beforeEach(() => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-inc-')); diff --git a/tests/integration/ast.test.js b/tests/integration/ast.test.ts similarity index 99% rename from tests/integration/ast.test.js rename to tests/integration/ast.test.ts index 706684d9..8c731322 100644 --- a/tests/integration/ast.test.js +++ b/tests/integration/ast.test.ts @@ -30,7 +30,7 @@ function insertAstNode(db, file, line, kind, name, text, receiver, parentNodeId) // ─── Fixture DB ──────────────────────────────────────────────────────── -let tmpDir, dbPath; +let tmpDir: string, dbPath: string; beforeAll(() => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-ast-')); diff --git a/tests/integration/audit.test.js b/tests/integration/audit.test.ts similarity index 99% rename from tests/integration/audit.test.js rename to tests/integration/audit.test.ts index f6d00768..547bb32e 100644 --- a/tests/integration/audit.test.js +++ b/tests/integration/audit.test.ts @@ -46,7 +46,7 @@ function insertComplexity(db, nodeId, opts = {}) { // ─── Fixture DB ──────────────────────────────────────────────────────── -let tmpDir, dbPath; +let tmpDir: string, dbPath: string; beforeAll(() => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-audit-')); diff --git a/tests/integration/batch.test.js b/tests/integration/batch.test.ts similarity index 99% rename from tests/integration/batch.test.js rename to tests/integration/batch.test.ts index 216c98e3..032ca6fe 100644 --- a/tests/integration/batch.test.js +++ b/tests/integration/batch.test.ts @@ -45,7 +45,7 @@ function insertEdge(db, sourceId, targetId, kind = 'calls', confidence = 1.0) { // ─── Fixture DB ──────────────────────────────────────────────────────── -let tmpDir, dbPath; +let tmpDir: string, dbPath: string; beforeAll(() => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-batch-')); diff --git a/tests/integration/branch-compare.test.js b/tests/integration/branch-compare.test.ts similarity index 99% rename from tests/integration/branch-compare.test.js rename to tests/integration/branch-compare.test.ts index 0a276117..b67326ae 100644 --- a/tests/integration/branch-compare.test.js +++ b/tests/integration/branch-compare.test.ts @@ -12,7 +12,7 @@ import path from 'node:path'; import { afterAll, beforeAll, describe, expect, test } from 'vitest'; import { branchCompareData, branchCompareMermaid } from '../../src/features/branch-compare.js'; -let tmpDir; +let tmpDir: string; beforeAll(() => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-bc-test-')); diff --git a/tests/integration/build-parity.test.js b/tests/integration/build-parity.test.ts similarity index 98% rename from tests/integration/build-parity.test.js rename to tests/integration/build-parity.test.ts index 98ce843d..a059d452 100644 --- a/tests/integration/build-parity.test.js +++ b/tests/integration/build-parity.test.ts @@ -61,8 +61,8 @@ function readGraph(dbPath) { } describeOrSkip('Build parity: native vs WASM', () => { - let wasmDir; - let nativeDir; + let wasmDir: string; + let nativeDir: string; beforeAll(async () => { // Create two temp copies of the fixture diff --git a/tests/integration/build.test.js b/tests/integration/build.test.ts similarity index 98% rename from tests/integration/build.test.js rename to tests/integration/build.test.ts index cfa1ebac..8ced90fe 100644 --- a/tests/integration/build.test.js +++ b/tests/integration/build.test.ts @@ -39,7 +39,7 @@ export function main() { `.trimStart(), }; -let tmpDir, dbPath; +let tmpDir: string, dbPath: string; beforeAll(async () => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-build-')); @@ -154,7 +154,7 @@ describe('buildGraph', () => { }); describe('three-tier incremental builds', () => { - let incrDir, incrDbPath; + let incrDir: string, incrDbPath: string; beforeAll(async () => { incrDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-tier-')); @@ -361,7 +361,7 @@ describe('three-tier incremental builds', () => { }); describe('nested function caller attribution', () => { - let nestDir, nestDbPath; + let nestDir: string, nestDbPath: string; beforeAll(async () => { nestDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-nested-')); @@ -406,7 +406,7 @@ describe('nested function caller attribution', () => { }); describe('version/engine mismatch auto-promotes to full rebuild', () => { - let promoDir, promoDbPath; + let promoDir: string, promoDbPath: string; beforeAll(async () => { promoDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-promo-')); @@ -480,7 +480,7 @@ describe('version/engine mismatch auto-promotes to full rebuild', () => { }); describe('typed method call resolution', () => { - let typedDir, typedDbPath; + let typedDir: string, typedDbPath: string; beforeAll(async () => { typedDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-typed-')); diff --git a/tests/integration/cfg.test.js b/tests/integration/cfg.test.ts similarity index 99% rename from tests/integration/cfg.test.js rename to tests/integration/cfg.test.ts index 548042ea..33284ce6 100644 --- a/tests/integration/cfg.test.js +++ b/tests/integration/cfg.test.ts @@ -36,7 +36,7 @@ function insertEdge(db, fnNodeId, sourceBlockId, targetBlockId, kind) { // ─── Fixture DB ──────────────────────────────────────────────────────── -let tmpDir, dbPath; +let tmpDir: string, dbPath: string; beforeAll(() => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-cfg-')); diff --git a/tests/integration/check.test.js b/tests/integration/check.test.ts similarity index 99% rename from tests/integration/check.test.js rename to tests/integration/check.test.ts index 1e339188..9866f20e 100644 --- a/tests/integration/check.test.js +++ b/tests/integration/check.test.ts @@ -36,7 +36,7 @@ function insertEdge(db, sourceId, targetId, kind) { // ─── Fixture DB ──────────────────────────────────────────────────────── -let tmpDir, dbPath, db; +let tmpDir: string, dbPath: string, db: any; beforeAll(() => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-check-')); diff --git a/tests/integration/cli.test.js b/tests/integration/cli.test.ts similarity index 99% rename from tests/integration/cli.test.js rename to tests/integration/cli.test.ts index 52cc8ff1..9dc6b743 100644 --- a/tests/integration/cli.test.js +++ b/tests/integration/cli.test.ts @@ -40,7 +40,7 @@ export function main() { `.trimStart(), }; -let tmpDir, tmpHome, dbPath; +let tmpDir: string, tmpHome: string, dbPath: string; /** Run the CLI and return stdout as a string. Throws on non-zero exit. */ function run(...args) { @@ -242,7 +242,7 @@ describe.skipIf(!canStripTypes)('CLI smoke tests', () => { // ─── Registry CLI ─────────────────────────────────────────────────────── describe.skipIf(!canStripTypes)('Registry CLI commands', () => { - let tmpHome; + let tmpHome: string; /** Run CLI with isolated HOME to avoid touching real registry */ function runReg(...args) { diff --git a/tests/integration/cochange.test.js b/tests/integration/cochange.test.ts similarity index 99% rename from tests/integration/cochange.test.js rename to tests/integration/cochange.test.ts index 5dbed8fb..e33dc9d8 100644 --- a/tests/integration/cochange.test.js +++ b/tests/integration/cochange.test.ts @@ -111,7 +111,7 @@ describe('computeCoChanges', () => { // ─── B. DB integration (coChangeData / coChangeTopData) ────────────── describe('coChangeData + coChangeTopData', () => { - let tmpDir, dbPath; + let tmpDir: string, dbPath: string; beforeAll(() => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-cochange-')); @@ -207,7 +207,7 @@ describe('coChangeData + coChangeTopData', () => { // ─── C. scanGitHistory (real git repo) ─────────────────────────────── describe('scanGitHistory', () => { - let tmpDir; + let tmpDir: string; beforeAll(() => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-git-')); diff --git a/tests/integration/communities.test.js b/tests/integration/communities.test.ts similarity index 99% rename from tests/integration/communities.test.js rename to tests/integration/communities.test.ts index 90324367..b48df12f 100644 --- a/tests/integration/communities.test.js +++ b/tests/integration/communities.test.ts @@ -16,7 +16,7 @@ import { createTestRepo } from '../helpers/fixtures.js'; // ─── Fixture ────────────────────────────────────────────────────────── -let repo; +let repo: any; beforeAll(() => { ({ repo } = createTestRepo() diff --git a/tests/integration/complexity.test.js b/tests/integration/complexity.test.ts similarity index 99% rename from tests/integration/complexity.test.js rename to tests/integration/complexity.test.ts index 8d7ea175..ddfbd8a8 100644 --- a/tests/integration/complexity.test.js +++ b/tests/integration/complexity.test.ts @@ -76,7 +76,7 @@ function insertComplexity( // ─── Fixture DB ──────────────────────────────────────────────────────── -let tmpDir, dbPath; +let tmpDir: string, dbPath: string; beforeAll(() => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-complexity-')); diff --git a/tests/integration/context.test.js b/tests/integration/context.test.ts similarity index 99% rename from tests/integration/context.test.js rename to tests/integration/context.test.ts index 14e39b05..1ee3e91e 100644 --- a/tests/integration/context.test.js +++ b/tests/integration/context.test.ts @@ -29,7 +29,7 @@ function insertEdge(db, sourceId, targetId, kind) { // ─── Fixture ────────────────────────────────────────────────────────── -let tmpDir, dbPath; +let tmpDir: string, dbPath: string; beforeAll(() => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-context-')); diff --git a/tests/integration/dataflow.test.js b/tests/integration/dataflow.test.ts similarity index 99% rename from tests/integration/dataflow.test.js rename to tests/integration/dataflow.test.ts index 78eb679d..29bd7c95 100644 --- a/tests/integration/dataflow.test.js +++ b/tests/integration/dataflow.test.ts @@ -43,7 +43,7 @@ function insertDataflow(db, sourceId, targetId, kind, opts = {}) { // ─── Fixture DB ──────────────────────────────────────────────────────── -let tmpDir, dbPath; +let tmpDir: string, dbPath: string; beforeAll(() => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-dataflow-')); @@ -300,7 +300,7 @@ describe('dataflowImpactData', () => { // ─── Empty dataflow table ────────────────────────────────────────────── describe('empty dataflow', () => { - let emptyDbPath; + let emptyDbPath: string; beforeAll(() => { const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-df-empty-')); diff --git a/tests/integration/exports.test.js b/tests/integration/exports.test.ts similarity index 99% rename from tests/integration/exports.test.js rename to tests/integration/exports.test.ts index 390bf7c1..46762128 100644 --- a/tests/integration/exports.test.js +++ b/tests/integration/exports.test.ts @@ -47,7 +47,7 @@ function insertEdge(db, sourceId, targetId, kind, confidence = 1.0) { // ─── Fixture DB ──────────────────────────────────────────────────────── -let tmpDir, dbPath; +let tmpDir: string, dbPath: string; beforeAll(() => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-exports-')); diff --git a/tests/integration/flow.test.js b/tests/integration/flow.test.ts similarity index 99% rename from tests/integration/flow.test.js rename to tests/integration/flow.test.ts index fc4ca7aa..8e1eba6f 100644 --- a/tests/integration/flow.test.js +++ b/tests/integration/flow.test.ts @@ -37,7 +37,7 @@ function insertEdge(db, sourceId, targetId, kind, confidence = 1.0) { // ─── Fixture DB ──────────────────────────────────────────────────────── -let tmpDir, dbPath; +let tmpDir: string, dbPath: string; beforeAll(() => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-flow-')); diff --git a/tests/integration/implementations.test.js b/tests/integration/implementations.test.ts similarity index 99% rename from tests/integration/implementations.test.js rename to tests/integration/implementations.test.ts index bdd56389..c4945dd3 100644 --- a/tests/integration/implementations.test.js +++ b/tests/integration/implementations.test.ts @@ -48,7 +48,7 @@ function insertEdge(db, sourceId, targetId, kind, confidence = 1.0) { // ─── Fixture DB ──────────────────────────────────────────────────────── -let tmpDir, dbPath; +let tmpDir: string, dbPath: string; beforeAll(() => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-impl-')); diff --git a/tests/integration/incr-edge-gap.test.js b/tests/integration/incr-edge-gap.test.ts similarity index 100% rename from tests/integration/incr-edge-gap.test.js rename to tests/integration/incr-edge-gap.test.ts diff --git a/tests/integration/incremental-edge-parity.test.js b/tests/integration/incremental-edge-parity.test.ts similarity index 99% rename from tests/integration/incremental-edge-parity.test.js rename to tests/integration/incremental-edge-parity.test.ts index 0d87dbea..06e097f4 100644 --- a/tests/integration/incremental-edge-parity.test.js +++ b/tests/integration/incremental-edge-parity.test.ts @@ -102,7 +102,7 @@ async function buildAndCompare(fixtureDir, mutate) { describe('Incremental edge parity (CI gate)', () => { // Scenario 1: Comment-only touch — edges must be identical describe('comment-only touch', () => { - let result; + let result: any; beforeAll(async () => { result = await buildAndCompare(FIXTURE_DIR, (dir) => { @@ -122,7 +122,7 @@ describe('Incremental edge parity (CI gate)', () => { // Scenario 2: Body edit — change function implementation, keep exports describe('body edit (same exports)', () => { - let result; + let result: any; beforeAll(async () => { result = await buildAndCompare(FIXTURE_DIR, (dir) => { @@ -147,7 +147,7 @@ describe('Incremental edge parity (CI gate)', () => { // Scenario 3: New export added — edges from consumers should resolve describe('new export added', () => { - let result; + let result: any; beforeAll(async () => { result = await buildAndCompare(FIXTURE_DIR, (dir) => { @@ -206,7 +206,7 @@ describe('Incremental edge parity (CI gate)', () => { // Scenario 4: File deletion — stale edges must be purged describe('file deletion', () => { - let result; + let result: any; beforeAll(async () => { result = await buildAndCompare(FIXTURE_DIR, (dir) => { diff --git a/tests/integration/incremental-parity.test.js b/tests/integration/incremental-parity.test.ts similarity index 98% rename from tests/integration/incremental-parity.test.js rename to tests/integration/incremental-parity.test.ts index 21a2a56c..d632503e 100644 --- a/tests/integration/incremental-parity.test.js +++ b/tests/integration/incremental-parity.test.ts @@ -87,9 +87,9 @@ function readAnalysisTables(dbPath) { } describe('Incremental build parity: full vs incremental', () => { - let fullDir; - let incrDir; - let tmpBase; + let fullDir: string; + let incrDir: string; + let tmpBase: string; beforeAll(async () => { tmpBase = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-incr-parity-')); diff --git a/tests/integration/manifesto.test.js b/tests/integration/manifesto.test.ts similarity index 99% rename from tests/integration/manifesto.test.js rename to tests/integration/manifesto.test.ts index d9b5d93f..73691e9a 100644 --- a/tests/integration/manifesto.test.js +++ b/tests/integration/manifesto.test.ts @@ -52,7 +52,7 @@ function insertEdge(db, sourceId, targetId, kind, confidence = 1.0) { // ─── Fixture DB ──────────────────────────────────────────────────────── -let tmpDir, dbPath; +let tmpDir: string, dbPath: string; beforeAll(() => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-manifesto-')); diff --git a/tests/integration/owners.test.js b/tests/integration/owners.test.ts similarity index 99% rename from tests/integration/owners.test.js rename to tests/integration/owners.test.ts index 7d26f958..025e1e1c 100644 --- a/tests/integration/owners.test.js +++ b/tests/integration/owners.test.ts @@ -22,7 +22,7 @@ function insertEdge(db, sourceId, targetId, kind, confidence = 1.0) { // ─── Fixture DB ──────────────────────────────────────────────────────── -let tmpDir, dbPath; +let tmpDir: string, dbPath: string; beforeAll(() => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-owners-')); diff --git a/tests/integration/pagination.test.js b/tests/integration/pagination.test.ts similarity index 99% rename from tests/integration/pagination.test.js rename to tests/integration/pagination.test.ts index ecf31c5d..4bf5d0b0 100644 --- a/tests/integration/pagination.test.js +++ b/tests/integration/pagination.test.ts @@ -59,7 +59,7 @@ function insertEdge(db, sourceId, targetId, kind, confidence = 1.0) { // ─── Fixture DB ──────────────────────────────────────────────────────── -let tmpDir, dbPath, dbForExport; +let tmpDir: string, dbPath: string, dbForExport: any; beforeAll(() => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-pagination-')); diff --git a/tests/integration/qualified-names.test.js b/tests/integration/qualified-names.test.ts similarity index 99% rename from tests/integration/qualified-names.test.js rename to tests/integration/qualified-names.test.ts index 3fc1beae..61b5eeb9 100644 --- a/tests/integration/qualified-names.test.js +++ b/tests/integration/qualified-names.test.ts @@ -49,8 +49,8 @@ export class MathUtils { `, }; -let tmpDir; -let dbPath; +let tmpDir: string; +let dbPath: string; beforeAll(async () => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'cg-qualified-')); diff --git a/tests/integration/queries.test.js b/tests/integration/queries.test.ts similarity index 99% rename from tests/integration/queries.test.js rename to tests/integration/queries.test.ts index d4f7f2b2..6381cdb2 100644 --- a/tests/integration/queries.test.js +++ b/tests/integration/queries.test.ts @@ -58,7 +58,7 @@ function insertEdge(db, sourceId, targetId, kind, confidence = 1.0) { // ─── Fixture DB ──────────────────────────────────────────────────────── -let tmpDir, dbPath; +let tmpDir: string, dbPath: string; beforeAll(() => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-queries-')); diff --git a/tests/integration/roles.test.js b/tests/integration/roles.test.ts similarity index 99% rename from tests/integration/roles.test.js rename to tests/integration/roles.test.ts index 9fce5e84..9a6f29da 100644 --- a/tests/integration/roles.test.js +++ b/tests/integration/roles.test.ts @@ -38,7 +38,7 @@ function insertEdge(db, sourceId, targetId, kind, confidence = 1.0) { // ─── Fixture DB ──────────────────────────────────────────────────────── -let tmpDir, dbPath; +let tmpDir: string, dbPath: string; beforeAll(() => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-roles-')); diff --git a/tests/integration/scoped-rebuild.test.js b/tests/integration/scoped-rebuild.test.ts similarity index 99% rename from tests/integration/scoped-rebuild.test.js rename to tests/integration/scoped-rebuild.test.ts index 5f6eaa9a..b3ff350d 100644 --- a/tests/integration/scoped-rebuild.test.js +++ b/tests/integration/scoped-rebuild.test.ts @@ -14,7 +14,7 @@ import { buildGraph } from '../../src/domain/graph/builder.js'; const FIXTURE_DIR = path.join(import.meta.dirname, '..', 'fixtures', 'sample-project'); -let tmpDir; +let tmpDir: string; function copyFixture() { const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-scoped-')); diff --git a/tests/integration/sequence.test.js b/tests/integration/sequence.test.ts similarity index 99% rename from tests/integration/sequence.test.js rename to tests/integration/sequence.test.ts index e8705820..e4f01338 100644 --- a/tests/integration/sequence.test.js +++ b/tests/integration/sequence.test.ts @@ -41,7 +41,7 @@ function insertEdge(db, sourceId, targetId, kind, confidence = 1.0) { // ─── InMemory Fixture ───────────────────────────────────────────────── -let repo; +let repo: any; beforeAll(() => { ({ repo } = createTestRepo() @@ -199,7 +199,7 @@ describe('sequenceToMermaid', () => { // ─── Dataflow annotations (SQLite — requires dataflow table) ────────── describe('dataflow annotations', () => { - let dfTmpDir, dfDbPath; + let dfTmpDir: string, dfDbPath: string; beforeAll(() => { dfTmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-seq-df-')); diff --git a/tests/integration/structure.test.js b/tests/integration/structure.test.ts similarity index 99% rename from tests/integration/structure.test.js rename to tests/integration/structure.test.ts index fe97a118..762918e4 100644 --- a/tests/integration/structure.test.js +++ b/tests/integration/structure.test.ts @@ -36,7 +36,7 @@ export function main() { printSum(1, double(2)); } `.trimStart(), }; -let tmpDir, dbPath; +let tmpDir: string, dbPath: string; beforeAll(async () => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-structure-')); diff --git a/tests/integration/triage.test.js b/tests/integration/triage.test.ts similarity index 99% rename from tests/integration/triage.test.js rename to tests/integration/triage.test.ts index 32a2b692..e7bdcb86 100644 --- a/tests/integration/triage.test.js +++ b/tests/integration/triage.test.ts @@ -10,7 +10,7 @@ import { createTestRepo } from '../helpers/fixtures.js'; // ─── Fixture ────────────────────────────────────────────────────────── -let repo; +let repo: any; beforeAll(() => { const builder = createTestRepo() diff --git a/tests/integration/watcher-rebuild.test.js b/tests/integration/watcher-rebuild.test.ts similarity index 98% rename from tests/integration/watcher-rebuild.test.js rename to tests/integration/watcher-rebuild.test.ts index b2f03142..be394f71 100644 --- a/tests/integration/watcher-rebuild.test.js +++ b/tests/integration/watcher-rebuild.test.ts @@ -92,9 +92,9 @@ function makeStmts(db) { } describe('Watcher rebuildFile parity (#533)', () => { - let fullDir; - let watcherDir; - let tmpBase; + let fullDir: string; + let watcherDir: string; + let tmpBase: string; beforeAll(async () => { tmpBase = fs.mkdtempSync(path.join(os.tmpdir(), 'cg-watcher-533-')); diff --git a/tests/parsers/ast-all-langs.test.js b/tests/parsers/ast-all-langs.test.ts similarity index 99% rename from tests/parsers/ast-all-langs.test.js rename to tests/parsers/ast-all-langs.test.ts index b4130a18..28e2d2b7 100644 --- a/tests/parsers/ast-all-langs.test.js +++ b/tests/parsers/ast-all-langs.test.ts @@ -40,7 +40,7 @@ function queryAll(db) { // ─── JS-side: buildAstNodes accepts astNodes for non-JS files ──────── describe('buildAstNodes — non-JS language astNodes', () => { - let tmpDir, db; + let tmpDir: string, db: any; beforeAll(() => { ({ tmpDir, db } = createTempDb()); @@ -266,7 +266,7 @@ function nativeSupportsMultiLangAst() { const canTestMultiLang = nativeSupportsMultiLangAst(); describe.skipIf(!canTestMultiLang)('native AST nodes — multi-language', () => { - let tmpDir, db; + let tmpDir: string, db: any; beforeAll(async () => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-ast-multilang-')); diff --git a/tests/parsers/ast-nodes.test.js b/tests/parsers/ast-nodes.test.ts similarity index 99% rename from tests/parsers/ast-nodes.test.js rename to tests/parsers/ast-nodes.test.ts index 3d3c3de7..5f3e9179 100644 --- a/tests/parsers/ast-nodes.test.js +++ b/tests/parsers/ast-nodes.test.ts @@ -47,7 +47,7 @@ function helper() { // ─── Setup ──────────────────────────────────────────────────────────── -let tmpDir, dbPath, db; +let tmpDir: string, dbPath: string, db: any; beforeAll(async () => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-ast-extract-')); @@ -210,7 +210,7 @@ function nativeSupportsAstNodes() { const canTestNative = nativeSupportsAstNodes(); describe.skipIf(!canTestNative)('buildAstNodes — native engine', () => { - let nativeTmpDir, nativeDbPath, nativeDb; + let nativeTmpDir: string, nativeDbPath: string, nativeDb: any; function queryNativeAstNodes(kind) { return nativeDb.prepare('SELECT * FROM ast_nodes WHERE kind = ? ORDER BY line').all(kind); diff --git a/tests/parsers/cfg-all-langs.test.js b/tests/parsers/cfg-all-langs.test.ts similarity index 99% rename from tests/parsers/cfg-all-langs.test.js rename to tests/parsers/cfg-all-langs.test.ts index f37c9a95..b13308c8 100644 --- a/tests/parsers/cfg-all-langs.test.js +++ b/tests/parsers/cfg-all-langs.test.ts @@ -34,7 +34,7 @@ function createTempDb() { // ─── JS-side: buildCFGData accepts native def.cfg ───────────────────── describe('buildCFGData — native CFG path', () => { - let tmpDir, db; + let tmpDir: string, db: any; beforeAll(() => { ({ tmpDir, db } = createTempDb()); @@ -294,7 +294,7 @@ const hasFixedCfg = (() => { })(); describe.skipIf(!canTestNativeCfg)('native CFG — multi-language', () => { - let tmpDir; + let tmpDir: string; const nativeResults = new Map(); beforeAll(async () => { @@ -389,9 +389,9 @@ describe.skipIf(!canTestNativeCfg)('native CFG — multi-language', () => { // ─── Parity: native vs WASM CFG ────────────────────────────────────── describe.skipIf(!canTestNativeCfg || !hasFixedCfg)('native vs WASM CFG parity', () => { - let tmpDir; + let tmpDir: string; const nativeResults = new Map(); - let parsers; + let parsers: any; const LANG_MAP = { '.js': 'javascript', diff --git a/tests/parsers/csharp.test.js b/tests/parsers/csharp.test.ts similarity index 99% rename from tests/parsers/csharp.test.js rename to tests/parsers/csharp.test.ts index 46ccd25c..8f9f4e0c 100644 --- a/tests/parsers/csharp.test.js +++ b/tests/parsers/csharp.test.ts @@ -2,7 +2,7 @@ import { beforeAll, describe, expect, it } from 'vitest'; import { createParsers, extractCSharpSymbols } from '../../src/domain/parser.js'; describe('C# parser', () => { - let parsers; + let parsers: any; beforeAll(async () => { parsers = await createParsers(); diff --git a/tests/parsers/dataflow-csharp.test.js b/tests/parsers/dataflow-csharp.test.ts similarity index 99% rename from tests/parsers/dataflow-csharp.test.js rename to tests/parsers/dataflow-csharp.test.ts index 30f91a2d..613438e1 100644 --- a/tests/parsers/dataflow-csharp.test.js +++ b/tests/parsers/dataflow-csharp.test.ts @@ -6,7 +6,7 @@ import { createParsers } from '../../src/domain/parser.js'; import { extractDataflow } from '../../src/features/dataflow.js'; describe('extractDataflow — C#', () => { - let parsers; + let parsers: any; beforeAll(async () => { parsers = await createParsers(); diff --git a/tests/parsers/dataflow-go.test.js b/tests/parsers/dataflow-go.test.ts similarity index 99% rename from tests/parsers/dataflow-go.test.js rename to tests/parsers/dataflow-go.test.ts index 2f73d294..bbc7f8ba 100644 --- a/tests/parsers/dataflow-go.test.js +++ b/tests/parsers/dataflow-go.test.ts @@ -6,7 +6,7 @@ import { createParsers } from '../../src/domain/parser.js'; import { extractDataflow } from '../../src/features/dataflow.js'; describe('extractDataflow — Go', () => { - let parsers; + let parsers: any; beforeAll(async () => { parsers = await createParsers(); diff --git a/tests/parsers/dataflow-java.test.js b/tests/parsers/dataflow-java.test.ts similarity index 99% rename from tests/parsers/dataflow-java.test.js rename to tests/parsers/dataflow-java.test.ts index 44481a4e..21d5947c 100644 --- a/tests/parsers/dataflow-java.test.js +++ b/tests/parsers/dataflow-java.test.ts @@ -6,7 +6,7 @@ import { createParsers } from '../../src/domain/parser.js'; import { extractDataflow } from '../../src/features/dataflow.js'; describe('extractDataflow — Java', () => { - let parsers; + let parsers: any; beforeAll(async () => { parsers = await createParsers(); diff --git a/tests/parsers/dataflow-javascript.test.js b/tests/parsers/dataflow-javascript.test.ts similarity index 99% rename from tests/parsers/dataflow-javascript.test.js rename to tests/parsers/dataflow-javascript.test.ts index bcbdf0c9..cb23eea8 100644 --- a/tests/parsers/dataflow-javascript.test.js +++ b/tests/parsers/dataflow-javascript.test.ts @@ -6,7 +6,7 @@ import { createParsers } from '../../src/domain/parser.js'; import { extractDataflow } from '../../src/features/dataflow.js'; describe('extractDataflow — JavaScript', () => { - let parsers; + let parsers: any; beforeAll(async () => { parsers = await createParsers(); @@ -329,7 +329,7 @@ describe('extractDataflow — JavaScript', () => { it('tracks x = foo() without const/let/var', () => { const data = parseAndExtract(` function update() { - let result; + let result: any; result = compute(); return result; } diff --git a/tests/parsers/dataflow-php.test.js b/tests/parsers/dataflow-php.test.ts similarity index 99% rename from tests/parsers/dataflow-php.test.js rename to tests/parsers/dataflow-php.test.ts index ef5ddcc9..b015e8e7 100644 --- a/tests/parsers/dataflow-php.test.js +++ b/tests/parsers/dataflow-php.test.ts @@ -6,7 +6,7 @@ import { createParsers } from '../../src/domain/parser.js'; import { extractDataflow } from '../../src/features/dataflow.js'; describe('extractDataflow — PHP', () => { - let parsers; + let parsers: any; beforeAll(async () => { parsers = await createParsers(); diff --git a/tests/parsers/dataflow-python.test.js b/tests/parsers/dataflow-python.test.ts similarity index 99% rename from tests/parsers/dataflow-python.test.js rename to tests/parsers/dataflow-python.test.ts index e97aeea9..be37839b 100644 --- a/tests/parsers/dataflow-python.test.js +++ b/tests/parsers/dataflow-python.test.ts @@ -6,7 +6,7 @@ import { createParsers } from '../../src/domain/parser.js'; import { extractDataflow } from '../../src/features/dataflow.js'; describe('extractDataflow — Python', () => { - let parsers; + let parsers: any; beforeAll(async () => { parsers = await createParsers(); diff --git a/tests/parsers/dataflow-ruby.test.js b/tests/parsers/dataflow-ruby.test.ts similarity index 99% rename from tests/parsers/dataflow-ruby.test.js rename to tests/parsers/dataflow-ruby.test.ts index 973c6bee..314f7739 100644 --- a/tests/parsers/dataflow-ruby.test.js +++ b/tests/parsers/dataflow-ruby.test.ts @@ -6,7 +6,7 @@ import { createParsers } from '../../src/domain/parser.js'; import { extractDataflow } from '../../src/features/dataflow.js'; describe('extractDataflow — Ruby', () => { - let parsers; + let parsers: any; beforeAll(async () => { parsers = await createParsers(); diff --git a/tests/parsers/dataflow-rust.test.js b/tests/parsers/dataflow-rust.test.ts similarity index 99% rename from tests/parsers/dataflow-rust.test.js rename to tests/parsers/dataflow-rust.test.ts index f7200487..2520a79c 100644 --- a/tests/parsers/dataflow-rust.test.js +++ b/tests/parsers/dataflow-rust.test.ts @@ -6,7 +6,7 @@ import { createParsers } from '../../src/domain/parser.js'; import { extractDataflow } from '../../src/features/dataflow.js'; describe('extractDataflow — Rust', () => { - let parsers; + let parsers: any; beforeAll(async () => { parsers = await createParsers(); diff --git a/tests/parsers/extended-kinds.test.js b/tests/parsers/extended-kinds.test.ts similarity index 99% rename from tests/parsers/extended-kinds.test.js rename to tests/parsers/extended-kinds.test.ts index ab1a8ccf..c10b1ea1 100644 --- a/tests/parsers/extended-kinds.test.js +++ b/tests/parsers/extended-kinds.test.ts @@ -20,7 +20,7 @@ import { // ── JavaScript ────────────────────────────────────────────────────────────── describe('JavaScript extended kinds', () => { - let parsers; + let parsers: any; beforeAll(async () => { parsers = await createParsers(); @@ -97,7 +97,7 @@ describe('JavaScript extended kinds', () => { // ── Python ────────────────────────────────────────────────────────────────── describe('Python extended kinds', () => { - let parsers; + let parsers: any; beforeAll(async () => { parsers = await createParsers(); @@ -155,7 +155,7 @@ describe('Python extended kinds', () => { // ── Go ────────────────────────────────────────────────────────────────────── describe('Go extended kinds', () => { - let parsers; + let parsers: any; beforeAll(async () => { parsers = await createParsers(); @@ -209,7 +209,7 @@ describe('Go extended kinds', () => { // ── Rust ───────────────────────────────────────────────────────────────────── describe('Rust extended kinds', () => { - let parsers; + let parsers: any; beforeAll(async () => { parsers = await createParsers(); @@ -276,7 +276,7 @@ describe('Rust extended kinds', () => { // ── Java ───────────────────────────────────────────────────────────────────── describe('Java extended kinds', () => { - let parsers; + let parsers: any; beforeAll(async () => { parsers = await createParsers(); @@ -335,7 +335,7 @@ describe('Java extended kinds', () => { // ── C# ────────────────────────────────────────────────────────────────────── describe('C# extended kinds', () => { - let parsers; + let parsers: any; beforeAll(async () => { parsers = await createParsers(); @@ -394,7 +394,7 @@ describe('C# extended kinds', () => { // ── Ruby ───────────────────────────────────────────────────────────────────── describe('Ruby extended kinds', () => { - let parsers; + let parsers: any; beforeAll(async () => { parsers = await createParsers(); @@ -447,7 +447,7 @@ describe('Ruby extended kinds', () => { // ── PHP ────────────────────────────────────────────────────────────────────── describe('PHP extended kinds', () => { - let parsers; + let parsers: any; beforeAll(async () => { parsers = await createParsers(); diff --git a/tests/parsers/go.test.js b/tests/parsers/go.test.ts similarity index 99% rename from tests/parsers/go.test.js rename to tests/parsers/go.test.ts index ddb1efdf..73e02b6d 100644 --- a/tests/parsers/go.test.js +++ b/tests/parsers/go.test.ts @@ -2,7 +2,7 @@ import { beforeAll, describe, expect, it } from 'vitest'; import { createParsers, extractGoSymbols } from '../../src/domain/parser.js'; describe('Go parser', () => { - let parsers; + let parsers: any; beforeAll(async () => { parsers = await createParsers(); diff --git a/tests/parsers/java.test.js b/tests/parsers/java.test.ts similarity index 99% rename from tests/parsers/java.test.js rename to tests/parsers/java.test.ts index 39bcf118..591bfa50 100644 --- a/tests/parsers/java.test.js +++ b/tests/parsers/java.test.ts @@ -2,7 +2,7 @@ import { beforeAll, describe, expect, it } from 'vitest'; import { createParsers, extractJavaSymbols } from '../../src/domain/parser.js'; describe('Java parser', () => { - let parsers; + let parsers: any; beforeAll(async () => { parsers = await createParsers(); diff --git a/tests/parsers/javascript.test.js b/tests/parsers/javascript.test.ts similarity index 99% rename from tests/parsers/javascript.test.js rename to tests/parsers/javascript.test.ts index 294ba39e..f68cffd8 100644 --- a/tests/parsers/javascript.test.js +++ b/tests/parsers/javascript.test.ts @@ -9,7 +9,7 @@ import { beforeAll, describe, expect, it } from 'vitest'; import { createParsers, extractSymbols } from '../../src/domain/parser.js'; describe('JavaScript parser', () => { - let parsers; + let parsers: any; beforeAll(async () => { parsers = await createParsers(); diff --git a/tests/parsers/php.test.js b/tests/parsers/php.test.ts similarity index 99% rename from tests/parsers/php.test.js rename to tests/parsers/php.test.ts index 106ba306..74a5436e 100644 --- a/tests/parsers/php.test.js +++ b/tests/parsers/php.test.ts @@ -2,7 +2,7 @@ import { beforeAll, describe, expect, it } from 'vitest'; import { createParsers, extractPHPSymbols } from '../../src/domain/parser.js'; describe('PHP parser', () => { - let parsers; + let parsers: any; beforeAll(async () => { parsers = await createParsers(); diff --git a/tests/parsers/ruby.test.js b/tests/parsers/ruby.test.ts similarity index 99% rename from tests/parsers/ruby.test.js rename to tests/parsers/ruby.test.ts index eff4a403..03f1d67c 100644 --- a/tests/parsers/ruby.test.js +++ b/tests/parsers/ruby.test.ts @@ -2,7 +2,7 @@ import { beforeAll, describe, expect, it } from 'vitest'; import { createParsers, extractRubySymbols } from '../../src/domain/parser.js'; describe('Ruby parser', () => { - let parsers; + let parsers: any; beforeAll(async () => { parsers = await createParsers(); diff --git a/tests/parsers/rust.test.js b/tests/parsers/rust.test.ts similarity index 99% rename from tests/parsers/rust.test.js rename to tests/parsers/rust.test.ts index e6ee7dae..0828fee4 100644 --- a/tests/parsers/rust.test.js +++ b/tests/parsers/rust.test.ts @@ -2,7 +2,7 @@ import { beforeAll, describe, expect, it } from 'vitest'; import { createParsers, extractRustSymbols } from '../../src/domain/parser.js'; describe('Rust parser', () => { - let parsers; + let parsers: any; beforeAll(async () => { parsers = await createParsers(); diff --git a/tests/parsers/unified.test.js b/tests/parsers/unified.test.ts similarity index 100% rename from tests/parsers/unified.test.js rename to tests/parsers/unified.test.ts diff --git a/tests/presentation/colors.test.js b/tests/presentation/colors.test.ts similarity index 100% rename from tests/presentation/colors.test.js rename to tests/presentation/colors.test.ts diff --git a/tests/presentation/queries-cli.test.js b/tests/presentation/queries-cli.test.ts similarity index 99% rename from tests/presentation/queries-cli.test.js rename to tests/presentation/queries-cli.test.ts index d5a41ae2..18fd76ea 100644 --- a/tests/presentation/queries-cli.test.js +++ b/tests/presentation/queries-cli.test.ts @@ -42,7 +42,7 @@ const { fileExports } = await import('../../src/presentation/queries-cli/exports const { moduleMap, roles } = await import('../../src/presentation/queries-cli/overview.js'); // ── Helpers ──────────────────────────────────────────────────────── -let lines; +let lines: any; beforeEach(() => { lines = []; diff --git a/tests/presentation/result-formatter.test.js b/tests/presentation/result-formatter.test.ts similarity index 99% rename from tests/presentation/result-formatter.test.js rename to tests/presentation/result-formatter.test.ts index b524a297..88e4f432 100644 --- a/tests/presentation/result-formatter.test.js +++ b/tests/presentation/result-formatter.test.ts @@ -17,7 +17,7 @@ vi.mock('../../src/shared/paginate.js', () => ({ const { outputResult } = await import('../../src/presentation/result-formatter.js'); describe('outputResult', () => { - let logSpy; + let logSpy: any; beforeEach(() => { logSpy = vi.spyOn(console, 'log').mockImplementation(() => {}); diff --git a/tests/presentation/table.test.js b/tests/presentation/table.test.ts similarity index 100% rename from tests/presentation/table.test.js rename to tests/presentation/table.test.ts diff --git a/tests/resolution/parity.test.js b/tests/resolution/parity.test.ts similarity index 100% rename from tests/resolution/parity.test.js rename to tests/resolution/parity.test.ts diff --git a/tests/search/embedder-search.test.js b/tests/search/embedder-search.test.ts similarity index 99% rename from tests/search/embedder-search.test.js rename to tests/search/embedder-search.test.ts index 6216c986..38582fd3 100644 --- a/tests/search/embedder-search.test.js +++ b/tests/search/embedder-search.test.ts @@ -97,8 +97,8 @@ function captureLog(fn) { // C: "function authMiddleware (auth Middleware) in src/middleware.js" // D: "function formatDate (format Date) in src/utils.js" -let tmpDir, dbPath; -let noFtsDir, noFtsDbPath; +let tmpDir: string, dbPath: string; +let noFtsDir: string, noFtsDbPath: string; beforeAll(() => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-test-')); diff --git a/tests/search/embedding-regression.test.js b/tests/search/embedding-regression.test.ts similarity index 99% rename from tests/search/embedding-regression.test.js rename to tests/search/embedding-regression.test.ts index 8e618185..a0391b7e 100644 --- a/tests/search/embedding-regression.test.js +++ b/tests/search/embedding-regression.test.ts @@ -51,7 +51,7 @@ export function main() { `.trimStart(), }; -let tmpDir, dbPath; +let tmpDir: string, dbPath: string; describe.skipIf(!hasTransformers)('embedding regression (real model)', () => { beforeAll(async () => { diff --git a/tests/search/embedding-strategy.test.js b/tests/search/embedding-strategy.test.ts similarity index 99% rename from tests/search/embedding-strategy.test.js rename to tests/search/embedding-strategy.test.ts index a8a50a2c..a8e58828 100644 --- a/tests/search/embedding-strategy.test.js +++ b/tests/search/embedding-strategy.test.ts @@ -70,7 +70,7 @@ const FIXTURE_FILES = { ].join('\n'), }; -let tmpDir, dbPath; +let tmpDir: string, dbPath: string; beforeAll(() => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-strategy-test-')); @@ -290,7 +290,7 @@ describe('FTS5 index built alongside embeddings', () => { }); describe('context window overflow detection', () => { - let bigDir, bigDbPath; + let bigDir: string, bigDbPath: string; beforeAll(() => { // Create a file with a very large function that will overflow minilm's 256-token window diff --git a/tests/unit/boundaries.test.js b/tests/unit/boundaries.test.ts similarity index 99% rename from tests/unit/boundaries.test.js rename to tests/unit/boundaries.test.ts index 0f2642e7..d3f9b440 100644 --- a/tests/unit/boundaries.test.js +++ b/tests/unit/boundaries.test.ts @@ -202,7 +202,7 @@ describe('PRESETS', () => { // ─── evaluateBoundaries ────────────────────────────────────────────── describe('evaluateBoundaries', () => { - let tmpDir, dbPath, db; + let tmpDir: string, dbPath: string, db: any; function insertNode(database, name, kind, file, line) { return database diff --git a/tests/unit/builder.test.js b/tests/unit/builder.test.ts similarity index 99% rename from tests/unit/builder.test.js rename to tests/unit/builder.test.ts index 7dc7d958..2d15c664 100644 --- a/tests/unit/builder.test.js +++ b/tests/unit/builder.test.ts @@ -11,7 +11,7 @@ import path from 'node:path'; import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest'; import { collectFiles, loadPathAliases, readFileSafe } from '../../src/domain/graph/builder.js'; -let tmpDir; +let tmpDir: string; beforeAll(() => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-builder-')); diff --git a/tests/unit/cfg.test.js b/tests/unit/cfg.test.ts similarity index 99% rename from tests/unit/cfg.test.js rename to tests/unit/cfg.test.ts index 50dc06dd..743ee7bd 100644 --- a/tests/unit/cfg.test.js +++ b/tests/unit/cfg.test.ts @@ -13,7 +13,7 @@ import { createParsers } from '../../src/domain/parser.js'; import { buildFunctionCFG, makeCfgRules } from '../../src/features/cfg.js'; import { COMPLEXITY_RULES } from '../../src/features/complexity.js'; -let jsParser; +let jsParser: any; beforeAll(async () => { const parsers = await createParsers(); @@ -493,7 +493,7 @@ function makeLangHelpers(langId, parsers) { // ── Python ─────────────────────────────────────────────────────────────── describe('buildFunctionCFG — Python', () => { - let helpers; + let helpers: any; beforeAll(async () => { const parsers = await createParsers(); @@ -589,7 +589,7 @@ def guard(x): // ── Go ─────────────────────────────────────────────────────────────────── describe('buildFunctionCFG — Go', () => { - let helpers; + let helpers: any; beforeAll(async () => { const parsers = await createParsers(); @@ -684,7 +684,7 @@ func sw(x int) { // ── Rust ───────────────────────────────────────────────────────────────── describe('buildFunctionCFG — Rust', () => { - let helpers; + let helpers: any; beforeAll(async () => { const parsers = await createParsers(); @@ -790,7 +790,7 @@ fn match_fn(x: i32) { // ── Java ───────────────────────────────────────────────────────────────── describe('buildFunctionCFG — Java', () => { - let helpers; + let helpers: any; beforeAll(async () => { const parsers = await createParsers(); @@ -913,7 +913,7 @@ class A { // ── C# ─────────────────────────────────────────────────────────────────── describe('buildFunctionCFG — C#', () => { - let helpers; + let helpers: any; beforeAll(async () => { const parsers = await createParsers(); @@ -1000,7 +1000,7 @@ class A { // ── Ruby ───────────────────────────────────────────────────────────────── describe('buildFunctionCFG — Ruby', () => { - let helpers; + let helpers: any; beforeAll(async () => { const parsers = await createParsers(); @@ -1102,7 +1102,7 @@ end // ── PHP ────────────────────────────────────────────────────────────────── describe('buildFunctionCFG — PHP', () => { - let helpers; + let helpers: any; beforeAll(async () => { const parsers = await createParsers(); diff --git a/tests/unit/change-journal.test.js b/tests/unit/change-journal.test.ts similarity index 99% rename from tests/unit/change-journal.test.js rename to tests/unit/change-journal.test.ts index 3595c674..1812e3f2 100644 --- a/tests/unit/change-journal.test.js +++ b/tests/unit/change-journal.test.ts @@ -16,7 +16,7 @@ import { rotateIfNeeded, } from '../../src/domain/graph/change-journal.js'; -let tmpDir; +let tmpDir: string; beforeAll(() => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-change-journal-')); diff --git a/tests/unit/complexity.test.js b/tests/unit/complexity.test.ts similarity index 99% rename from tests/unit/complexity.test.js rename to tests/unit/complexity.test.ts index 776df84c..83bd87fe 100644 --- a/tests/unit/complexity.test.js +++ b/tests/unit/complexity.test.ts @@ -16,7 +16,7 @@ import { HALSTEAD_RULES, } from '../../src/features/complexity.js'; -let jsParser; +let jsParser: any; beforeAll(async () => { const parsers = await createParsers(); @@ -458,7 +458,7 @@ describe('computeMaintainabilityIndex', () => { function makeHelpers(langId, parsersPromise) { const rules = COMPLEXITY_RULES.get(langId); - let parser; + let parser: any; let available = false; beforeAll(async () => { const parsers = await parsersPromise; @@ -499,7 +499,7 @@ function makeHelpers(langId, parsersPromise) { } // Shared parsers promise to avoid re-initializing per suite -let _parsersPromise; +let _parsersPromise: any; function sharedParsers() { if (!_parsersPromise) _parsersPromise = createParsers(); return _parsersPromise; diff --git a/tests/unit/config.test.js b/tests/unit/config.test.ts similarity index 99% rename from tests/unit/config.test.js rename to tests/unit/config.test.ts index 401046da..bba8c190 100644 --- a/tests/unit/config.test.js +++ b/tests/unit/config.test.ts @@ -24,7 +24,7 @@ vi.mock('node:child_process', async (importOriginal) => { }; }); -let tmpDir; +let tmpDir: string; beforeAll(() => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-config-')); @@ -374,7 +374,7 @@ describe('applyEnvOverrides', () => { }); describe('resolveSecrets', () => { - let mockExecFile; + let mockExecFile: any; beforeAll(async () => { const cp = await import('node:child_process'); @@ -486,7 +486,7 @@ describe('resolveSecrets', () => { describe('apiKeyCommand integration', () => { const ENV_KEYS = ['CODEGRAPH_LLM_PROVIDER', 'CODEGRAPH_LLM_API_KEY', 'CODEGRAPH_LLM_MODEL']; - let mockExecFile; + let mockExecFile: any; beforeAll(async () => { const cp = await import('node:child_process'); diff --git a/tests/unit/constants.test.js b/tests/unit/constants.test.ts similarity index 100% rename from tests/unit/constants.test.js rename to tests/unit/constants.test.ts diff --git a/tests/unit/db.test.js b/tests/unit/db.test.ts similarity index 99% rename from tests/unit/db.test.js rename to tests/unit/db.test.ts index fd0a595c..055afac9 100644 --- a/tests/unit/db.test.js +++ b/tests/unit/db.test.ts @@ -32,7 +32,7 @@ import { setBuildMeta, } from '../../src/db/index.js'; -let tmpDir; +let tmpDir: string; beforeAll(() => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-db-')); @@ -261,9 +261,9 @@ describe('findRepoRoot', () => { }); describe('findDbPath with git ceiling', () => { - let outerDir; - let worktreeRoot; - let innerDir; + let outerDir: string; + let worktreeRoot: string; + let innerDir: string; beforeAll(() => { // Simulate a worktree-inside-repo layout: diff --git a/tests/unit/errors.test.js b/tests/unit/errors.test.ts similarity index 100% rename from tests/unit/errors.test.js rename to tests/unit/errors.test.ts diff --git a/tests/unit/in-memory-repository.test.js b/tests/unit/in-memory-repository.test.ts similarity index 100% rename from tests/unit/in-memory-repository.test.js rename to tests/unit/in-memory-repository.test.ts diff --git a/tests/unit/index-exports.test.js b/tests/unit/index-exports.test.ts similarity index 100% rename from tests/unit/index-exports.test.js rename to tests/unit/index-exports.test.ts diff --git a/tests/unit/journal.test.js b/tests/unit/journal.test.ts similarity index 99% rename from tests/unit/journal.test.js rename to tests/unit/journal.test.ts index 27769f2a..8428e752 100644 --- a/tests/unit/journal.test.js +++ b/tests/unit/journal.test.ts @@ -13,7 +13,7 @@ import { writeJournalHeader, } from '../../src/domain/graph/journal.js'; -let tmpDir; +let tmpDir: string; beforeAll(() => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-journal-')); diff --git a/tests/unit/logger.test.js b/tests/unit/logger.test.ts similarity index 99% rename from tests/unit/logger.test.js rename to tests/unit/logger.test.ts index fb54863d..22fbc1ab 100644 --- a/tests/unit/logger.test.js +++ b/tests/unit/logger.test.ts @@ -13,7 +13,7 @@ import { } from '../../src/infrastructure/logger.js'; describe('logger', () => { - let stderrSpy; + let stderrSpy: any; afterEach(() => { setVerbose(false); diff --git a/tests/unit/mcp.test.js b/tests/unit/mcp.test.ts similarity index 100% rename from tests/unit/mcp.test.js rename to tests/unit/mcp.test.ts diff --git a/tests/unit/normalize-symbol.test.js b/tests/unit/normalize-symbol.test.ts similarity index 100% rename from tests/unit/normalize-symbol.test.js rename to tests/unit/normalize-symbol.test.ts diff --git a/tests/unit/owners.test.js b/tests/unit/owners.test.ts similarity index 99% rename from tests/unit/owners.test.js rename to tests/unit/owners.test.ts index 52ed123c..f49a3939 100644 --- a/tests/unit/owners.test.js +++ b/tests/unit/owners.test.ts @@ -160,7 +160,7 @@ describe('matchOwners', () => { // ─── parseCodeowners ───────────────────────────────────────────────── describe('parseCodeowners', () => { - let tmpDir; + let tmpDir: string; beforeEach(() => { clearCodeownersCache(); diff --git a/tests/unit/parser.test.js b/tests/unit/parser.test.ts similarity index 100% rename from tests/unit/parser.test.js rename to tests/unit/parser.test.ts diff --git a/tests/unit/prompt-install.test.js b/tests/unit/prompt-install.test.ts similarity index 98% rename from tests/unit/prompt-install.test.js rename to tests/unit/prompt-install.test.ts index d7e09981..280620b1 100644 --- a/tests/unit/prompt-install.test.js +++ b/tests/unit/prompt-install.test.ts @@ -11,10 +11,10 @@ import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'; describe('loadTransformers install prompt', () => { - let exitSpy; - let errorSpy; - let logSpy; - let origTTY; + let exitSpy: any; + let errorSpy: any; + let logSpy: any; + let origTTY: any; beforeEach(() => { vi.resetModules(); diff --git a/tests/unit/purge-files.test.js b/tests/unit/purge-files.test.ts similarity index 100% rename from tests/unit/purge-files.test.js rename to tests/unit/purge-files.test.ts diff --git a/tests/unit/queries-unit.test.js b/tests/unit/queries-unit.test.ts similarity index 99% rename from tests/unit/queries-unit.test.js rename to tests/unit/queries-unit.test.ts index 19a6b2aa..33ceafc4 100644 --- a/tests/unit/queries-unit.test.js +++ b/tests/unit/queries-unit.test.ts @@ -60,7 +60,7 @@ function insertEdge(db, sourceId, targetId, kind) { // testHelper -> handleRequest // ChildService.process -> BaseService.process (intra-hierarchy) -let tmpDir, dbPath; +let tmpDir: string, dbPath: string; beforeAll(() => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-queries-unit-')); diff --git a/tests/unit/query-builder.test.js b/tests/unit/query-builder.test.ts similarity index 99% rename from tests/unit/query-builder.test.js rename to tests/unit/query-builder.test.ts index a2a15e51..fa755a46 100644 --- a/tests/unit/query-builder.test.js +++ b/tests/unit/query-builder.test.ts @@ -158,7 +158,7 @@ describe('collectFile', () => { // ─── NodeQuery ─────────────────────────────────────────────────────── describe('NodeQuery', () => { - let db; + let db: any; beforeEach(() => { db = new Database(':memory:'); diff --git a/tests/unit/registry.test.js b/tests/unit/registry.test.ts similarity index 99% rename from tests/unit/registry.test.js rename to tests/unit/registry.test.ts index c4456c1d..7b16c108 100644 --- a/tests/unit/registry.test.js +++ b/tests/unit/registry.test.ts @@ -19,8 +19,8 @@ import { const [_major, _minor] = process.versions.node.split('.').map(Number); const canStripTypes = _major > 22 || (_major === 22 && _minor >= 6); -let tmpDir; -let registryPath; +let tmpDir: string; +let registryPath: string; beforeEach(() => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-registry-')); diff --git a/tests/unit/repository-parity.test.js b/tests/unit/repository-parity.test.ts similarity index 99% rename from tests/unit/repository-parity.test.js rename to tests/unit/repository-parity.test.ts index 86d11610..a3bac79d 100644 --- a/tests/unit/repository-parity.test.js +++ b/tests/unit/repository-parity.test.ts @@ -149,9 +149,9 @@ describe.each([ { label: 'SqliteRepository', seed: seedSqliteRepo }, { label: 'InMemoryRepository', seed: seedInMemoryRepo }, ])('Repository parity: $label', ({ seed }) => { - let repo; - let ids; - let dbToClose; + let repo: any; + let ids: any; + let dbToClose: any; beforeEach(() => { const result = seed(); diff --git a/tests/unit/repository.test.js b/tests/unit/repository.test.ts similarity index 99% rename from tests/unit/repository.test.js rename to tests/unit/repository.test.ts index 37d3defa..5966c2d8 100644 --- a/tests/unit/repository.test.js +++ b/tests/unit/repository.test.ts @@ -13,7 +13,7 @@ import { } from '../../src/db/repository/nodes.js'; describe('repository', () => { - let db; + let db: any; beforeEach(() => { db = new Database(':memory:'); diff --git a/tests/unit/resolve.test.js b/tests/unit/resolve.test.ts similarity index 99% rename from tests/unit/resolve.test.js rename to tests/unit/resolve.test.ts index 8f6227f8..eaa8b5aa 100644 --- a/tests/unit/resolve.test.js +++ b/tests/unit/resolve.test.ts @@ -25,7 +25,7 @@ import { // ─── Temp project setup ────────────────────────────────────────────── -let tmpDir; +let tmpDir: string; beforeAll(() => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-resolve-')); @@ -257,7 +257,7 @@ describe('parseBareSpecifier', () => { // ─── resolveViaExports ─────────────────────────────────────────────── describe('resolveViaExports', () => { - let pkgRoot; + let pkgRoot: string; beforeAll(() => { clearExportsCache(); @@ -392,7 +392,7 @@ describe('resolveViaExports', () => { // ─── resolveImportPathJS with exports ──────────────────────────────── describe('resolveImportPathJS with package.json exports', () => { - let pkgRoot; + let pkgRoot: string; beforeAll(() => { clearExportsCache(); @@ -428,7 +428,7 @@ describe('resolveImportPathJS with package.json exports', () => { // ─── resolveViaWorkspace ───────────────────────────────────────────── describe('resolveViaWorkspace', () => { - let wsRoot; + let wsRoot: string; beforeAll(() => { wsRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-ws-')); @@ -521,7 +521,7 @@ describe('resolveViaWorkspace', () => { // ─── resolveImportPathJS with workspaces ───────────────────────────── describe('resolveImportPathJS with workspace resolution', () => { - let wsRoot; + let wsRoot: string; beforeAll(() => { wsRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-ws-resolve-')); @@ -588,7 +588,7 @@ describe('resolveImportPathJS with workspace resolution', () => { // ─── computeConfidenceJS with workspace boost ──────────────────────── describe('computeConfidenceJS workspace confidence', () => { - let wsRoot; + let wsRoot: string; beforeAll(() => { wsRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-ws-conf-')); diff --git a/tests/unit/roles.test.js b/tests/unit/roles.test.ts similarity index 99% rename from tests/unit/roles.test.js rename to tests/unit/roles.test.ts index cf4fd5e6..d729f8e0 100644 --- a/tests/unit/roles.test.js +++ b/tests/unit/roles.test.ts @@ -18,7 +18,7 @@ import { beforeEach, describe, expect, it } from 'vitest'; import { initSchema } from '../../src/db/index.js'; import { classifyNodeRoles } from '../../src/features/structure.js'; -let db; +let db: any; function setup() { db = new Database(':memory:'); diff --git a/tests/unit/snapshot.test.js b/tests/unit/snapshot.test.ts similarity index 99% rename from tests/unit/snapshot.test.js rename to tests/unit/snapshot.test.ts index b12b912a..ec149252 100644 --- a/tests/unit/snapshot.test.js +++ b/tests/unit/snapshot.test.ts @@ -12,8 +12,8 @@ import { validateSnapshotName, } from '../../src/features/snapshot.js'; -let tmpDir; -let dbPath; +let tmpDir: string; +let dbPath: string; function createTestDb(filePath) { fs.mkdirSync(path.dirname(filePath), { recursive: true }); diff --git a/tests/unit/structure.test.js b/tests/unit/structure.test.ts similarity index 99% rename from tests/unit/structure.test.js rename to tests/unit/structure.test.ts index fd57e487..77aa3088 100644 --- a/tests/unit/structure.test.js +++ b/tests/unit/structure.test.ts @@ -10,7 +10,7 @@ import { beforeEach, describe, expect, it } from 'vitest'; import { initSchema } from '../../src/db/index.js'; import { buildStructure } from '../../src/features/structure.js'; -let db; +let db: any; function setup() { db = new Database(':memory:'); diff --git a/tests/unit/update-check.test.js b/tests/unit/update-check.test.ts similarity index 99% rename from tests/unit/update-check.test.js rename to tests/unit/update-check.test.ts index 78bc204f..46621d8b 100644 --- a/tests/unit/update-check.test.js +++ b/tests/unit/update-check.test.ts @@ -8,8 +8,8 @@ import { semverCompare, } from '../../src/infrastructure/update-check.js'; -let tmpDir; -let cachePath; +let tmpDir: string; +let cachePath: string; beforeEach(() => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-update-')); diff --git a/tests/unit/visitor.test.js b/tests/unit/visitor.test.ts similarity index 99% rename from tests/unit/visitor.test.js rename to tests/unit/visitor.test.ts index e15571ec..62dc5b3e 100644 --- a/tests/unit/visitor.test.js +++ b/tests/unit/visitor.test.ts @@ -4,7 +4,7 @@ import { describe, expect, it } from 'vitest'; // We need a tree-sitter tree to test. Use the JS parser. -let parse; +let parse: any; async function ensureParser() { if (parse) return; diff --git a/tsconfig.json b/tsconfig.json index 613956a9..99220394 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -44,10 +44,6 @@ "noPropertyAccessFromIndexSignature": true, "exactOptionalPropertyTypes": false, - /* Migration support */ - "allowJs": true, - "checkJs": false, - /* Misc */ "skipLibCheck": true, "forceConsistentCasingInFileNames": true, diff --git a/vitest.config.js b/vitest.config.js deleted file mode 100644 index 04d915bf..00000000 --- a/vitest.config.js +++ /dev/null @@ -1,60 +0,0 @@ -import { existsSync } from 'node:fs'; -import { dirname, resolve } from 'node:path'; -import { fileURLToPath, pathToFileURL } from 'node:url'; -import { defineConfig } from 'vitest/config'; - -const __dirname = dirname(fileURLToPath(import.meta.url)); -const loaderPath = pathToFileURL(resolve(__dirname, 'scripts/ts-resolve-loader.js')).href; -const [major, minor] = process.versions.node.split('.').map(Number); -const supportsStripTypes = major > 22 || (major === 22 && minor >= 6); -const supportsHooks = major > 20 || (major === 20 && minor >= 6); -const existing = process.env.NODE_OPTIONS || ''; - -/** - * During the JS → TS migration, some .js files import from modules that have - * already been renamed to .ts. Vite only auto-resolves .js→.ts when the - * *importer* is itself a .ts file. This plugin fills the gap: when a .js - * import target doesn't exist on disk, it tries the .ts counterpart. - */ -function jsToTsResolver() { - return { - name: 'js-to-ts-resolver', - enforce: 'pre', - resolveId(source, importer) { - if (!importer || !source.endsWith('.js')) return null; - // Only handle relative/absolute paths, not bare specifiers - if (!source.startsWith('.') && !source.startsWith('/')) return null; - const importerPath = importer.startsWith('file://') - ? fileURLToPath(importer) - : importer; - const fsPath = resolve(dirname(importerPath), source); - if (!existsSync(fsPath)) { - const tsPath = fsPath.replace(/\.js$/, '.ts'); - if (existsSync(tsPath)) return tsPath; - } - return null; - }, - }; -} - -export default defineConfig({ - plugins: [jsToTsResolver()], - test: { - globals: true, - testTimeout: 30000, - exclude: ['**/node_modules/**', '**/.git/**', '.claude/**'], - // Register the .js→.ts resolve loader for Node's native ESM resolver. - // This covers require() calls and child processes spawned by tests. - env: { - NODE_OPTIONS: [ - existing, - supportsStripTypes && - !existing.includes('--experimental-strip-types') && - !existing.includes('--strip-types') - ? (major >= 23 ? '--strip-types' : '--experimental-strip-types') - : '', - existing.includes(loaderPath) ? '' : (supportsHooks ? `--import ${loaderPath}` : ''), - ].filter(Boolean).join(' '), - }, - }, -}); diff --git a/vitest.config.ts b/vitest.config.ts new file mode 100644 index 00000000..73e6ca22 --- /dev/null +++ b/vitest.config.ts @@ -0,0 +1,31 @@ +import { dirname, resolve } from 'node:path'; +import { fileURLToPath, pathToFileURL } from 'node:url'; +import { defineConfig } from 'vitest/config'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const loaderPath = pathToFileURL(resolve(__dirname, 'scripts/ts-resolve-loader.js')).href; +const [major, minor] = process.versions.node.split('.').map(Number); +const supportsStripTypes = major > 22 || (major === 22 && minor >= 6); +const supportsHooks = major > 20 || (major === 20 && minor >= 6); +const existing = process.env.NODE_OPTIONS || ''; + +export default defineConfig({ + test: { + globals: true, + testTimeout: 30000, + exclude: ['**/node_modules/**', '**/.git/**', '.claude/**'], + // Register the .ts resolve loader for Node's native ESM resolver. + // This covers child processes spawned by tests (e.g. CLI integration tests). + env: { + NODE_OPTIONS: [ + existing, + supportsStripTypes && + !existing.includes('--experimental-strip-types') && + !existing.includes('--strip-types') + ? (major >= 23 ? '--strip-types' : '--experimental-strip-types') + : '', + existing.includes(loaderPath) ? '' : (supportsHooks ? `--import ${loaderPath}` : ''), + ].filter(Boolean).join(' '), + }, + }, +}); From 7d778117cba2cd8ee78fa4723c34f35345636e77 Mon Sep 17 00:00:00 2001 From: carlos-alm <127798846+carlos-alm@users.noreply.github.com> Date: Tue, 24 Mar 2026 15:50:59 -0600 Subject: [PATCH 02/15] feat(types): migrate scripts and docs examples to TypeScript Rename 16 scripts and 1 docs example from .js to .ts. Update all invocations in package.json, CI workflows, and shell scripts to use node --experimental-strip-types. Remaining .js files (must stay JS): - scripts/test.js, scripts/build-wasm.js (Node 20 bootstrap) - 4 ESM loader hooks (bootstrap .ts support) - scripts/gen-deps.cjs, src/index.cjs (CJS by design) --- .github/workflows/benchmark.yml | 16 ++++++++-------- .github/workflows/ci.yml | 2 +- .github/workflows/publish.yml | 4 ++-- ...pre-commit-checks.js => pre-commit-checks.ts} | 0 docs/examples/claude-code-hooks/pre-commit.sh | 2 +- package.json | 4 ++-- scripts/{bench-version.js => bench-version.ts} | 0 scripts/{benchmark.js => benchmark.ts} | 0 ...dding-benchmark.js => embedding-benchmark.ts} | 0 ...tal-benchmark.js => incremental-benchmark.ts} | 0 scripts/lib/{bench-config.js => bench-config.ts} | 0 scripts/lib/{fork-engine.js => fork-engine.ts} | 0 .../{query-benchmark.js => query-benchmark.ts} | 0 ...ative-versions.js => sync-native-versions.ts} | 0 ...hmark-issues.js => token-benchmark-issues.ts} | 0 .../{token-benchmark.js => token-benchmark.ts} | 0 ...mark-report.js => update-benchmark-report.ts} | 0 ...ding-report.js => update-embedding-report.ts} | 0 ...al-report.js => update-incremental-report.ts} | 0 ...te-query-report.js => update-query-report.ts} | 0 ...te-token-report.js => update-token-report.ts} | 0 scripts/{verify-imports.js => verify-imports.ts} | 0 22 files changed, 14 insertions(+), 14 deletions(-) rename docs/examples/claude-code-hooks/{pre-commit-checks.js => pre-commit-checks.ts} (100%) rename scripts/{bench-version.js => bench-version.ts} (100%) rename scripts/{benchmark.js => benchmark.ts} (100%) rename scripts/{embedding-benchmark.js => embedding-benchmark.ts} (100%) rename scripts/{incremental-benchmark.js => incremental-benchmark.ts} (100%) rename scripts/lib/{bench-config.js => bench-config.ts} (100%) rename scripts/lib/{fork-engine.js => fork-engine.ts} (100%) rename scripts/{query-benchmark.js => query-benchmark.ts} (100%) rename scripts/{sync-native-versions.js => sync-native-versions.ts} (100%) rename scripts/{token-benchmark-issues.js => token-benchmark-issues.ts} (100%) rename scripts/{token-benchmark.js => token-benchmark.ts} (100%) rename scripts/{update-benchmark-report.js => update-benchmark-report.ts} (100%) rename scripts/{update-embedding-report.js => update-embedding-report.ts} (100%) rename scripts/{update-incremental-report.js => update-incremental-report.ts} (100%) rename scripts/{update-query-report.js => update-query-report.ts} (100%) rename scripts/{update-token-report.js => update-token-report.ts} (100%) rename scripts/{verify-imports.js => verify-imports.ts} (100%) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 670b8b9e..064ceedd 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -93,11 +93,11 @@ jobs: if [ "${{ steps.mode.outputs.source }}" = "npm" ]; then ARGS="$ARGS --npm" fi - node scripts/benchmark.js $ARGS 2>/dev/null > benchmark-result.json + node --experimental-strip-types scripts/benchmark.ts $ARGS 2>/dev/null > benchmark-result.json - name: Update build report if: steps.existing.outputs.skip != 'true' - run: node scripts/update-benchmark-report.js benchmark-result.json + run: node --experimental-strip-types scripts/update-benchmark-report.ts benchmark-result.json - name: Upload build result if: steps.existing.outputs.skip != 'true' @@ -244,11 +244,11 @@ jobs: if [ "${{ steps.mode.outputs.source }}" = "npm" ]; then ARGS="$ARGS --npm" fi - node scripts/embedding-benchmark.js $ARGS 2>/dev/null > embedding-benchmark-result.json + node --experimental-strip-types scripts/embedding-benchmark.ts $ARGS 2>/dev/null > embedding-benchmark-result.json - name: Update embedding report if: steps.existing.outputs.skip != 'true' - run: node scripts/update-embedding-report.js embedding-benchmark-result.json + run: node --experimental-strip-types scripts/update-embedding-report.ts embedding-benchmark-result.json - name: Upload embedding result if: steps.existing.outputs.skip != 'true' @@ -381,11 +381,11 @@ jobs: if [ "${{ steps.mode.outputs.source }}" = "npm" ]; then ARGS="$ARGS --npm" fi - node scripts/query-benchmark.js $ARGS 2>/dev/null > query-benchmark-result.json + node --experimental-strip-types scripts/query-benchmark.ts $ARGS 2>/dev/null > query-benchmark-result.json - name: Update query report if: steps.existing.outputs.skip != 'true' - run: node scripts/update-query-report.js query-benchmark-result.json + run: node --experimental-strip-types scripts/update-query-report.ts query-benchmark-result.json - name: Upload query result if: steps.existing.outputs.skip != 'true' @@ -518,11 +518,11 @@ jobs: if [ "${{ steps.mode.outputs.source }}" = "npm" ]; then ARGS="$ARGS --npm" fi - node scripts/incremental-benchmark.js $ARGS 2>/dev/null > incremental-benchmark-result.json + node --experimental-strip-types scripts/incremental-benchmark.ts $ARGS 2>/dev/null > incremental-benchmark-result.json - name: Update incremental report if: steps.existing.outputs.skip != 'true' - run: node scripts/update-incremental-report.js incremental-benchmark-result.json + run: node --experimental-strip-types scripts/update-incremental-report.ts incremental-benchmark-result.json - name: Upload incremental result if: steps.existing.outputs.skip != 'true' diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a80097ce..9e8bb2d9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -113,7 +113,7 @@ jobs: node-version: 22 - name: Verify all dynamic imports resolve - run: node scripts/verify-imports.js + run: node --experimental-strip-types scripts/verify-imports.ts rust-check: runs-on: ubuntu-latest diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index df34445c..060b964d 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -224,7 +224,7 @@ jobs: VERSION: ${{ needs.compute-version.outputs.version }} run: | npm version "$VERSION" --no-git-tag-version --allow-same-version - node scripts/sync-native-versions.js --strip + node --experimental-strip-types scripts/sync-native-versions.ts --strip echo "Packaging version $VERSION" - name: Build TypeScript @@ -405,7 +405,7 @@ jobs: run: | git checkout -- package-lock.json npm version "$VERSION" --no-git-tag-version --allow-same-version - node scripts/sync-native-versions.js + node --experimental-strip-types scripts/sync-native-versions.ts echo "Publishing version $VERSION" - name: Build TypeScript diff --git a/docs/examples/claude-code-hooks/pre-commit-checks.js b/docs/examples/claude-code-hooks/pre-commit-checks.ts similarity index 100% rename from docs/examples/claude-code-hooks/pre-commit-checks.js rename to docs/examples/claude-code-hooks/pre-commit-checks.ts diff --git a/docs/examples/claude-code-hooks/pre-commit.sh b/docs/examples/claude-code-hooks/pre-commit.sh index dfa1f3d2..49187b5c 100644 --- a/docs/examples/claude-code-hooks/pre-commit.sh +++ b/docs/examples/claude-code-hooks/pre-commit.sh @@ -48,7 +48,7 @@ fi # Run all checks in a single Node.js process HOOK_DIR="$(cd "$(dirname "$0")" && pwd)" -RESULT=$(node "$HOOK_DIR/pre-commit-checks.js" "$WORK_ROOT" "$EDITED_FILES" "$STAGED" 2>/dev/null) || true +RESULT=$(node --experimental-strip-types "$HOOK_DIR/pre-commit-checks.ts" "$WORK_ROOT" "$EDITED_FILES" "$STAGED" 2>/dev/null) || true if [ -z "$RESULT" ]; then exit 0 diff --git a/package.json b/package.json index 01115f82..4436814b 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,7 @@ "build": "tsc", "build:wasm": "node scripts/build-wasm.js", "typecheck": "tsc --noEmit", - "verify-imports": "node scripts/verify-imports.js", + "verify-imports": "node --experimental-strip-types scripts/verify-imports.ts", "test": "node scripts/test.js run", "test:watch": "node scripts/test.js", "test:coverage": "node scripts/test.js run --coverage", @@ -45,7 +45,7 @@ "deps:tree": "node scripts/gen-deps.cjs", "release": "commit-and-tag-version", "release:dry-run": "commit-and-tag-version --dry-run", - "version": "node scripts/sync-native-versions.js && git add package.json crates/codegraph-core/Cargo.toml" + "version": "node --experimental-strip-types scripts/sync-native-versions.ts && git add package.json crates/codegraph-core/Cargo.toml" }, "keywords": [ "codegraph", diff --git a/scripts/bench-version.js b/scripts/bench-version.ts similarity index 100% rename from scripts/bench-version.js rename to scripts/bench-version.ts diff --git a/scripts/benchmark.js b/scripts/benchmark.ts similarity index 100% rename from scripts/benchmark.js rename to scripts/benchmark.ts diff --git a/scripts/embedding-benchmark.js b/scripts/embedding-benchmark.ts similarity index 100% rename from scripts/embedding-benchmark.js rename to scripts/embedding-benchmark.ts diff --git a/scripts/incremental-benchmark.js b/scripts/incremental-benchmark.ts similarity index 100% rename from scripts/incremental-benchmark.js rename to scripts/incremental-benchmark.ts diff --git a/scripts/lib/bench-config.js b/scripts/lib/bench-config.ts similarity index 100% rename from scripts/lib/bench-config.js rename to scripts/lib/bench-config.ts diff --git a/scripts/lib/fork-engine.js b/scripts/lib/fork-engine.ts similarity index 100% rename from scripts/lib/fork-engine.js rename to scripts/lib/fork-engine.ts diff --git a/scripts/query-benchmark.js b/scripts/query-benchmark.ts similarity index 100% rename from scripts/query-benchmark.js rename to scripts/query-benchmark.ts diff --git a/scripts/sync-native-versions.js b/scripts/sync-native-versions.ts similarity index 100% rename from scripts/sync-native-versions.js rename to scripts/sync-native-versions.ts diff --git a/scripts/token-benchmark-issues.js b/scripts/token-benchmark-issues.ts similarity index 100% rename from scripts/token-benchmark-issues.js rename to scripts/token-benchmark-issues.ts diff --git a/scripts/token-benchmark.js b/scripts/token-benchmark.ts similarity index 100% rename from scripts/token-benchmark.js rename to scripts/token-benchmark.ts diff --git a/scripts/update-benchmark-report.js b/scripts/update-benchmark-report.ts similarity index 100% rename from scripts/update-benchmark-report.js rename to scripts/update-benchmark-report.ts diff --git a/scripts/update-embedding-report.js b/scripts/update-embedding-report.ts similarity index 100% rename from scripts/update-embedding-report.js rename to scripts/update-embedding-report.ts diff --git a/scripts/update-incremental-report.js b/scripts/update-incremental-report.ts similarity index 100% rename from scripts/update-incremental-report.js rename to scripts/update-incremental-report.ts diff --git a/scripts/update-query-report.js b/scripts/update-query-report.ts similarity index 100% rename from scripts/update-query-report.js rename to scripts/update-query-report.ts diff --git a/scripts/update-token-report.js b/scripts/update-token-report.ts similarity index 100% rename from scripts/update-token-report.js rename to scripts/update-token-report.ts diff --git a/scripts/verify-imports.js b/scripts/verify-imports.ts similarity index 100% rename from scripts/verify-imports.js rename to scripts/verify-imports.ts From 476e6422956083eb1aff92609a07dda222ca8afe Mon Sep 17 00:00:00 2001 From: carlos-alm <127798846+carlos-alm@users.noreply.github.com> Date: Tue, 24 Mar 2026 16:13:40 -0600 Subject: [PATCH 03/15] feat(types): drop Node 20, require Node >= 22 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Bump engines.node from >=20 to >=22 - Remove Node 20 from CI test matrix - Bump native build Node from 20 to 22 - Replace old JS ESM loader hooks with 2 TypeScript hooks (ts-resolve-loader.ts + ts-resolve-hooks.ts) that resolve .js→.ts import specifiers at runtime - Remove canStripTypes helper and version guards from tests - Update src/index.cjs to import('./index.ts') for dev; build step produces dist/index.cjs with ./index.js for published package - Simplify vitest.config.ts to use --experimental-strip-types only - Update child-process test invocations to use --import loader flag - Rewrite scripts/gen-deps.cjs as ESM TypeScript - Rename scripts/build-wasm.js → .ts Impact: 1 functions changed, 1 affected --- .github/workflows/build-native.yml | 2 +- .github/workflows/ci.yml | 2 +- .github/workflows/publish.yml | 2 +- package.json | 14 ++++++------ scripts/{build-wasm.js => build-wasm.ts} | 0 scripts/{gen-deps.cjs => gen-deps.ts} | 17 +++++++------- scripts/ts-resolve-hooks.ts | 28 ++++++++++++++++++++++++ scripts/ts-resolve-loader.ts | 9 ++++++++ src/index.cjs | 2 +- tests/helpers/node-version.ts | 6 ----- tests/integration/batch.test.ts | 24 ++++++++++---------- tests/integration/cli.test.ts | 18 +++++++-------- tests/unit/index-exports.test.ts | 10 ++++----- tests/unit/registry.test.ts | 10 ++++----- vitest.config.ts | 20 +++++------------ 15 files changed, 94 insertions(+), 70 deletions(-) rename scripts/{build-wasm.js => build-wasm.ts} (100%) rename scripts/{gen-deps.cjs => gen-deps.ts} (51%) create mode 100644 scripts/ts-resolve-hooks.ts create mode 100644 scripts/ts-resolve-loader.ts delete mode 100644 tests/helpers/node-version.ts diff --git a/.github/workflows/build-native.yml b/.github/workflows/build-native.yml index fb6760a2..77eb428d 100644 --- a/.github/workflows/build-native.yml +++ b/.github/workflows/build-native.yml @@ -60,7 +60,7 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v6 with: - node-version: 20 + node-version: 22 - name: Setup Rust uses: dtolnay/rust-toolchain@stable diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9e8bb2d9..ded156ca 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,7 +43,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest, macos-latest, windows-latest] - node-version: [20, 22] + node-version: [22] runs-on: ${{ matrix.os }} name: Test Node ${{ matrix.node-version }} (${{ matrix.os }}) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 060b964d..cf98cd1f 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -142,7 +142,7 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v6 with: - node-version: 20 + node-version: 22 - name: Setup Rust uses: dtolnay/rust-toolchain@stable diff --git a/package.json b/package.json index 4436814b..554c314d 100644 --- a/package.json +++ b/package.json @@ -26,23 +26,23 @@ "README.md" ], "engines": { - "node": ">=20" + "node": ">=22" }, "scripts": { - "build": "tsc", - "build:wasm": "node scripts/build-wasm.js", + "build": "tsc && node -e \"require('fs').writeFileSync('dist/index.cjs',require('fs').readFileSync('src/index.cjs','utf8').replace('./index.ts','./index.js'))\"", + "build:wasm": "node --experimental-strip-types scripts/build-wasm.ts", "typecheck": "tsc --noEmit", "verify-imports": "node --experimental-strip-types scripts/verify-imports.ts", - "test": "node scripts/test.js run", - "test:watch": "node scripts/test.js", - "test:coverage": "node scripts/test.js run --coverage", + "test": "vitest run", + "test:watch": "vitest", + "test:coverage": "vitest run --coverage", "lint": "biome check src/ tests/", "lint:fix": "biome check --write src/ tests/", "format": "biome format --write src/ tests/", "prepack": "npm run build", "clean": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true});require('fs').rmSync('.tsbuildinfo',{force:true})\"", "prepare": "npm run build:wasm && npm run build && husky && npm run deps:tree", - "deps:tree": "node scripts/gen-deps.cjs", + "deps:tree": "node --experimental-strip-types scripts/gen-deps.ts", "release": "commit-and-tag-version", "release:dry-run": "commit-and-tag-version --dry-run", "version": "node --experimental-strip-types scripts/sync-native-versions.ts && git add package.json crates/codegraph-core/Cargo.toml" diff --git a/scripts/build-wasm.js b/scripts/build-wasm.ts similarity index 100% rename from scripts/build-wasm.js rename to scripts/build-wasm.ts diff --git a/scripts/gen-deps.cjs b/scripts/gen-deps.ts similarity index 51% rename from scripts/gen-deps.cjs rename to scripts/gen-deps.ts index 32072004..6c7ec148 100644 --- a/scripts/gen-deps.cjs +++ b/scripts/gen-deps.ts @@ -1,18 +1,19 @@ -const { execSync } = require('child_process'); -const fs = require('fs'); -const path = require('path'); +#!/usr/bin/env node +import { execSync } from 'node:child_process'; +import { mkdirSync, writeFileSync } from 'node:fs'; +import { dirname, join } from 'node:path'; -const outFile = path.join('generated', 'DEPENDENCIES.md'); -fs.mkdirSync(path.dirname(outFile), { recursive: true }); +const outFile = join('generated', 'DEPENDENCIES.md'); +mkdirSync(dirname(outFile), { recursive: true }); try { const tree = execSync('npm ls --all --omit=dev', { encoding: 'utf8' }); - fs.writeFileSync(outFile, '# Dependencies\n\n```\n' + tree + '```\n'); -} catch (err) { + writeFileSync(outFile, '# Dependencies\n\n```\n' + tree + '```\n'); +} catch (err: any) { // npm ls exits non-zero on ELSPROBLEMS (version mismatches in optional deps). // If stdout still has content, write it; otherwise skip silently. if (err.stdout) { - fs.writeFileSync( + writeFileSync( outFile, '# Dependencies\n\n```\n' + err.stdout + '```\n', ); diff --git a/scripts/ts-resolve-hooks.ts b/scripts/ts-resolve-hooks.ts new file mode 100644 index 00000000..d8290945 --- /dev/null +++ b/scripts/ts-resolve-hooks.ts @@ -0,0 +1,28 @@ +/** + * ESM resolve hook — rewrites .js specifiers to .ts when the .js file + * does not exist on disk. Needed because TypeScript's moduleResolution + * "nodenext" convention uses .js extensions in imports, but at runtime + * the source files are .ts. + * + * Loaded via module.register() from ts-resolve-loader.ts. + */ + +import { existsSync } from 'node:fs'; +import { fileURLToPath } from 'node:url'; + +export async function resolve( + specifier: string, + context: { parentURL?: string; conditions: string[] }, + nextResolve: Function, +): Promise<{ url: string; shortCircuit?: boolean }> { + try { + return await nextResolve(specifier, context); + } catch (err: any) { + // Only attempt .js → .ts fallback for file-relative specifiers + if (err?.code === 'ERR_MODULE_NOT_FOUND' && specifier.endsWith('.js')) { + const tsSpecifier = specifier.slice(0, -3) + '.ts'; + return nextResolve(tsSpecifier, context); + } + throw err; + } +} diff --git a/scripts/ts-resolve-loader.ts b/scripts/ts-resolve-loader.ts new file mode 100644 index 00000000..241c87ec --- /dev/null +++ b/scripts/ts-resolve-loader.ts @@ -0,0 +1,9 @@ +/** + * Registers the .js → .ts ESM resolve hook. + * + * Usage: node --experimental-strip-types --import ./scripts/ts-resolve-loader.ts src/cli.ts + */ + +import { register } from 'node:module'; + +register(new URL('./ts-resolve-hooks.ts', import.meta.url)); diff --git a/src/index.cjs b/src/index.cjs index 811e449c..2b06f4a6 100644 --- a/src/index.cjs +++ b/src/index.cjs @@ -13,4 +13,4 @@ // Note: if import() rejects (e.g. missing dependency), the rejected Promise is cached // by the CJS module system and every subsequent require() call will re-surface the same // rejection without re-attempting the load. -module.exports = import('./index.js'); +module.exports = import('./index.ts'); diff --git a/tests/helpers/node-version.ts b/tests/helpers/node-version.ts deleted file mode 100644 index f8603a82..00000000 --- a/tests/helpers/node-version.ts +++ /dev/null @@ -1,6 +0,0 @@ -/** - * Node >= 22.6 supports --experimental-strip-types, required for tests that - * spawn child processes loading .ts source files directly. - */ -const [major, minor] = process.versions.node.split('.').map(Number); -export const canStripTypes = major > 22 || (major === 22 && minor >= 6); diff --git a/tests/integration/batch.test.ts b/tests/integration/batch.test.ts index 032ca6fe..5fbb2172 100644 --- a/tests/integration/batch.test.ts +++ b/tests/integration/batch.test.ts @@ -25,10 +25,6 @@ import { splitTargets, } from '../../src/features/batch.js'; -// Child processes load .ts files natively — requires Node >= 22.6 type stripping -const [_major, _minor] = process.versions.node.split('.').map(Number); -const canStripTypes = _major > 22 || (_major === 22 && _minor >= 6); - // ─── Helpers ─────────────────────────────────────────────────────────── function insertNode(db, name, kind, file, line) { @@ -212,17 +208,23 @@ describe('batchData — complexity (dbOnly signature)', () => { // ─── CLI smoke test ────────────────────────────────────────────────── -describe.skipIf(!canStripTypes)('batch CLI', () => { +describe('batch CLI', () => { const cliPath = path.resolve( path.dirname(new URL(import.meta.url).pathname.replace(/^\/([A-Z]:)/i, '$1')), - '../../src/cli.js', + '../../src/cli.ts', ); + const loaderUrl = new URL('../../scripts/ts-resolve-loader.ts', import.meta.url).href; + const NODE_TS_FLAGS = ['--experimental-strip-types', '--import', loaderUrl]; test('outputs valid JSON', () => { - const out = execFileSync('node', [cliPath, 'batch', 'query', 'authenticate', '--db', dbPath], { - encoding: 'utf-8', - timeout: 30_000, - }); + const out = execFileSync( + 'node', + [...NODE_TS_FLAGS, cliPath, 'batch', 'query', 'authenticate', '--db', dbPath], + { + encoding: 'utf-8', + timeout: 30_000, + }, + ); const parsed = JSON.parse(out); expect(parsed.command).toBe('query'); expect(parsed.total).toBe(1); @@ -232,7 +234,7 @@ describe.skipIf(!canStripTypes)('batch CLI', () => { test('batch accepts comma-separated positional targets', () => { const out = execFileSync( 'node', - [cliPath, 'batch', 'where', 'authenticate,validateToken', '--db', dbPath], + [...NODE_TS_FLAGS, cliPath, 'batch', 'where', 'authenticate,validateToken', '--db', dbPath], { encoding: 'utf-8', timeout: 30_000 }, ); const parsed = JSON.parse(out); diff --git a/tests/integration/cli.test.ts b/tests/integration/cli.test.ts index 9dc6b743..d254a578 100644 --- a/tests/integration/cli.test.ts +++ b/tests/integration/cli.test.ts @@ -9,11 +9,9 @@ import os from 'node:os'; import path from 'node:path'; import { afterAll, beforeAll, describe, expect, test } from 'vitest'; -// All tests spawn child processes that load .ts files — requires Node >= 22.6 -const [_major, _minor] = process.versions.node.split('.').map(Number); -const canStripTypes = _major > 22 || (_major === 22 && _minor >= 6); - -const CLI = path.resolve('src/cli.js'); +const CLI = path.resolve('src/cli.ts'); +const LOADER = new URL('../../scripts/ts-resolve-loader.ts', import.meta.url).href; +const NODE_TS_FLAGS = ['--experimental-strip-types', '--import', LOADER]; const FIXTURE_FILES = { 'math.js': ` @@ -44,7 +42,7 @@ let tmpDir: string, tmpHome: string, dbPath: string; /** Run the CLI and return stdout as a string. Throws on non-zero exit. */ function run(...args) { - return execFileSync('node', [CLI, ...args], { + return execFileSync('node', [...NODE_TS_FLAGS, CLI, ...args], { cwd: tmpDir, encoding: 'utf-8', timeout: 30_000, @@ -69,7 +67,7 @@ afterAll(() => { if (tmpHome) fs.rmSync(tmpHome, { recursive: true, force: true }); }); -describe.skipIf(!canStripTypes)('CLI smoke tests', () => { +describe('CLI smoke tests', () => { // ─── Build ─────────────────────────────────────────────────────────── test('build creates graph.db', () => { expect(fs.existsSync(dbPath)).toBe(true); @@ -200,7 +198,7 @@ describe.skipIf(!canStripTypes)('CLI smoke tests', () => { const { spawnSync } = require('node:child_process'); const result = spawnSync( 'node', - [CLI, 'query', 'sumOfSquares', '--path', 'add', '--db', dbPath, '--json'], + [...NODE_TS_FLAGS, CLI, 'query', 'sumOfSquares', '--path', 'add', '--db', dbPath, '--json'], { cwd: tmpDir, encoding: 'utf-8', @@ -241,12 +239,12 @@ describe.skipIf(!canStripTypes)('CLI smoke tests', () => { // ─── Registry CLI ─────────────────────────────────────────────────────── -describe.skipIf(!canStripTypes)('Registry CLI commands', () => { +describe('Registry CLI commands', () => { let tmpHome: string; /** Run CLI with isolated HOME to avoid touching real registry */ function runReg(...args) { - return execFileSync('node', [CLI, ...args], { + return execFileSync('node', [...NODE_TS_FLAGS, CLI, ...args], { cwd: tmpDir, encoding: 'utf-8', timeout: 30_000, diff --git a/tests/unit/index-exports.test.ts b/tests/unit/index-exports.test.ts index caa5a5fa..ad636436 100644 --- a/tests/unit/index-exports.test.ts +++ b/tests/unit/index-exports.test.ts @@ -1,12 +1,12 @@ import { readFileSync } from 'node:fs'; -import { createRequire } from 'node:module'; +import { createRequire, register } from 'node:module'; import { dirname, resolve } from 'node:path'; import { fileURLToPath } from 'node:url'; import { describe, expect, it } from 'vitest'; -// CJS require goes through Node's native loader — needs Node >= 22.6 for .ts -const [_major, _minor] = process.versions.node.split('.').map(Number); -const canStripTypes = _major > 22 || (_major === 22 && _minor >= 6); +// Register .js → .ts resolve hook so CJS wrapper's import('./index.ts') +// can resolve the .js import specifiers used throughout the source. +register(new URL('../../scripts/ts-resolve-hooks.ts', import.meta.url)); const __dirname = dirname(fileURLToPath(import.meta.url)); const pkg = JSON.parse(readFileSync(resolve(__dirname, '../../package.json'), 'utf8')); @@ -26,7 +26,7 @@ describe('index.js re-exports', () => { expect(typeof mod).toBe('object'); }); - it.skipIf(!canStripTypes)('CJS wrapper resolves to the same exports', async () => { + it('CJS wrapper resolves to the same exports', async () => { const require = createRequire(import.meta.url); const cjs = await require('../../src/index.cjs'); const esm = await import('../../src/index.js'); diff --git a/tests/unit/registry.test.ts b/tests/unit/registry.test.ts index 7b16c108..9a927804 100644 --- a/tests/unit/registry.test.ts +++ b/tests/unit/registry.test.ts @@ -15,10 +15,6 @@ import { unregisterRepo, } from '../../src/infrastructure/registry.js'; -// Child processes load .ts files natively — requires Node >= 22.6 type stripping -const [_major, _minor] = process.versions.node.split('.').map(Number); -const canStripTypes = _major > 22 || (_major === 22 && _minor >= 6); - let tmpDir: string; let registryPath: string; @@ -38,11 +34,15 @@ describe('REGISTRY_PATH', () => { expect(REGISTRY_PATH).toBe(path.join(os.homedir(), '.codegraph', 'registry.json')); }); - it.skipIf(!canStripTypes)('respects CODEGRAPH_REGISTRY_PATH env var', () => { + it('respects CODEGRAPH_REGISTRY_PATH env var', () => { const customPath = path.join(tmpDir, 'custom', 'registry.json'); + const loaderUrl = new URL('../../scripts/ts-resolve-loader.ts', import.meta.url).href; const result = execFileSync( 'node', [ + '--experimental-strip-types', + '--import', + loaderUrl, '--input-type=module', '-e', `import { REGISTRY_PATH } from './src/infrastructure/registry.js'; process.stdout.write(REGISTRY_PATH);`, diff --git a/vitest.config.ts b/vitest.config.ts index 73e6ca22..dc6999e6 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -1,30 +1,22 @@ -import { dirname, resolve } from 'node:path'; -import { fileURLToPath, pathToFileURL } from 'node:url'; import { defineConfig } from 'vitest/config'; -const __dirname = dirname(fileURLToPath(import.meta.url)); -const loaderPath = pathToFileURL(resolve(__dirname, 'scripts/ts-resolve-loader.js')).href; -const [major, minor] = process.versions.node.split('.').map(Number); -const supportsStripTypes = major > 22 || (major === 22 && minor >= 6); -const supportsHooks = major > 20 || (major === 20 && minor >= 6); +const [major] = process.versions.node.split('.').map(Number); const existing = process.env.NODE_OPTIONS || ''; +const stripFlag = major >= 23 ? '--strip-types' : '--experimental-strip-types'; export default defineConfig({ test: { globals: true, testTimeout: 30000, exclude: ['**/node_modules/**', '**/.git/**', '.claude/**'], - // Register the .ts resolve loader for Node's native ESM resolver. - // This covers child processes spawned by tests (e.g. CLI integration tests). + // Ensure child processes spawned by tests (e.g. CLI integration tests) + // can load .ts files via Node's built-in type stripping. env: { NODE_OPTIONS: [ existing, - supportsStripTypes && - !existing.includes('--experimental-strip-types') && - !existing.includes('--strip-types') - ? (major >= 23 ? '--strip-types' : '--experimental-strip-types') + !existing.includes('--experimental-strip-types') && !existing.includes('--strip-types') + ? stripFlag : '', - existing.includes(loaderPath) ? '' : (supportsHooks ? `--import ${loaderPath}` : ''), ].filter(Boolean).join(' '), }, }, From 8cfb0847599c856baddc286e6c1ccdae9d483441 Mon Sep 17 00:00:00 2001 From: carlos-alm <127798846+carlos-alm@users.noreply.github.com> Date: Tue, 24 Mar 2026 16:44:01 -0600 Subject: [PATCH 04/15] fix(ci): update embedding regression workflow to reference .ts test file (#588) The test file was renamed from .js to .ts in the Phase 6 migration but the CI workflow was not updated, causing test discovery to fail. --- .github/workflows/embedding-regression.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/embedding-regression.yml b/.github/workflows/embedding-regression.yml index a42cc6e7..e049911d 100644 --- a/.github/workflows/embedding-regression.yml +++ b/.github/workflows/embedding-regression.yml @@ -38,4 +38,4 @@ jobs: key: hf-models-minilm-v1 - name: Run embedding regression tests - run: npx vitest run tests/search/embedding-regression.test.js + run: npx vitest run tests/search/embedding-regression.test.ts From a6b8f2e7a89d5d4f8c5ecfe95b58d7439bfe130e Mon Sep 17 00:00:00 2001 From: carlos-alm <127798846+carlos-alm@users.noreply.github.com> Date: Tue, 24 Mar 2026 17:55:33 -0600 Subject: [PATCH 05/15] fix: tighten Node engines to >=22.6, restore strip-types guard, use replaceAll (#588) Impact: 1 functions changed, 0 affected --- package.json | 4 ++-- scripts/ts-resolve-hooks.ts | 2 +- vitest.config.ts | 5 +++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 554c314d..36805d53 100644 --- a/package.json +++ b/package.json @@ -26,10 +26,10 @@ "README.md" ], "engines": { - "node": ">=22" + "node": ">=22.6" }, "scripts": { - "build": "tsc && node -e \"require('fs').writeFileSync('dist/index.cjs',require('fs').readFileSync('src/index.cjs','utf8').replace('./index.ts','./index.js'))\"", + "build": "tsc && node -e \"require('fs').writeFileSync('dist/index.cjs',require('fs').readFileSync('src/index.cjs','utf8').replaceAll('./index.ts','./index.js'))\"", "build:wasm": "node --experimental-strip-types scripts/build-wasm.ts", "typecheck": "tsc --noEmit", "verify-imports": "node --experimental-strip-types scripts/verify-imports.ts", diff --git a/scripts/ts-resolve-hooks.ts b/scripts/ts-resolve-hooks.ts index d8290945..572fbf37 100644 --- a/scripts/ts-resolve-hooks.ts +++ b/scripts/ts-resolve-hooks.ts @@ -13,7 +13,7 @@ import { fileURLToPath } from 'node:url'; export async function resolve( specifier: string, context: { parentURL?: string; conditions: string[] }, - nextResolve: Function, + nextResolve: (specifier: string, context?: { parentURL?: string; conditions: string[] }) => Promise<{ url: string; shortCircuit?: boolean }>, ): Promise<{ url: string; shortCircuit?: boolean }> { try { return await nextResolve(specifier, context); diff --git a/vitest.config.ts b/vitest.config.ts index 67866087..76d7f5e0 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -1,7 +1,8 @@ import { defineConfig } from 'vitest/config'; -const [major] = process.versions.node.split('.').map(Number); +const [major, minor] = process.versions.node.split('.').map(Number); const existing = process.env.NODE_OPTIONS || ''; +const supportsStripTypes = major > 22 || (major === 22 && minor >= 6); const stripFlag = major >= 23 ? '--strip-types' : '--experimental-strip-types'; export default defineConfig({ @@ -15,7 +16,7 @@ export default defineConfig({ env: { NODE_OPTIONS: [ existing, - !existing.includes('--experimental-strip-types') && !existing.includes('--strip-types') + supportsStripTypes && !existing.includes('--experimental-strip-types') && !existing.includes('--strip-types') ? stripFlag : '', ].filter(Boolean).join(' '), From 04a2e50356f4d8b1fbc7f3dc5f095cdca2067da9 Mon Sep 17 00:00:00 2001 From: carlos-alm <127798846+carlos-alm@users.noreply.github.com> Date: Tue, 24 Mar 2026 18:18:22 -0600 Subject: [PATCH 06/15] fix(docs): use version-aware strip-types flag in pre-commit example (#588) Detect Node version at runtime and select --strip-types (>=23) vs --experimental-strip-types (22.x), matching the approach in vitest.config.ts. --- docs/examples/claude-code-hooks/pre-commit.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/examples/claude-code-hooks/pre-commit.sh b/docs/examples/claude-code-hooks/pre-commit.sh index 49187b5c..96f3e23f 100644 --- a/docs/examples/claude-code-hooks/pre-commit.sh +++ b/docs/examples/claude-code-hooks/pre-commit.sh @@ -48,7 +48,8 @@ fi # Run all checks in a single Node.js process HOOK_DIR="$(cd "$(dirname "$0")" && pwd)" -RESULT=$(node --experimental-strip-types "$HOOK_DIR/pre-commit-checks.ts" "$WORK_ROOT" "$EDITED_FILES" "$STAGED" 2>/dev/null) || true +STRIP_FLAG=$(node -e "const [M,m]=process.versions.node.split('.').map(Number); console.log(M>=23?'--strip-types':'--experimental-strip-types')") +RESULT=$(node $STRIP_FLAG "$HOOK_DIR/pre-commit-checks.ts" "$WORK_ROOT" "$EDITED_FILES" "$STAGED" 2>/dev/null) || true if [ -z "$RESULT" ]; then exit 0 From 57bcfaf1fd67e16d3d6f12cbf599fd6286a0ac85 Mon Sep 17 00:00:00 2001 From: carlos-alm <127798846+carlos-alm@users.noreply.github.com> Date: Tue, 24 Mar 2026 18:18:35 -0600 Subject: [PATCH 07/15] fix(types): add TypeScript parameter types to test helpers (#588) Replace JSDoc-only annotations in fixtures.ts with proper TypeScript interfaces and typed parameters. Add interfaces and typed parameters to resolution-benchmark.test.ts helper functions. Eliminates all noExplicitAny warnings in both files. Impact: 22 functions changed, 0 affected --- .../resolution/resolution-benchmark.test.ts | 72 ++++++++--- tests/helpers/fixtures.ts | 112 ++++++++++-------- 2 files changed, 118 insertions(+), 66 deletions(-) diff --git a/tests/benchmarks/resolution/resolution-benchmark.test.ts b/tests/benchmarks/resolution/resolution-benchmark.test.ts index 00d4d99e..599c60cc 100644 --- a/tests/benchmarks/resolution/resolution-benchmark.test.ts +++ b/tests/benchmarks/resolution/resolution-benchmark.test.ts @@ -17,6 +17,42 @@ import { afterAll, beforeAll, describe, expect, test } from 'vitest'; import { openReadonlyOrFail } from '../../../src/db/index.js'; import { buildGraph } from '../../../src/domain/graph/builder.js'; +// ── Types ───────────────────────────────────────────────────────────────── + +interface ResolvedEdge { + source_name: string; + source_file: string; + target_name: string; + target_file: string; + kind: string; + confidence: number; +} + +interface ExpectedEdge { + source: { name: string; file: string }; + target: { name: string; file: string }; + mode?: string; +} + +interface ModeMetrics { + expected: number; + resolved: number; + recall?: number; +} + +interface BenchmarkMetrics { + precision: number; + recall: number; + truePositives: number; + falsePositives: number; + falseNegatives: number; + totalResolved: number; + totalExpected: number; + byMode: Record; + falsePositiveEdges: string[]; + falseNegativeEdges: string[]; +} + // ── Configuration ──────────────────────────────────────────────────────── const FIXTURES_DIR = path.join(import.meta.dirname, 'fixtures'); @@ -40,7 +76,7 @@ const THRESHOLDS = { * Copy fixture to a temp directory so buildGraph can write .codegraph/ without * polluting the repo. */ -function copyFixture(lang) { +function copyFixture(lang: string): string { const src = path.join(FIXTURES_DIR, lang); const tmp = fs.mkdtempSync(path.join(os.tmpdir(), `codegraph-resolution-${lang}-`)); for (const entry of fs.readdirSync(src, { withFileTypes: true })) { @@ -54,7 +90,7 @@ function copyFixture(lang) { /** * Build graph for a fixture directory. */ -async function buildFixtureGraph(fixtureDir) { +async function buildFixtureGraph(fixtureDir: string): Promise { await buildGraph(fixtureDir, { incremental: false, engine: 'wasm', @@ -68,7 +104,7 @@ async function buildFixtureGraph(fixtureDir) { * Extract all call edges from the built graph DB. * Returns array of { sourceName, sourceFile, targetName, targetFile, kind, confidence }. */ -function extractResolvedEdges(fixtureDir) { +function extractResolvedEdges(fixtureDir: string) { const dbPath = path.join(fixtureDir, '.codegraph', 'graph.db'); const db = openReadonlyOrFail(dbPath); try { @@ -97,14 +133,19 @@ function extractResolvedEdges(fixtureDir) { /** * Normalize a file path to just the basename for comparison. */ -function normalizeFile(filePath) { +function normalizeFile(filePath: string): string { return path.basename(filePath); } /** * Build a string key for an edge to enable set-based comparison. */ -function edgeKey(sourceName, sourceFile, targetName, targetFile) { +function edgeKey( + sourceName: string, + sourceFile: string, + targetName: string, + targetFile: string, +): string { return `${sourceName}@${normalizeFile(sourceFile)} -> ${targetName}@${normalizeFile(targetFile)}`; } @@ -112,7 +153,10 @@ function edgeKey(sourceName, sourceFile, targetName, targetFile) { * Compare resolved edges against expected edges manifest. * Returns precision, recall, and detailed breakdown by mode. */ -function computeMetrics(resolvedEdges, expectedEdges) { +function computeMetrics( + resolvedEdges: ResolvedEdge[], + expectedEdges: ExpectedEdge[], +): BenchmarkMetrics { // Build sets for overall comparison const resolvedSet = new Set( resolvedEdges.map((e) => edgeKey(e.source_name, e.source_file, e.target_name, e.target_file)), @@ -135,7 +179,7 @@ function computeMetrics(resolvedEdges, expectedEdges) { const recall = expectedSet.size > 0 ? truePositives.size / expectedSet.size : 0; // Break down by resolution mode - const byMode = {}; + const byMode: Record = {}; for (const edge of expectedEdges) { const mode = edge.mode || 'unknown'; if (!byMode[mode]) byMode[mode] = { expected: 0, resolved: 0 }; @@ -168,7 +212,7 @@ function computeMetrics(resolvedEdges, expectedEdges) { /** * Format a metrics report for console output. */ -function formatReport(lang, metrics) { +function formatReport(lang: string, metrics: BenchmarkMetrics): string { const lines = [ `\n ── ${lang.toUpperCase()} Resolution Metrics ──`, ` Precision: ${(metrics.precision * 100).toFixed(1)}% (${metrics.truePositives} correct / ${metrics.totalResolved} resolved)`, @@ -208,9 +252,9 @@ function formatReport(lang, metrics) { /** * Discover all fixture languages that have an expected-edges.json manifest. */ -function discoverFixtures() { +function discoverFixtures(): string[] { if (!fs.existsSync(FIXTURES_DIR)) return []; - const languages = []; + const languages: string[] = []; for (const dir of fs.readdirSync(FIXTURES_DIR)) { const manifestPath = path.join(FIXTURES_DIR, dir, 'expected-edges.json'); if (fs.existsSync(manifestPath)) { @@ -223,7 +267,7 @@ function discoverFixtures() { const languages = discoverFixtures(); /** Stores all results for the final summary */ -const allResults = {}; +const allResults: Record = {}; describe('Call Resolution Precision/Recall', () => { afterAll(() => { @@ -243,9 +287,9 @@ describe('Call Resolution Precision/Recall', () => { for (const lang of languages) { describe(lang, () => { let fixtureDir: string; - let resolvedEdges: any[]; - let expectedEdges: any[]; - let metrics: any; + let resolvedEdges: ResolvedEdge[]; + let expectedEdges: ExpectedEdge[]; + let metrics: BenchmarkMetrics; beforeAll(async () => { fixtureDir = copyFixture(lang); diff --git a/tests/helpers/fixtures.ts b/tests/helpers/fixtures.ts index e9225b12..33154720 100644 --- a/tests/helpers/fixtures.ts +++ b/tests/helpers/fixtures.ts @@ -10,84 +10,89 @@ import { InMemoryRepository } from '../../src/db/repository/in-memory-repository * .calls('authMiddleware', 'authenticate') * .build(); */ -class TestRepoBuilder { - #pending = { nodes: [], edges: [], complexity: [] }; +interface PendingNode { + name: string; + kind: string; + file: string; + line: number; + [key: string]: unknown; +} - /** - * Add a function node. - * @param {string} name - * @param {string} file - * @param {number} line - * @param {object} [extra] - Additional node attrs (role, end_line, scope, etc.) - */ - fn(name, file, line, extra = {}) { +interface PendingEdge { + source: string; + target: string; + kind: string; +} + +interface PendingComplexity { + name: string; + metrics: Record; +} + +class TestRepoBuilder { + #pending: { nodes: PendingNode[]; edges: PendingEdge[]; complexity: PendingComplexity[] } = { + nodes: [], + edges: [], + complexity: [], + }; + + /** Add a function node. */ + fn(name: string, file: string, line: number, extra: Record = {}): this { return this.#addNode(name, 'function', file, line, extra); } - /** - * Add a method node. - */ - method(name, file, line, extra = {}) { + /** Add a method node. */ + method(name: string, file: string, line: number, extra: Record = {}): this { return this.#addNode(name, 'method', file, line, extra); } - /** - * Add a class node. - */ - cls(name, file, line, extra = {}) { + /** Add a class node. */ + cls(name: string, file: string, line: number, extra: Record = {}): this { return this.#addNode(name, 'class', file, line, extra); } - /** - * Add a file node. - */ - file(filePath) { + /** Add a file node. */ + file(filePath: string): this { return this.#addNode(filePath, 'file', filePath, 0); } - /** - * Add an arbitrary node. - */ - node(name, kind, file, line, extra = {}) { + /** Add an arbitrary node. */ + node( + name: string, + kind: string, + file: string, + line: number, + extra: Record = {}, + ): this { return this.#addNode(name, kind, file, line, extra); } - /** - * Add a 'calls' edge between two named nodes. - */ - calls(sourceName, targetName) { + /** Add a 'calls' edge between two named nodes. */ + calls(sourceName: string, targetName: string): this { this.#pending.edges.push({ source: sourceName, target: targetName, kind: 'calls' }); return this; } - /** - * Add an 'imports' edge. - */ - imports(sourceName, targetName) { + /** Add an 'imports' edge. */ + imports(sourceName: string, targetName: string): this { this.#pending.edges.push({ source: sourceName, target: targetName, kind: 'imports' }); return this; } - /** - * Add an 'extends' edge. - */ - extends(sourceName, targetName) { + /** Add an 'extends' edge. */ + extends(sourceName: string, targetName: string): this { this.#pending.edges.push({ source: sourceName, target: targetName, kind: 'extends' }); return this; } - /** - * Add an edge of any kind. - */ - edge(sourceName, targetName, kind) { + /** Add an edge of any kind. */ + edge(sourceName: string, targetName: string, kind: string): this { this.#pending.edges.push({ source: sourceName, target: targetName, kind }); return this; } - /** - * Add complexity metrics for a named node. - */ - complexity(name, metrics) { + /** Add complexity metrics for a named node. */ + complexity(name: string, metrics: Record): this { this.#pending.complexity.push({ name, metrics }); return this; } @@ -129,16 +134,19 @@ class TestRepoBuilder { return { repo, ids }; } - #addNode(name, kind, file, line, extra = {}) { + #addNode( + name: string, + kind: string, + file: string, + line: number, + extra: Record = {}, + ): this { this.#pending.nodes.push({ name, kind, file, line, ...extra }); return this; } } -/** - * Create a new TestRepoBuilder. - * @returns {TestRepoBuilder} - */ -export function createTestRepo() { +/** Create a new TestRepoBuilder. */ +export function createTestRepo(): TestRepoBuilder { return new TestRepoBuilder(); } From a21a4196db38075edae4c91b07eb14b406df90e6 Mon Sep 17 00:00:00 2001 From: carlos-alm <127798846+carlos-alm@users.noreply.github.com> Date: Tue, 24 Mar 2026 22:16:46 -0600 Subject: [PATCH 08/15] fix(hooks): convert pre-commit-checks.ts from CJS require() to ESM imports (#588) The file was renamed from .js to .ts but retained CommonJS require() calls. Since the repo has "type": "module", Node loads .ts files as ESM under --strip-types, where require is not defined. This caused a silent ReferenceError swallowed by 2>/dev/null || true in pre-commit.sh, making all pre-commit checks (cycles, dead exports, diff-impact) non-functional. Convert to ESM imports with createRequire for dynamic runtime requires. Also update the file header comment to reference .ts instead of .js. --- .../examples/claude-code-hooks/pre-commit-checks.ts | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/docs/examples/claude-code-hooks/pre-commit-checks.ts b/docs/examples/claude-code-hooks/pre-commit-checks.ts index f6bd6cfb..c863d8fd 100644 --- a/docs/examples/claude-code-hooks/pre-commit-checks.ts +++ b/docs/examples/claude-code-hooks/pre-commit-checks.ts @@ -1,8 +1,8 @@ /** - * pre-commit-checks.js — Consolidated pre-commit codegraph checks. + * pre-commit-checks.ts — Consolidated pre-commit codegraph checks. * Single Node.js process that runs all checks and returns structured JSON. * - * Usage: node pre-commit-checks.js + * Usage: node pre-commit-checks.ts * * Output JSON: { action: "deny"|"allow", reason?: string, context?: string[] } * @@ -12,8 +12,11 @@ * 3. Diff-impact (informational) — shows blast radius of staged changes */ -const fs = require('fs'); -const path = require('path'); +import fs from 'node:fs'; +import path from 'node:path'; +import { createRequire } from 'node:module'; + +const require = createRequire(import.meta.url); const root = process.argv[2]; const editedRaw = process.argv[3] || ''; @@ -124,7 +127,7 @@ try { // Scan for dynamic import() consumers const srcDir = path.join(root, 'src'); - function scanDynamic(dir) { + function scanDynamic(dir: string) { for (const ent of fs.readdirSync(dir, { withFileTypes: true })) { if (ent.isDirectory()) { scanDynamic(path.join(dir, ent.name)); continue; } if (!/\.(js|ts|tsx)$/.test(ent.name)) continue; From 563de547d14473ddcdeb7b61498196e45df02a5a Mon Sep 17 00:00:00 2001 From: carlos-alm <127798846+carlos-alm@users.noreply.github.com> Date: Tue, 24 Mar 2026 22:16:59 -0600 Subject: [PATCH 09/15] fix(ci): add --import loader to benchmark workflow for Node 22 compatibility (#588) Benchmark scripts import ./lib/bench-config.js and ./lib/fork-engine.js but only .ts files exist on disk. Node 22's --experimental-strip-types does not auto-resolve .js to .ts (that was added in Node 23+). Add --import ./scripts/ts-resolve-loader.js to all four benchmark invocations so the ESM loader resolves .js specifiers to their .ts counterparts. --- .github/workflows/benchmark.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 064ceedd..25867c63 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -93,7 +93,7 @@ jobs: if [ "${{ steps.mode.outputs.source }}" = "npm" ]; then ARGS="$ARGS --npm" fi - node --experimental-strip-types scripts/benchmark.ts $ARGS 2>/dev/null > benchmark-result.json + node --experimental-strip-types --import ./scripts/ts-resolve-loader.js scripts/benchmark.ts $ARGS 2>/dev/null > benchmark-result.json - name: Update build report if: steps.existing.outputs.skip != 'true' @@ -244,7 +244,7 @@ jobs: if [ "${{ steps.mode.outputs.source }}" = "npm" ]; then ARGS="$ARGS --npm" fi - node --experimental-strip-types scripts/embedding-benchmark.ts $ARGS 2>/dev/null > embedding-benchmark-result.json + node --experimental-strip-types --import ./scripts/ts-resolve-loader.js scripts/embedding-benchmark.ts $ARGS 2>/dev/null > embedding-benchmark-result.json - name: Update embedding report if: steps.existing.outputs.skip != 'true' @@ -381,7 +381,7 @@ jobs: if [ "${{ steps.mode.outputs.source }}" = "npm" ]; then ARGS="$ARGS --npm" fi - node --experimental-strip-types scripts/query-benchmark.ts $ARGS 2>/dev/null > query-benchmark-result.json + node --experimental-strip-types --import ./scripts/ts-resolve-loader.js scripts/query-benchmark.ts $ARGS 2>/dev/null > query-benchmark-result.json - name: Update query report if: steps.existing.outputs.skip != 'true' @@ -518,7 +518,7 @@ jobs: if [ "${{ steps.mode.outputs.source }}" = "npm" ]; then ARGS="$ARGS --npm" fi - node --experimental-strip-types scripts/incremental-benchmark.ts $ARGS 2>/dev/null > incremental-benchmark-result.json + node --experimental-strip-types --import ./scripts/ts-resolve-loader.js scripts/incremental-benchmark.ts $ARGS 2>/dev/null > incremental-benchmark-result.json - name: Update incremental report if: steps.existing.outputs.skip != 'true' From 9b8eef1c3479319ade4a71ca1c4698bc7e47dac8 Mon Sep 17 00:00:00 2001 From: carlos-alm <127798846+carlos-alm@users.noreply.github.com> Date: Tue, 24 Mar 2026 23:16:54 -0600 Subject: [PATCH 10/15] fix: version-aware strip-types flag in benchmark.yml, update docs (#588) - Use dynamic STRIP_FLAG selection in all benchmark.yml node invocations for forward compatibility with Node 23+ (--strip-types vs --experimental-strip-types) - Update token-benchmark.ts usage docs to include --import loader flag - Update CLAUDE.md Node version from >= 20 to >= 22.6 --- .github/workflows/benchmark.yml | 28 ++++++++++++++++++++-------- CLAUDE.md | 2 +- scripts/token-benchmark.ts | 6 +++--- 3 files changed, 24 insertions(+), 12 deletions(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 25867c63..736fb7cb 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -89,15 +89,18 @@ jobs: - name: Run build benchmark if: steps.existing.outputs.skip != 'true' run: | + STRIP_FLAG=$(node -e "const [M]=process.versions.node.split('.').map(Number); console.log(M>=23?'--strip-types':'--experimental-strip-types')") ARGS="--version ${{ steps.mode.outputs.version }}" if [ "${{ steps.mode.outputs.source }}" = "npm" ]; then ARGS="$ARGS --npm" fi - node --experimental-strip-types --import ./scripts/ts-resolve-loader.js scripts/benchmark.ts $ARGS 2>/dev/null > benchmark-result.json + node $STRIP_FLAG --import ./scripts/ts-resolve-loader.js scripts/benchmark.ts $ARGS 2>/dev/null > benchmark-result.json - name: Update build report if: steps.existing.outputs.skip != 'true' - run: node --experimental-strip-types scripts/update-benchmark-report.ts benchmark-result.json + run: | + STRIP_FLAG=$(node -e "const [M]=process.versions.node.split('.').map(Number); console.log(M>=23?'--strip-types':'--experimental-strip-types')") + node $STRIP_FLAG scripts/update-benchmark-report.ts benchmark-result.json - name: Upload build result if: steps.existing.outputs.skip != 'true' @@ -240,15 +243,18 @@ jobs: env: HF_TOKEN: ${{ secrets.HF_TOKEN }} run: | + STRIP_FLAG=$(node -e "const [M]=process.versions.node.split('.').map(Number); console.log(M>=23?'--strip-types':'--experimental-strip-types')") ARGS="--version ${{ steps.mode.outputs.version }}" if [ "${{ steps.mode.outputs.source }}" = "npm" ]; then ARGS="$ARGS --npm" fi - node --experimental-strip-types --import ./scripts/ts-resolve-loader.js scripts/embedding-benchmark.ts $ARGS 2>/dev/null > embedding-benchmark-result.json + node $STRIP_FLAG --import ./scripts/ts-resolve-loader.js scripts/embedding-benchmark.ts $ARGS 2>/dev/null > embedding-benchmark-result.json - name: Update embedding report if: steps.existing.outputs.skip != 'true' - run: node --experimental-strip-types scripts/update-embedding-report.ts embedding-benchmark-result.json + run: | + STRIP_FLAG=$(node -e "const [M]=process.versions.node.split('.').map(Number); console.log(M>=23?'--strip-types':'--experimental-strip-types')") + node $STRIP_FLAG scripts/update-embedding-report.ts embedding-benchmark-result.json - name: Upload embedding result if: steps.existing.outputs.skip != 'true' @@ -377,15 +383,18 @@ jobs: - name: Run query benchmark if: steps.existing.outputs.skip != 'true' run: | + STRIP_FLAG=$(node -e "const [M]=process.versions.node.split('.').map(Number); console.log(M>=23?'--strip-types':'--experimental-strip-types')") ARGS="--version ${{ steps.mode.outputs.version }}" if [ "${{ steps.mode.outputs.source }}" = "npm" ]; then ARGS="$ARGS --npm" fi - node --experimental-strip-types --import ./scripts/ts-resolve-loader.js scripts/query-benchmark.ts $ARGS 2>/dev/null > query-benchmark-result.json + node $STRIP_FLAG --import ./scripts/ts-resolve-loader.js scripts/query-benchmark.ts $ARGS 2>/dev/null > query-benchmark-result.json - name: Update query report if: steps.existing.outputs.skip != 'true' - run: node --experimental-strip-types scripts/update-query-report.ts query-benchmark-result.json + run: | + STRIP_FLAG=$(node -e "const [M]=process.versions.node.split('.').map(Number); console.log(M>=23?'--strip-types':'--experimental-strip-types')") + node $STRIP_FLAG scripts/update-query-report.ts query-benchmark-result.json - name: Upload query result if: steps.existing.outputs.skip != 'true' @@ -514,15 +523,18 @@ jobs: - name: Run incremental benchmark if: steps.existing.outputs.skip != 'true' run: | + STRIP_FLAG=$(node -e "const [M]=process.versions.node.split('.').map(Number); console.log(M>=23?'--strip-types':'--experimental-strip-types')") ARGS="--version ${{ steps.mode.outputs.version }}" if [ "${{ steps.mode.outputs.source }}" = "npm" ]; then ARGS="$ARGS --npm" fi - node --experimental-strip-types --import ./scripts/ts-resolve-loader.js scripts/incremental-benchmark.ts $ARGS 2>/dev/null > incremental-benchmark-result.json + node $STRIP_FLAG --import ./scripts/ts-resolve-loader.js scripts/incremental-benchmark.ts $ARGS 2>/dev/null > incremental-benchmark-result.json - name: Update incremental report if: steps.existing.outputs.skip != 'true' - run: node --experimental-strip-types scripts/update-incremental-report.ts incremental-benchmark-result.json + run: | + STRIP_FLAG=$(node -e "const [M]=process.versions.node.split('.').map(Number); console.log(M>=23?'--strip-types':'--experimental-strip-types')") + node $STRIP_FLAG scripts/update-incremental-report.ts incremental-benchmark-result.json - name: Upload incremental result if: steps.existing.outputs.skip != 'true' diff --git a/CLAUDE.md b/CLAUDE.md index 6d441c78..5c97e60a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -218,4 +218,4 @@ This repo uses [Greptile](https://greptile.com) for automated PR reviews. After ## Node Version -Requires Node >= 20. +Requires Node >= 22.6. diff --git a/scripts/token-benchmark.ts b/scripts/token-benchmark.ts index 1598e6dc..02e053bc 100644 --- a/scripts/token-benchmark.ts +++ b/scripts/token-benchmark.ts @@ -12,9 +12,9 @@ * ANTHROPIC_API_KEY set in environment * * Usage: - * node scripts/token-benchmark.js > result.json - * node scripts/token-benchmark.js --runs 1 --issues csrf-case-insensitive - * node scripts/token-benchmark.js --nextjs-dir /tmp/next.js --skip-graph + * node --experimental-strip-types --import ./scripts/ts-resolve-loader.js scripts/token-benchmark.ts > result.json + * node --experimental-strip-types --import ./scripts/ts-resolve-loader.js scripts/token-benchmark.ts --runs 1 --issues csrf-case-insensitive + * node --experimental-strip-types --import ./scripts/ts-resolve-loader.js scripts/token-benchmark.ts --nextjs-dir /tmp/next.js --skip-graph */ import { execFileSync, execSync } from 'node:child_process'; From 966ec3e2dc108035a38d8de865885ad8d8618720 Mon Sep 17 00:00:00 2001 From: carlos-alm <127798846+carlos-alm@users.noreply.github.com> Date: Tue, 24 Mar 2026 23:17:55 -0600 Subject: [PATCH 11/15] fix: remove unused .ts loader duplicates (#588) ESM loader hooks must stay as .js files for bootstrap reasons. The .ts duplicates were never referenced by any CI workflow or package script and only created confusion about which version to use. --- scripts/ts-resolve-hooks.ts | 28 ---------------------------- scripts/ts-resolve-loader.ts | 9 --------- 2 files changed, 37 deletions(-) delete mode 100644 scripts/ts-resolve-hooks.ts delete mode 100644 scripts/ts-resolve-loader.ts diff --git a/scripts/ts-resolve-hooks.ts b/scripts/ts-resolve-hooks.ts deleted file mode 100644 index 572fbf37..00000000 --- a/scripts/ts-resolve-hooks.ts +++ /dev/null @@ -1,28 +0,0 @@ -/** - * ESM resolve hook — rewrites .js specifiers to .ts when the .js file - * does not exist on disk. Needed because TypeScript's moduleResolution - * "nodenext" convention uses .js extensions in imports, but at runtime - * the source files are .ts. - * - * Loaded via module.register() from ts-resolve-loader.ts. - */ - -import { existsSync } from 'node:fs'; -import { fileURLToPath } from 'node:url'; - -export async function resolve( - specifier: string, - context: { parentURL?: string; conditions: string[] }, - nextResolve: (specifier: string, context?: { parentURL?: string; conditions: string[] }) => Promise<{ url: string; shortCircuit?: boolean }>, -): Promise<{ url: string; shortCircuit?: boolean }> { - try { - return await nextResolve(specifier, context); - } catch (err: any) { - // Only attempt .js → .ts fallback for file-relative specifiers - if (err?.code === 'ERR_MODULE_NOT_FOUND' && specifier.endsWith('.js')) { - const tsSpecifier = specifier.slice(0, -3) + '.ts'; - return nextResolve(tsSpecifier, context); - } - throw err; - } -} diff --git a/scripts/ts-resolve-loader.ts b/scripts/ts-resolve-loader.ts deleted file mode 100644 index 241c87ec..00000000 --- a/scripts/ts-resolve-loader.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Registers the .js → .ts ESM resolve hook. - * - * Usage: node --experimental-strip-types --import ./scripts/ts-resolve-loader.ts src/cli.ts - */ - -import { register } from 'node:module'; - -register(new URL('./ts-resolve-hooks.ts', import.meta.url)); From f19aec912f55d18ce97d5b4db5570c332bc3d3ea Mon Sep 17 00:00:00 2001 From: carlos-alm <127798846+carlos-alm@users.noreply.github.com> Date: Tue, 24 Mar 2026 23:21:55 -0600 Subject: [PATCH 12/15] Revert "fix: remove unused .ts loader duplicates (#588)" This reverts commit 966ec3e2dc108035a38d8de865885ad8d8618720. --- scripts/ts-resolve-hooks.ts | 28 ++++++++++++++++++++++++++++ scripts/ts-resolve-loader.ts | 9 +++++++++ 2 files changed, 37 insertions(+) create mode 100644 scripts/ts-resolve-hooks.ts create mode 100644 scripts/ts-resolve-loader.ts diff --git a/scripts/ts-resolve-hooks.ts b/scripts/ts-resolve-hooks.ts new file mode 100644 index 00000000..572fbf37 --- /dev/null +++ b/scripts/ts-resolve-hooks.ts @@ -0,0 +1,28 @@ +/** + * ESM resolve hook — rewrites .js specifiers to .ts when the .js file + * does not exist on disk. Needed because TypeScript's moduleResolution + * "nodenext" convention uses .js extensions in imports, but at runtime + * the source files are .ts. + * + * Loaded via module.register() from ts-resolve-loader.ts. + */ + +import { existsSync } from 'node:fs'; +import { fileURLToPath } from 'node:url'; + +export async function resolve( + specifier: string, + context: { parentURL?: string; conditions: string[] }, + nextResolve: (specifier: string, context?: { parentURL?: string; conditions: string[] }) => Promise<{ url: string; shortCircuit?: boolean }>, +): Promise<{ url: string; shortCircuit?: boolean }> { + try { + return await nextResolve(specifier, context); + } catch (err: any) { + // Only attempt .js → .ts fallback for file-relative specifiers + if (err?.code === 'ERR_MODULE_NOT_FOUND' && specifier.endsWith('.js')) { + const tsSpecifier = specifier.slice(0, -3) + '.ts'; + return nextResolve(tsSpecifier, context); + } + throw err; + } +} diff --git a/scripts/ts-resolve-loader.ts b/scripts/ts-resolve-loader.ts new file mode 100644 index 00000000..241c87ec --- /dev/null +++ b/scripts/ts-resolve-loader.ts @@ -0,0 +1,9 @@ +/** + * Registers the .js → .ts ESM resolve hook. + * + * Usage: node --experimental-strip-types --import ./scripts/ts-resolve-loader.ts src/cli.ts + */ + +import { register } from 'node:module'; + +register(new URL('./ts-resolve-hooks.ts', import.meta.url)); From 39d07f17f14feadd0aa17730d85875e2b2ffe524 Mon Sep 17 00:00:00 2001 From: carlos-alm <127798846+carlos-alm@users.noreply.github.com> Date: Tue, 24 Mar 2026 23:34:11 -0600 Subject: [PATCH 13/15] fix(ci): use version-aware strip-types flag in verify-imports step (#588) --- .github/workflows/ci.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ded156ca..26bd4f16 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -113,7 +113,9 @@ jobs: node-version: 22 - name: Verify all dynamic imports resolve - run: node --experimental-strip-types scripts/verify-imports.ts + run: | + STRIP_FLAG=$(node -e "const [M]=process.versions.node.split('.').map(Number); console.log(M>=23?'--strip-types':'--experimental-strip-types')") + node $STRIP_FLAG scripts/verify-imports.ts rust-check: runs-on: ubuntu-latest From d5f30c7ea19243df138e3be522afec934833acb6 Mon Sep 17 00:00:00 2001 From: carlos-alm <127798846+carlos-alm@users.noreply.github.com> Date: Wed, 25 Mar 2026 00:20:22 -0600 Subject: [PATCH 14/15] fix(ci): use dynamic strip-types flag in publish workflow (#588) --- .github/workflows/publish.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index cf98cd1f..af3e79b7 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -224,7 +224,8 @@ jobs: VERSION: ${{ needs.compute-version.outputs.version }} run: | npm version "$VERSION" --no-git-tag-version --allow-same-version - node --experimental-strip-types scripts/sync-native-versions.ts --strip + STRIP_FLAG=$(node -e "const [M]=process.versions.node.split('.').map(Number); console.log(M>=23?'--strip-types':'--experimental-strip-types')") + node $STRIP_FLAG scripts/sync-native-versions.ts --strip echo "Packaging version $VERSION" - name: Build TypeScript @@ -405,7 +406,8 @@ jobs: run: | git checkout -- package-lock.json npm version "$VERSION" --no-git-tag-version --allow-same-version - node --experimental-strip-types scripts/sync-native-versions.ts + STRIP_FLAG=$(node -e "const [M]=process.versions.node.split('.').map(Number); console.log(M>=23?'--strip-types':'--experimental-strip-types')") + node $STRIP_FLAG scripts/sync-native-versions.ts echo "Publishing version $VERSION" - name: Build TypeScript From 83f2633389f75fd884f1b86e28debe93e914f07a Mon Sep 17 00:00:00 2001 From: carlos-alm <127798846+carlos-alm@users.noreply.github.com> Date: Wed, 25 Mar 2026 00:20:34 -0600 Subject: [PATCH 15/15] fix: remove dead scripts/test.js (#588) --- scripts/test.js | 46 ---------------------------------------------- 1 file changed, 46 deletions(-) delete mode 100644 scripts/test.js diff --git a/scripts/test.js b/scripts/test.js deleted file mode 100644 index ea317a7d..00000000 --- a/scripts/test.js +++ /dev/null @@ -1,46 +0,0 @@ -#!/usr/bin/env node -/** - * Test runner wrapper that configures Node.js for the TypeScript migration - * before spawning vitest. Adds --experimental-strip-types on Node >= 22.6 - * so child processes can execute .ts files natively. - */ - -import { spawnSync } from 'node:child_process'; -import { fileURLToPath, pathToFileURL } from 'node:url'; -import { dirname, resolve } from 'node:path'; - -const __dirname = dirname(fileURLToPath(import.meta.url)); -const hook = pathToFileURL(resolve(__dirname, 'ts-resolver-hook.js')).href; - -const args = process.argv.slice(2); -const vitestBin = resolve(__dirname, '..', 'node_modules', 'vitest', 'vitest.mjs'); - -const [major, minor] = process.versions.node.split('.').map(Number); -const supportsStripTypes = major > 22 || (major === 22 && minor >= 6); - -// Build NODE_OPTIONS: resolver hook + type stripping (Node >= 22.6) -const hookImport = `--import ${hook}`; -const existing = process.env.NODE_OPTIONS || ''; -const parts = [ - existing.includes(hookImport) ? null : hookImport, - supportsStripTypes && !existing.includes('--experimental-strip-types') && !existing.includes('--strip-types') - ? (major >= 23 ? '--strip-types' : '--experimental-strip-types') - : null, - existing || null, -].filter(Boolean); - -// Spawn vitest via node directly — avoids shell: true and works cross-platform -const result = spawnSync(process.execPath, [vitestBin, ...args], { - stdio: 'inherit', - env: { - ...process.env, - NODE_OPTIONS: parts.join(' '), - }, -}); - -if (result.error) { - process.stderr.write(`[test runner] Failed to spawn vitest: ${result.error.message}\n`); - process.exit(1); -} - -process.exit(result.status ?? 1);