@optave/codegraph 3.1.4 → 3.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +26 -70
- package/package.json +10 -8
- package/src/ast-analysis/engine.js +32 -12
- package/src/ast-analysis/shared.js +2 -2
- package/src/cli/commands/ast.js +2 -6
- package/src/cli/commands/audit.js +9 -10
- package/src/cli/commands/batch.js +4 -4
- package/src/cli/commands/branch-compare.js +1 -1
- package/src/cli/commands/build.js +1 -1
- package/src/cli/commands/cfg.js +3 -7
- package/src/cli/commands/check.js +12 -17
- package/src/cli/commands/children.js +3 -6
- package/src/cli/commands/co-change.js +5 -3
- package/src/cli/commands/communities.js +2 -6
- package/src/cli/commands/complexity.js +3 -2
- package/src/cli/commands/context.js +3 -7
- package/src/cli/commands/cycles.js +12 -8
- package/src/cli/commands/dataflow.js +3 -7
- package/src/cli/commands/deps.js +2 -6
- package/src/cli/commands/diff-impact.js +2 -6
- package/src/cli/commands/embed.js +1 -1
- package/src/cli/commands/export.js +34 -31
- package/src/cli/commands/exports.js +2 -6
- package/src/cli/commands/flow.js +3 -7
- package/src/cli/commands/fn-impact.js +3 -7
- package/src/cli/commands/impact.js +2 -6
- package/src/cli/commands/info.js +2 -2
- package/src/cli/commands/map.js +1 -1
- package/src/cli/commands/mcp.js +1 -1
- package/src/cli/commands/models.js +1 -1
- package/src/cli/commands/owners.js +1 -1
- package/src/cli/commands/path.js +2 -2
- package/src/cli/commands/plot.js +40 -31
- package/src/cli/commands/query.js +3 -7
- package/src/cli/commands/registry.js +2 -2
- package/src/cli/commands/roles.js +3 -7
- package/src/cli/commands/search.js +1 -1
- package/src/cli/commands/sequence.js +3 -7
- package/src/cli/commands/snapshot.js +6 -1
- package/src/cli/commands/stats.js +1 -1
- package/src/cli/commands/structure.js +5 -4
- package/src/cli/commands/triage.js +4 -4
- package/src/cli/commands/watch.js +1 -1
- package/src/cli/commands/where.js +2 -6
- package/src/cli/index.js +11 -5
- package/src/cli/shared/open-graph.js +13 -0
- package/src/cli/shared/options.js +22 -2
- package/src/cli.js +1 -1
- package/src/db/connection.js +127 -4
- package/src/{db.js → db/index.js} +12 -5
- package/src/db/migrations.js +1 -1
- package/src/db/query-builder.js +15 -8
- package/src/db/repository/base.js +1 -1
- package/src/db/repository/graph-read.js +3 -3
- package/src/db/repository/in-memory-repository.js +4 -13
- package/src/db/repository/nodes.js +3 -8
- package/src/{analysis → domain/analysis}/context.js +6 -6
- package/src/{analysis → domain/analysis}/dependencies.js +5 -5
- package/src/{analysis → domain/analysis}/exports.js +8 -4
- package/src/{analysis → domain/analysis}/impact.js +61 -58
- package/src/{analysis → domain/analysis}/module-map.js +3 -3
- package/src/{analysis → domain/analysis}/roles.js +4 -4
- package/src/{analysis → domain/analysis}/symbol-lookup.js +13 -7
- package/src/{builder → domain/graph/builder}/helpers.js +3 -3
- package/src/{builder → domain/graph/builder}/incremental.js +3 -3
- package/src/{builder → domain/graph/builder}/pipeline.js +4 -4
- package/src/{builder → domain/graph/builder}/stages/build-edges.js +2 -2
- package/src/{builder → domain/graph/builder}/stages/build-structure.js +4 -4
- package/src/{builder → domain/graph/builder}/stages/collect-files.js +2 -2
- package/src/{builder → domain/graph/builder}/stages/detect-changes.js +6 -6
- package/src/{builder → domain/graph/builder}/stages/finalize.js +4 -4
- package/src/{builder → domain/graph/builder}/stages/insert-nodes.js +1 -1
- package/src/{builder → domain/graph/builder}/stages/parse-files.js +2 -2
- package/src/{builder → domain/graph/builder}/stages/resolve-imports.js +1 -1
- package/src/{builder → domain/graph/builder}/stages/run-analyses.js +2 -2
- package/src/{change-journal.js → domain/graph/change-journal.js} +1 -1
- package/src/{cycles.js → domain/graph/cycles.js} +4 -4
- package/src/{journal.js → domain/graph/journal.js} +1 -1
- package/src/{resolve.js → domain/graph/resolve.js} +2 -2
- package/src/{watcher.js → domain/graph/watcher.js} +5 -5
- package/src/{parser.js → domain/parser.js} +5 -5
- package/src/{queries.js → domain/queries.js} +16 -16
- package/src/{embeddings → domain/search}/generator.js +3 -3
- package/src/{embeddings → domain/search}/models.js +2 -2
- package/src/{embeddings → domain/search}/search/cli-formatter.js +1 -1
- package/src/{embeddings → domain/search}/search/hybrid.js +1 -1
- package/src/{embeddings → domain/search}/search/keyword.js +1 -1
- package/src/{embeddings → domain/search}/search/prepare.js +2 -2
- package/src/{embeddings → domain/search}/search/semantic.js +1 -1
- package/src/{embeddings → domain/search}/strategies/structured.js +1 -1
- package/src/extractors/javascript.js +1 -1
- package/src/{ast.js → features/ast.js} +8 -8
- package/src/{audit.js → features/audit.js} +16 -44
- package/src/{batch.js → features/batch.js} +5 -5
- package/src/{boundaries.js → features/boundaries.js} +2 -2
- package/src/{branch-compare.js → features/branch-compare.js} +3 -3
- package/src/{cfg.js → features/cfg.js} +10 -10
- package/src/{check.js → features/check.js} +13 -30
- package/src/{cochange.js → features/cochange.js} +5 -5
- package/src/{communities.js → features/communities.js} +7 -7
- package/src/{complexity.js → features/complexity.js} +13 -13
- package/src/{dataflow.js → features/dataflow.js} +11 -11
- package/src/{export.js → features/export.js} +3 -3
- package/src/{flow.js → features/flow.js} +4 -4
- package/src/{viewer.js → features/graph-enrichment.js} +6 -6
- package/src/{manifesto.js → features/manifesto.js} +6 -6
- package/src/{owners.js → features/owners.js} +2 -2
- package/src/{sequence.js → features/sequence.js} +15 -15
- package/src/{snapshot.js → features/snapshot.js} +3 -3
- package/src/{structure.js → features/structure.js} +7 -7
- package/src/{triage.js → features/triage.js} +8 -8
- package/src/graph/builders/dependency.js +33 -14
- package/src/index.cjs +16 -0
- package/src/index.js +39 -39
- package/src/{native.js → infrastructure/native.js} +1 -1
- package/src/mcp/middleware.js +1 -1
- package/src/mcp/server.js +5 -5
- package/src/mcp/tool-registry.js +2 -2
- package/src/mcp/tools/ast-query.js +1 -1
- package/src/mcp/tools/audit.js +1 -1
- package/src/mcp/tools/batch-query.js +1 -1
- package/src/mcp/tools/branch-compare.js +3 -1
- package/src/mcp/tools/cfg.js +1 -1
- package/src/mcp/tools/check.js +3 -3
- package/src/mcp/tools/co-changes.js +1 -1
- package/src/mcp/tools/code-owners.js +1 -1
- package/src/mcp/tools/communities.js +1 -1
- package/src/mcp/tools/complexity.js +1 -1
- package/src/mcp/tools/dataflow.js +2 -2
- package/src/mcp/tools/execution-flow.js +2 -2
- package/src/mcp/tools/export-graph.js +2 -2
- package/src/mcp/tools/find-cycles.js +2 -2
- package/src/mcp/tools/list-repos.js +1 -1
- package/src/mcp/tools/sequence.js +1 -1
- package/src/mcp/tools/structure.js +1 -1
- package/src/mcp/tools/triage.js +2 -2
- package/src/{commands → presentation}/audit.js +2 -2
- package/src/{commands → presentation}/batch.js +1 -1
- package/src/{commands → presentation}/branch-compare.js +2 -2
- package/src/{commands → presentation}/cfg.js +1 -1
- package/src/{commands → presentation}/check.js +2 -2
- package/src/{commands → presentation}/communities.js +1 -1
- package/src/{commands → presentation}/complexity.js +1 -1
- package/src/{commands → presentation}/dataflow.js +1 -1
- package/src/{commands → presentation}/flow.js +2 -2
- package/src/{commands → presentation}/manifesto.js +1 -1
- package/src/{commands → presentation}/owners.js +1 -1
- package/src/presentation/queries-cli/exports.js +46 -0
- package/src/presentation/queries-cli/impact.js +198 -0
- package/src/presentation/queries-cli/index.js +5 -0
- package/src/presentation/queries-cli/inspect.js +334 -0
- package/src/presentation/queries-cli/overview.js +197 -0
- package/src/presentation/queries-cli/path.js +58 -0
- package/src/presentation/queries-cli.js +27 -0
- package/src/{commands → presentation}/query.js +1 -1
- package/src/presentation/result-formatter.js +126 -3
- package/src/{commands → presentation}/sequence.js +2 -2
- package/src/{commands → presentation}/structure.js +1 -1
- package/src/{commands → presentation}/triage.js +1 -1
- package/src/{constants.js → shared/constants.js} +1 -1
- package/src/shared/file-utils.js +2 -2
- package/src/shared/generators.js +2 -2
- package/src/shared/hierarchy.js +1 -1
- package/src/mcp.js +0 -2
- package/src/queries-cli.js +0 -866
- /package/src/{builder → domain/graph/builder}/context.js +0 -0
- /package/src/{builder.js → domain/graph/builder.js} +0 -0
- /package/src/{embeddings → domain/search}/index.js +0 -0
- /package/src/{embeddings → domain/search}/search/filters.js +0 -0
- /package/src/{embeddings → domain/search}/stores/fts5.js +0 -0
- /package/src/{embeddings → domain/search}/stores/sqlite-blob.js +0 -0
- /package/src/{embeddings → domain/search}/strategies/source.js +0 -0
- /package/src/{embeddings → domain/search}/strategies/text-utils.js +0 -0
- /package/src/{config.js → infrastructure/config.js} +0 -0
- /package/src/{logger.js → infrastructure/logger.js} +0 -0
- /package/src/{registry.js → infrastructure/registry.js} +0 -0
- /package/src/{update-check.js → infrastructure/update-check.js} +0 -0
- /package/src/{commands → presentation}/cochange.js +0 -0
- /package/src/{errors.js → shared/errors.js} +0 -0
- /package/src/{kinds.js → shared/kinds.js} +0 -0
- /package/src/{paginate.js → shared/paginate.js} +0 -0
|
@@ -11,9 +11,9 @@ import fs from 'node:fs';
|
|
|
11
11
|
import os from 'node:os';
|
|
12
12
|
import path from 'node:path';
|
|
13
13
|
import Database from 'better-sqlite3';
|
|
14
|
-
import { buildGraph } from '
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
14
|
+
import { buildGraph } from '../domain/graph/builder.js';
|
|
15
|
+
import { kindIcon } from '../domain/queries.js';
|
|
16
|
+
import { isTestFile } from '../infrastructure/test-filter.js';
|
|
17
17
|
|
|
18
18
|
// ─── Git Helpers ────────────────────────────────────────────────────────
|
|
19
19
|
|
|
@@ -7,14 +7,14 @@
|
|
|
7
7
|
|
|
8
8
|
import fs from 'node:fs';
|
|
9
9
|
import path from 'node:path';
|
|
10
|
-
import { CFG_RULES } from '
|
|
10
|
+
import { CFG_RULES } from '../ast-analysis/rules/index.js';
|
|
11
11
|
import {
|
|
12
12
|
makeCfgRules as _makeCfgRules,
|
|
13
13
|
buildExtensionSet,
|
|
14
14
|
buildExtToLangMap,
|
|
15
|
-
} from '
|
|
16
|
-
import { walkWithVisitors } from '
|
|
17
|
-
import { createCfgVisitor } from '
|
|
15
|
+
} from '../ast-analysis/shared.js';
|
|
16
|
+
import { walkWithVisitors } from '../ast-analysis/visitor.js';
|
|
17
|
+
import { createCfgVisitor } from '../ast-analysis/visitors/cfg-visitor.js';
|
|
18
18
|
import {
|
|
19
19
|
deleteCfgForNode,
|
|
20
20
|
getCfgBlocks,
|
|
@@ -22,10 +22,10 @@ import {
|
|
|
22
22
|
getFunctionNodeId,
|
|
23
23
|
hasCfgTables,
|
|
24
24
|
openReadonlyOrFail,
|
|
25
|
-
} from '
|
|
26
|
-
import {
|
|
27
|
-
import {
|
|
28
|
-
import { paginateResult } from '
|
|
25
|
+
} from '../db/index.js';
|
|
26
|
+
import { info } from '../infrastructure/logger.js';
|
|
27
|
+
import { isTestFile } from '../infrastructure/test-filter.js';
|
|
28
|
+
import { paginateResult } from '../shared/paginate.js';
|
|
29
29
|
|
|
30
30
|
// Re-export for backward compatibility
|
|
31
31
|
export { _makeCfgRules as makeCfgRules, CFG_RULES };
|
|
@@ -104,13 +104,13 @@ export async function buildCFGData(db, fileSymbols, rootDir, _engineOpts) {
|
|
|
104
104
|
}
|
|
105
105
|
|
|
106
106
|
if (needsFallback) {
|
|
107
|
-
const { createParsers } = await import('
|
|
107
|
+
const { createParsers } = await import('../domain/parser.js');
|
|
108
108
|
parsers = await createParsers();
|
|
109
109
|
}
|
|
110
110
|
|
|
111
111
|
let getParserFn = null;
|
|
112
112
|
if (parsers) {
|
|
113
|
-
const mod = await import('
|
|
113
|
+
const mod = await import('../domain/parser.js');
|
|
114
114
|
getParserFn = mod.getParser;
|
|
115
115
|
}
|
|
116
116
|
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { execFileSync } from 'node:child_process';
|
|
2
2
|
import fs from 'node:fs';
|
|
3
3
|
import path from 'node:path';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
4
|
+
import { findDbPath, openReadonlyOrFail } from '../db/index.js';
|
|
5
|
+
import { bfsTransitiveCallers } from '../domain/analysis/impact.js';
|
|
6
|
+
import { findCycles } from '../domain/graph/cycles.js';
|
|
7
|
+
import { loadConfig } from '../infrastructure/config.js';
|
|
8
|
+
import { isTestFile } from '../infrastructure/test-filter.js';
|
|
8
9
|
import { matchOwners, parseCodeowners } from './owners.js';
|
|
9
10
|
|
|
10
11
|
// ─── Diff Parser ──────────────────────────────────────────────────────
|
|
@@ -96,31 +97,10 @@ export function checkMaxBlastRadius(db, changedRanges, threshold, noTests, maxDe
|
|
|
96
97
|
}
|
|
97
98
|
if (!overlaps) continue;
|
|
98
99
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
for (let d = 1; d <= maxDepth; d++) {
|
|
104
|
-
const nextFrontier = [];
|
|
105
|
-
for (const fid of frontier) {
|
|
106
|
-
const callers = db
|
|
107
|
-
.prepare(
|
|
108
|
-
`SELECT DISTINCT n.id, n.name, n.kind, n.file, n.line
|
|
109
|
-
FROM edges e JOIN nodes n ON e.source_id = n.id
|
|
110
|
-
WHERE e.target_id = ? AND e.kind = 'calls'`,
|
|
111
|
-
)
|
|
112
|
-
.all(fid);
|
|
113
|
-
for (const c of callers) {
|
|
114
|
-
if (!visited.has(c.id) && (!noTests || !isTestFile(c.file))) {
|
|
115
|
-
visited.add(c.id);
|
|
116
|
-
nextFrontier.push(c.id);
|
|
117
|
-
totalCallers++;
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
frontier = nextFrontier;
|
|
122
|
-
if (frontier.length === 0) break;
|
|
123
|
-
}
|
|
100
|
+
const { totalDependents: totalCallers } = bfsTransitiveCallers(db, def.id, {
|
|
101
|
+
noTests,
|
|
102
|
+
maxDepth,
|
|
103
|
+
});
|
|
124
104
|
|
|
125
105
|
if (totalCallers > maxFound) maxFound = totalCallers;
|
|
126
106
|
if (totalCallers > threshold) {
|
|
@@ -240,7 +220,10 @@ export function checkData(customDbPath, opts = {}) {
|
|
|
240
220
|
const maxDepth = opts.depth || 3;
|
|
241
221
|
|
|
242
222
|
// Load config defaults for check predicates
|
|
243
|
-
|
|
223
|
+
// NOTE: opts.config is loaded from process.cwd() at startup (via CLI context),
|
|
224
|
+
// which may differ from the DB's parent repo root when --db points to an external
|
|
225
|
+
// project. This is an acceptable trade-off to avoid duplicate I/O on the hot path.
|
|
226
|
+
const config = opts.config || loadConfig(repoRoot);
|
|
244
227
|
const checkConfig = config.check || {};
|
|
245
228
|
|
|
246
229
|
// Resolve which predicates are enabled: CLI flags ?? config ?? built-in defaults
|
|
@@ -8,11 +8,11 @@
|
|
|
8
8
|
import { execFileSync } from 'node:child_process';
|
|
9
9
|
import fs from 'node:fs';
|
|
10
10
|
import path from 'node:path';
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import { isTestFile } from '
|
|
14
|
-
import {
|
|
15
|
-
import { paginateResult } from '
|
|
11
|
+
import { closeDb, findDbPath, initSchema, openDb, openReadonlyOrFail } from '../db/index.js';
|
|
12
|
+
import { warn } from '../infrastructure/logger.js';
|
|
13
|
+
import { isTestFile } from '../infrastructure/test-filter.js';
|
|
14
|
+
import { normalizePath } from '../shared/constants.js';
|
|
15
|
+
import { paginateResult } from '../shared/paginate.js';
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* Scan git history and return parsed commit data.
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
|
-
import {
|
|
3
|
-
import { louvainCommunities } from '
|
|
4
|
-
import { buildDependencyGraph } from '
|
|
5
|
-
import { paginateResult } from '
|
|
2
|
+
import { openRepo } from '../db/index.js';
|
|
3
|
+
import { louvainCommunities } from '../graph/algorithms/louvain.js';
|
|
4
|
+
import { buildDependencyGraph } from '../graph/builders/dependency.js';
|
|
5
|
+
import { paginateResult } from '../shared/paginate.js';
|
|
6
6
|
|
|
7
7
|
// ─── Directory Helpers ────────────────────────────────────────────────
|
|
8
8
|
|
|
@@ -26,15 +26,15 @@ function getDirectory(filePath) {
|
|
|
26
26
|
* @returns {{ communities: object[], modularity: number, drift: object, summary: object }}
|
|
27
27
|
*/
|
|
28
28
|
export function communitiesData(customDbPath, opts = {}) {
|
|
29
|
-
const
|
|
29
|
+
const { repo, close } = openRepo(customDbPath, opts);
|
|
30
30
|
let graph;
|
|
31
31
|
try {
|
|
32
|
-
graph = buildDependencyGraph(
|
|
32
|
+
graph = buildDependencyGraph(repo, {
|
|
33
33
|
fileLevel: !opts.functions,
|
|
34
34
|
noTests: opts.noTests,
|
|
35
35
|
});
|
|
36
36
|
} finally {
|
|
37
|
-
|
|
37
|
+
close();
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
// Handle empty or trivial graphs
|
|
@@ -3,20 +3,20 @@ import path from 'node:path';
|
|
|
3
3
|
import {
|
|
4
4
|
computeLOCMetrics as _computeLOCMetrics,
|
|
5
5
|
computeMaintainabilityIndex as _computeMaintainabilityIndex,
|
|
6
|
-
} from '
|
|
7
|
-
import { COMPLEXITY_RULES, HALSTEAD_RULES } from '
|
|
6
|
+
} from '../ast-analysis/metrics.js';
|
|
7
|
+
import { COMPLEXITY_RULES, HALSTEAD_RULES } from '../ast-analysis/rules/index.js';
|
|
8
8
|
import {
|
|
9
9
|
findFunctionNode as _findFunctionNode,
|
|
10
10
|
buildExtensionSet,
|
|
11
11
|
buildExtToLangMap,
|
|
12
|
-
} from '
|
|
13
|
-
import { walkWithVisitors } from '
|
|
14
|
-
import { createComplexityVisitor } from '
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import { paginateResult } from '
|
|
12
|
+
} from '../ast-analysis/shared.js';
|
|
13
|
+
import { walkWithVisitors } from '../ast-analysis/visitor.js';
|
|
14
|
+
import { createComplexityVisitor } from '../ast-analysis/visitors/complexity-visitor.js';
|
|
15
|
+
import { getFunctionNodeId, openReadonlyOrFail } from '../db/index.js';
|
|
16
|
+
import { loadConfig } from '../infrastructure/config.js';
|
|
17
|
+
import { info } from '../infrastructure/logger.js';
|
|
18
|
+
import { isTestFile } from '../infrastructure/test-filter.js';
|
|
19
|
+
import { paginateResult } from '../shared/paginate.js';
|
|
20
20
|
|
|
21
21
|
// Re-export rules for backward compatibility
|
|
22
22
|
export { COMPLEXITY_RULES, HALSTEAD_RULES };
|
|
@@ -360,12 +360,12 @@ export async function buildComplexityMetrics(db, fileSymbols, rootDir, _engineOp
|
|
|
360
360
|
}
|
|
361
361
|
}
|
|
362
362
|
if (needsFallback) {
|
|
363
|
-
const { createParsers } = await import('
|
|
363
|
+
const { createParsers } = await import('../domain/parser.js');
|
|
364
364
|
parsers = await createParsers();
|
|
365
365
|
extToLang = buildExtToLangMap();
|
|
366
366
|
}
|
|
367
367
|
|
|
368
|
-
const { getParser } = await import('
|
|
368
|
+
const { getParser } = await import('../domain/parser.js');
|
|
369
369
|
|
|
370
370
|
const upsert = db.prepare(
|
|
371
371
|
`INSERT OR REPLACE INTO function_complexity
|
|
@@ -524,7 +524,7 @@ export function complexityData(customDbPath, opts = {}) {
|
|
|
524
524
|
const kindFilter = opts.kind || null;
|
|
525
525
|
|
|
526
526
|
// Load thresholds from config
|
|
527
|
-
const config = loadConfig(process.cwd());
|
|
527
|
+
const config = opts.config || loadConfig(process.cwd());
|
|
528
528
|
const thresholds = config.manifesto?.rules || {
|
|
529
529
|
cognitive: { warn: 15, fail: null },
|
|
530
530
|
cyclomatic: { warn: 10, fail: null },
|
|
@@ -11,19 +11,19 @@
|
|
|
11
11
|
|
|
12
12
|
import fs from 'node:fs';
|
|
13
13
|
import path from 'node:path';
|
|
14
|
-
import { DATAFLOW_RULES } from '
|
|
14
|
+
import { DATAFLOW_RULES } from '../ast-analysis/rules/index.js';
|
|
15
15
|
import {
|
|
16
16
|
makeDataflowRules as _makeDataflowRules,
|
|
17
17
|
buildExtensionSet,
|
|
18
18
|
buildExtToLangMap,
|
|
19
|
-
} from '
|
|
20
|
-
import { walkWithVisitors } from '
|
|
21
|
-
import { createDataflowVisitor } from '
|
|
22
|
-
import { hasDataflowTable, openReadonlyOrFail } from '
|
|
23
|
-
import {
|
|
24
|
-
import { info } from '
|
|
25
|
-
import {
|
|
26
|
-
import {
|
|
19
|
+
} from '../ast-analysis/shared.js';
|
|
20
|
+
import { walkWithVisitors } from '../ast-analysis/visitor.js';
|
|
21
|
+
import { createDataflowVisitor } from '../ast-analysis/visitors/dataflow-visitor.js';
|
|
22
|
+
import { hasDataflowTable, openReadonlyOrFail } from '../db/index.js';
|
|
23
|
+
import { ALL_SYMBOL_KINDS, normalizeSymbol } from '../domain/queries.js';
|
|
24
|
+
import { info } from '../infrastructure/logger.js';
|
|
25
|
+
import { isTestFile } from '../infrastructure/test-filter.js';
|
|
26
|
+
import { paginateResult } from '../shared/paginate.js';
|
|
27
27
|
|
|
28
28
|
// Re-export for backward compatibility
|
|
29
29
|
export { _makeDataflowRules as makeDataflowRules, DATAFLOW_RULES };
|
|
@@ -88,13 +88,13 @@ export async function buildDataflowEdges(db, fileSymbols, rootDir, _engineOpts)
|
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
if (needsFallback) {
|
|
91
|
-
const { createParsers } = await import('
|
|
91
|
+
const { createParsers } = await import('../domain/parser.js');
|
|
92
92
|
parsers = await createParsers();
|
|
93
93
|
}
|
|
94
94
|
|
|
95
95
|
let getParserFn = null;
|
|
96
96
|
if (parsers) {
|
|
97
|
-
const mod = await import('
|
|
97
|
+
const mod = await import('../domain/parser.js');
|
|
98
98
|
getParserFn = mod.getParser;
|
|
99
99
|
}
|
|
100
100
|
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
|
-
import { isTestFile } from '
|
|
3
|
-
import { paginateResult } from './paginate.js';
|
|
2
|
+
import { isTestFile } from '../infrastructure/test-filter.js';
|
|
4
3
|
import {
|
|
5
4
|
renderFileLevelDOT,
|
|
6
5
|
renderFileLevelGraphML,
|
|
@@ -10,7 +9,8 @@ import {
|
|
|
10
9
|
renderFunctionLevelGraphML,
|
|
11
10
|
renderFunctionLevelMermaid,
|
|
12
11
|
renderFunctionLevelNeo4jCSV,
|
|
13
|
-
} from '
|
|
12
|
+
} from '../presentation/export.js';
|
|
13
|
+
import { paginateResult } from '../shared/paginate.js';
|
|
14
14
|
|
|
15
15
|
const DEFAULT_MIN_CONFIDENCE = 0.5;
|
|
16
16
|
|
|
@@ -5,10 +5,10 @@
|
|
|
5
5
|
* framework entry points (routes, commands, events) through their call chains.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { openReadonlyOrFail } from '
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
8
|
+
import { openReadonlyOrFail } from '../db/index.js';
|
|
9
|
+
import { CORE_SYMBOL_KINDS, findMatchingNodes } from '../domain/queries.js';
|
|
10
|
+
import { isTestFile } from '../infrastructure/test-filter.js';
|
|
11
|
+
import { paginateResult } from '../shared/paginate.js';
|
|
12
12
|
import { FRAMEWORK_ENTRY_PREFIXES } from './structure.js';
|
|
13
13
|
|
|
14
14
|
/**
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
|
-
import { louvainCommunities } from '
|
|
3
|
-
import { CodeGraph } from '
|
|
4
|
-
import { isTestFile } from '
|
|
2
|
+
import { louvainCommunities } from '../graph/algorithms/louvain.js';
|
|
3
|
+
import { CodeGraph } from '../graph/model.js';
|
|
4
|
+
import { isTestFile } from '../infrastructure/test-filter.js';
|
|
5
5
|
import {
|
|
6
6
|
COMMUNITY_COLORS,
|
|
7
7
|
DEFAULT_NODE_COLORS,
|
|
8
8
|
DEFAULT_ROLE_COLORS,
|
|
9
|
-
} from '
|
|
10
|
-
import { DEFAULT_CONFIG, renderPlotHTML } from '
|
|
9
|
+
} from '../presentation/colors.js';
|
|
10
|
+
import { DEFAULT_CONFIG, renderPlotHTML } from '../presentation/viewer.js';
|
|
11
11
|
|
|
12
12
|
// Re-export presentation utilities for backward compatibility
|
|
13
|
-
export { loadPlotConfig } from '
|
|
13
|
+
export { loadPlotConfig } from '../presentation/viewer.js';
|
|
14
14
|
|
|
15
15
|
const DEFAULT_MIN_CONFIDENCE = 0.5;
|
|
16
16
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
+
import { openReadonlyOrFail } from '../db/index.js';
|
|
2
|
+
import { findCycles } from '../domain/graph/cycles.js';
|
|
3
|
+
import { loadConfig } from '../infrastructure/config.js';
|
|
4
|
+
import { debug } from '../infrastructure/logger.js';
|
|
5
|
+
import { paginateResult } from '../shared/paginate.js';
|
|
1
6
|
import { evaluateBoundaries } from './boundaries.js';
|
|
2
|
-
import { loadConfig } from './config.js';
|
|
3
|
-
import { findCycles } from './cycles.js';
|
|
4
|
-
import { openReadonlyOrFail } from './db.js';
|
|
5
|
-
import { debug } from './logger.js';
|
|
6
|
-
import { paginateResult } from './paginate.js';
|
|
7
7
|
|
|
8
8
|
// ─── Rule Definitions ─────────────────────────────────────────────────
|
|
9
9
|
|
|
@@ -395,7 +395,7 @@ export function manifestoData(customDbPath, opts = {}) {
|
|
|
395
395
|
const db = openReadonlyOrFail(customDbPath);
|
|
396
396
|
|
|
397
397
|
try {
|
|
398
|
-
const config = loadConfig(process.cwd());
|
|
398
|
+
const config = opts.config || loadConfig(process.cwd());
|
|
399
399
|
const rules = resolveRules(config.manifesto?.rules);
|
|
400
400
|
|
|
401
401
|
const violations = [];
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import { findDbPath, openReadonlyOrFail } from '
|
|
4
|
-
import { isTestFile } from '
|
|
3
|
+
import { findDbPath, openReadonlyOrFail } from '../db/index.js';
|
|
4
|
+
import { isTestFile } from '../infrastructure/test-filter.js';
|
|
5
5
|
|
|
6
6
|
// ─── CODEOWNERS Parsing ──────────────────────────────────────────────
|
|
7
7
|
|
|
@@ -6,10 +6,11 @@
|
|
|
6
6
|
* sequence-diagram conventions.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
9
|
+
import { openRepo } from '../db/index.js';
|
|
10
|
+
import { SqliteRepository } from '../db/repository/sqlite-repository.js';
|
|
11
|
+
import { findMatchingNodes } from '../domain/queries.js';
|
|
12
|
+
import { isTestFile } from '../infrastructure/test-filter.js';
|
|
13
|
+
import { paginateResult } from '../shared/paginate.js';
|
|
13
14
|
import { FRAMEWORK_ENTRY_PREFIXES } from './structure.js';
|
|
14
15
|
|
|
15
16
|
// ─── Alias generation ────────────────────────────────────────────────
|
|
@@ -85,19 +86,19 @@ function buildAliases(files) {
|
|
|
85
86
|
* @returns {{ entry, participants, messages, depth, totalMessages, truncated }}
|
|
86
87
|
*/
|
|
87
88
|
export function sequenceData(name, dbPath, opts = {}) {
|
|
88
|
-
const
|
|
89
|
+
const { repo, close } = openRepo(dbPath, opts);
|
|
89
90
|
try {
|
|
90
91
|
const maxDepth = opts.depth || 10;
|
|
91
92
|
const noTests = opts.noTests || false;
|
|
92
93
|
const withDataflow = opts.dataflow || false;
|
|
93
94
|
|
|
94
95
|
// Phase 1: Direct LIKE match
|
|
95
|
-
let matchNode = findMatchingNodes(
|
|
96
|
+
let matchNode = findMatchingNodes(repo, name, opts)[0] ?? null;
|
|
96
97
|
|
|
97
98
|
// Phase 2: Prefix-stripped matching
|
|
98
99
|
if (!matchNode) {
|
|
99
100
|
for (const prefix of FRAMEWORK_ENTRY_PREFIXES) {
|
|
100
|
-
matchNode = findMatchingNodes(
|
|
101
|
+
matchNode = findMatchingNodes(repo, `${prefix}${name}`, opts)[0] ?? null;
|
|
101
102
|
if (matchNode) break;
|
|
102
103
|
}
|
|
103
104
|
}
|
|
@@ -133,7 +134,7 @@ export function sequenceData(name, dbPath, opts = {}) {
|
|
|
133
134
|
const nextFrontier = [];
|
|
134
135
|
|
|
135
136
|
for (const fid of frontier) {
|
|
136
|
-
const callees = findCallees(
|
|
137
|
+
const callees = repo.findCallees(fid);
|
|
137
138
|
|
|
138
139
|
const caller = idToNode.get(fid);
|
|
139
140
|
|
|
@@ -163,18 +164,17 @@ export function sequenceData(name, dbPath, opts = {}) {
|
|
|
163
164
|
|
|
164
165
|
if (d === maxDepth && frontier.length > 0) {
|
|
165
166
|
// Only mark truncated if at least one frontier node has further callees
|
|
166
|
-
const hasMoreCalls = frontier.some((fid) => findCallees(
|
|
167
|
+
const hasMoreCalls = frontier.some((fid) => repo.findCallees(fid).length > 0);
|
|
167
168
|
if (hasMoreCalls) truncated = true;
|
|
168
169
|
}
|
|
169
170
|
}
|
|
170
171
|
|
|
171
172
|
// Dataflow annotations: add return arrows
|
|
172
173
|
if (withDataflow && messages.length > 0) {
|
|
173
|
-
const hasTable =
|
|
174
|
-
.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='dataflow'")
|
|
175
|
-
.get();
|
|
174
|
+
const hasTable = repo.hasDataflowTable();
|
|
176
175
|
|
|
177
|
-
if (hasTable) {
|
|
176
|
+
if (hasTable && repo instanceof SqliteRepository) {
|
|
177
|
+
const db = repo.db;
|
|
178
178
|
// Build name|file lookup for O(1) target node access
|
|
179
179
|
const nodeByNameFile = new Map();
|
|
180
180
|
for (const n of idToNode.values()) {
|
|
@@ -281,9 +281,9 @@ export function sequenceData(name, dbPath, opts = {}) {
|
|
|
281
281
|
}
|
|
282
282
|
return result;
|
|
283
283
|
} finally {
|
|
284
|
-
|
|
284
|
+
close();
|
|
285
285
|
}
|
|
286
286
|
}
|
|
287
287
|
|
|
288
288
|
// Re-export Mermaid renderer from presentation layer
|
|
289
|
-
export { sequenceToMermaid } from '
|
|
289
|
+
export { sequenceToMermaid } from '../presentation/sequence-renderer.js';
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import Database from 'better-sqlite3';
|
|
4
|
-
import { findDbPath } from '
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
4
|
+
import { findDbPath } from '../db/index.js';
|
|
5
|
+
import { debug } from '../infrastructure/logger.js';
|
|
6
|
+
import { ConfigError, DbError } from '../shared/errors.js';
|
|
7
7
|
|
|
8
8
|
const NAME_RE = /^[a-zA-Z0-9_-]+$/;
|
|
9
9
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { isTestFile } from '
|
|
5
|
-
import {
|
|
6
|
-
import { paginateResult } from '
|
|
2
|
+
import { getNodeId, openReadonlyOrFail, testFilterSQL } from '../db/index.js';
|
|
3
|
+
import { debug } from '../infrastructure/logger.js';
|
|
4
|
+
import { isTestFile } from '../infrastructure/test-filter.js';
|
|
5
|
+
import { normalizePath } from '../shared/constants.js';
|
|
6
|
+
import { paginateResult } from '../shared/paginate.js';
|
|
7
7
|
|
|
8
8
|
// ─── Build-time: insert directory nodes, contains edges, and metrics ────
|
|
9
9
|
|
|
@@ -313,9 +313,9 @@ export function buildStructure(db, fileSymbols, _rootDir, lineCountMap, director
|
|
|
313
313
|
// ─── Node role classification ─────────────────────────────────────────
|
|
314
314
|
|
|
315
315
|
// Re-export from classifier for backward compatibility
|
|
316
|
-
export { FRAMEWORK_ENTRY_PREFIXES } from '
|
|
316
|
+
export { FRAMEWORK_ENTRY_PREFIXES } from '../graph/classifiers/roles.js';
|
|
317
317
|
|
|
318
|
-
import { classifyRoles } from '
|
|
318
|
+
import { classifyRoles } from '../graph/classifiers/roles.js';
|
|
319
319
|
|
|
320
320
|
export function classifyNodeRoles(db) {
|
|
321
321
|
const rows = db
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { DEFAULT_WEIGHTS, scoreRisk } from '
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { paginateResult } from '
|
|
1
|
+
import { openRepo } from '../db/index.js';
|
|
2
|
+
import { DEFAULT_WEIGHTS, scoreRisk } from '../graph/classifiers/risk.js';
|
|
3
|
+
import { warn } from '../infrastructure/logger.js';
|
|
4
|
+
import { isTestFile } from '../infrastructure/test-filter.js';
|
|
5
|
+
import { paginateResult } from '../shared/paginate.js';
|
|
6
6
|
|
|
7
7
|
// ─── Data Function ────────────────────────────────────────────────────
|
|
8
8
|
|
|
@@ -14,7 +14,7 @@ import { paginateResult } from './paginate.js';
|
|
|
14
14
|
* @returns {{ items: object[], summary: object, _pagination?: object }}
|
|
15
15
|
*/
|
|
16
16
|
export function triageData(customDbPath, opts = {}) {
|
|
17
|
-
const
|
|
17
|
+
const { repo, close } = openRepo(customDbPath, opts);
|
|
18
18
|
try {
|
|
19
19
|
const noTests = opts.noTests || false;
|
|
20
20
|
const fileFilter = opts.file || null;
|
|
@@ -26,7 +26,7 @@ export function triageData(customDbPath, opts = {}) {
|
|
|
26
26
|
|
|
27
27
|
let rows;
|
|
28
28
|
try {
|
|
29
|
-
rows = findNodesForTriage(
|
|
29
|
+
rows = repo.findNodesForTriage({
|
|
30
30
|
noTests,
|
|
31
31
|
file: fileFilter,
|
|
32
32
|
kind: kindFilter,
|
|
@@ -115,7 +115,7 @@ export function triageData(customDbPath, opts = {}) {
|
|
|
115
115
|
offset: opts.offset,
|
|
116
116
|
});
|
|
117
117
|
} finally {
|
|
118
|
-
|
|
118
|
+
close();
|
|
119
119
|
}
|
|
120
120
|
}
|
|
121
121
|
|
|
@@ -3,32 +3,39 @@
|
|
|
3
3
|
* Replaces inline graph construction in cycles.js, communities.js, viewer.js, export.js.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
getCallableNodes,
|
|
8
|
+
getCallEdges,
|
|
9
|
+
getFileNodesAll,
|
|
10
|
+
getImportEdges,
|
|
11
|
+
Repository,
|
|
12
|
+
} from '../../db/index.js';
|
|
7
13
|
import { isTestFile } from '../../infrastructure/test-filter.js';
|
|
8
14
|
import { CodeGraph } from '../model.js';
|
|
9
15
|
|
|
10
16
|
/**
|
|
11
|
-
* @param {object}
|
|
17
|
+
* @param {object} dbOrRepo - Open better-sqlite3 database (readonly) or a Repository instance
|
|
12
18
|
* @param {object} [opts]
|
|
13
19
|
* @param {boolean} [opts.fileLevel=true] - File-level (imports) or function-level (calls)
|
|
14
20
|
* @param {boolean} [opts.noTests=false] - Exclude test files
|
|
15
21
|
* @param {number} [opts.minConfidence] - Minimum edge confidence (function-level only)
|
|
16
22
|
* @returns {CodeGraph}
|
|
17
23
|
*/
|
|
18
|
-
export function buildDependencyGraph(
|
|
24
|
+
export function buildDependencyGraph(dbOrRepo, opts = {}) {
|
|
19
25
|
const fileLevel = opts.fileLevel !== false;
|
|
20
26
|
const noTests = opts.noTests || false;
|
|
21
27
|
|
|
22
28
|
if (fileLevel) {
|
|
23
|
-
return buildFileLevelGraph(
|
|
29
|
+
return buildFileLevelGraph(dbOrRepo, noTests);
|
|
24
30
|
}
|
|
25
|
-
return buildFunctionLevelGraph(
|
|
31
|
+
return buildFunctionLevelGraph(dbOrRepo, noTests, opts.minConfidence);
|
|
26
32
|
}
|
|
27
33
|
|
|
28
|
-
function buildFileLevelGraph(
|
|
34
|
+
function buildFileLevelGraph(dbOrRepo, noTests) {
|
|
29
35
|
const graph = new CodeGraph();
|
|
36
|
+
const isRepo = dbOrRepo instanceof Repository;
|
|
30
37
|
|
|
31
|
-
let nodes = getFileNodesAll(
|
|
38
|
+
let nodes = isRepo ? dbOrRepo.getFileNodesAll() : getFileNodesAll(dbOrRepo);
|
|
32
39
|
if (noTests) nodes = nodes.filter((n) => !isTestFile(n.file));
|
|
33
40
|
|
|
34
41
|
const nodeIds = new Set();
|
|
@@ -37,7 +44,7 @@ function buildFileLevelGraph(db, noTests) {
|
|
|
37
44
|
nodeIds.add(n.id);
|
|
38
45
|
}
|
|
39
46
|
|
|
40
|
-
const edges = getImportEdges(
|
|
47
|
+
const edges = isRepo ? dbOrRepo.getImportEdges() : getImportEdges(dbOrRepo);
|
|
41
48
|
for (const e of edges) {
|
|
42
49
|
if (!nodeIds.has(e.source_id) || !nodeIds.has(e.target_id)) continue;
|
|
43
50
|
const src = String(e.source_id);
|
|
@@ -51,10 +58,11 @@ function buildFileLevelGraph(db, noTests) {
|
|
|
51
58
|
return graph;
|
|
52
59
|
}
|
|
53
60
|
|
|
54
|
-
function buildFunctionLevelGraph(
|
|
61
|
+
function buildFunctionLevelGraph(dbOrRepo, noTests, minConfidence) {
|
|
55
62
|
const graph = new CodeGraph();
|
|
63
|
+
const isRepo = dbOrRepo instanceof Repository;
|
|
56
64
|
|
|
57
|
-
let nodes = getCallableNodes(
|
|
65
|
+
let nodes = isRepo ? dbOrRepo.getCallableNodes() : getCallableNodes(dbOrRepo);
|
|
58
66
|
if (noTests) nodes = nodes.filter((n) => !isTestFile(n.file));
|
|
59
67
|
|
|
60
68
|
const nodeIds = new Set();
|
|
@@ -70,11 +78,22 @@ function buildFunctionLevelGraph(db, noTests, minConfidence) {
|
|
|
70
78
|
|
|
71
79
|
let edges;
|
|
72
80
|
if (minConfidence != null) {
|
|
73
|
-
|
|
74
|
-
.
|
|
75
|
-
.all
|
|
81
|
+
if (isRepo) {
|
|
82
|
+
// Trade-off: Repository.getCallEdges() returns all call edges, so we
|
|
83
|
+
// filter in JS. This is O(all call edges) rather than the SQL path's
|
|
84
|
+
// indexed WHERE clause. Acceptable for current data sizes; a dedicated
|
|
85
|
+
// getCallEdgesByMinConfidence(threshold) method on the Repository
|
|
86
|
+
// interface would be the proper fix if this becomes a bottleneck.
|
|
87
|
+
edges = dbOrRepo
|
|
88
|
+
.getCallEdges()
|
|
89
|
+
.filter((e) => e.confidence != null && e.confidence >= minConfidence);
|
|
90
|
+
} else {
|
|
91
|
+
edges = dbOrRepo
|
|
92
|
+
.prepare("SELECT source_id, target_id FROM edges WHERE kind = 'calls' AND confidence >= ?")
|
|
93
|
+
.all(minConfidence);
|
|
94
|
+
}
|
|
76
95
|
} else {
|
|
77
|
-
edges = getCallEdges(
|
|
96
|
+
edges = isRepo ? dbOrRepo.getCallEdges() : getCallEdges(dbOrRepo);
|
|
78
97
|
}
|
|
79
98
|
|
|
80
99
|
for (const e of edges) {
|