@optave/codegraph 3.4.1 → 3.6.0
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 +50 -28
- package/dist/ast-analysis/engine.d.ts.map +1 -1
- package/dist/ast-analysis/engine.js +119 -127
- package/dist/ast-analysis/engine.js.map +1 -1
- package/dist/ast-analysis/rules/javascript.d.ts.map +1 -1
- package/dist/ast-analysis/rules/javascript.js +1 -0
- package/dist/ast-analysis/rules/javascript.js.map +1 -1
- package/dist/ast-analysis/visitors/ast-store-visitor.d.ts.map +1 -1
- package/dist/ast-analysis/visitors/ast-store-visitor.js +116 -35
- package/dist/ast-analysis/visitors/ast-store-visitor.js.map +1 -1
- package/dist/ast-analysis/visitors/complexity-visitor.d.ts.map +1 -1
- package/dist/ast-analysis/visitors/complexity-visitor.js +11 -13
- package/dist/ast-analysis/visitors/complexity-visitor.js.map +1 -1
- package/dist/db/better-sqlite3.d.ts +3 -0
- package/dist/db/better-sqlite3.d.ts.map +1 -0
- package/dist/db/better-sqlite3.js +19 -0
- package/dist/db/better-sqlite3.js.map +1 -0
- package/dist/db/connection.d.ts +25 -4
- package/dist/db/connection.d.ts.map +1 -1
- package/dist/db/connection.js +125 -23
- package/dist/db/connection.js.map +1 -1
- package/dist/db/index.d.ts +2 -2
- package/dist/db/index.d.ts.map +1 -1
- package/dist/db/index.js +1 -1
- package/dist/db/index.js.map +1 -1
- package/dist/db/migrations.d.ts.map +1 -1
- package/dist/db/migrations.js +40 -32
- package/dist/db/migrations.js.map +1 -1
- package/dist/db/query-builder.d.ts +5 -5
- package/dist/db/query-builder.d.ts.map +1 -1
- package/dist/db/query-builder.js +20 -4
- package/dist/db/query-builder.js.map +1 -1
- package/dist/db/repository/index.d.ts +1 -0
- package/dist/db/repository/index.d.ts.map +1 -1
- package/dist/db/repository/index.js +1 -0
- package/dist/db/repository/index.js.map +1 -1
- package/dist/db/repository/native-repository.d.ts +58 -0
- package/dist/db/repository/native-repository.d.ts.map +1 -0
- package/dist/db/repository/native-repository.js +261 -0
- package/dist/db/repository/native-repository.js.map +1 -0
- package/dist/db/repository/nodes.d.ts +4 -4
- package/dist/db/repository/nodes.d.ts.map +1 -1
- package/dist/db/repository/nodes.js +6 -6
- package/dist/db/repository/nodes.js.map +1 -1
- package/dist/domain/analysis/context.d.ts.map +1 -1
- package/dist/domain/analysis/context.js +51 -66
- package/dist/domain/analysis/context.js.map +1 -1
- package/dist/domain/analysis/dependencies.d.ts.map +1 -1
- package/dist/domain/analysis/dependencies.js +62 -70
- package/dist/domain/analysis/dependencies.js.map +1 -1
- package/dist/domain/analysis/diff-impact.d.ts +9 -7
- package/dist/domain/analysis/diff-impact.d.ts.map +1 -1
- package/dist/domain/analysis/exports.d.ts.map +1 -1
- package/dist/domain/analysis/exports.js +29 -33
- package/dist/domain/analysis/exports.js.map +1 -1
- package/dist/domain/analysis/fn-impact.d.ts +15 -17
- package/dist/domain/analysis/fn-impact.d.ts.map +1 -1
- package/dist/domain/analysis/fn-impact.js +35 -65
- package/dist/domain/analysis/fn-impact.js.map +1 -1
- package/dist/domain/analysis/module-map.d.ts.map +1 -1
- package/dist/domain/analysis/module-map.js +91 -6
- package/dist/domain/analysis/module-map.js.map +1 -1
- package/dist/domain/analysis/query-helpers.d.ts +20 -0
- package/dist/domain/analysis/query-helpers.d.ts.map +1 -0
- package/dist/domain/analysis/query-helpers.js +27 -0
- package/dist/domain/analysis/query-helpers.js.map +1 -0
- package/dist/domain/graph/builder/context.d.ts +2 -1
- package/dist/domain/graph/builder/context.d.ts.map +1 -1
- package/dist/domain/graph/builder/context.js +1 -0
- package/dist/domain/graph/builder/context.js.map +1 -1
- package/dist/domain/graph/builder/helpers.d.ts.map +1 -1
- package/dist/domain/graph/builder/helpers.js +15 -9
- package/dist/domain/graph/builder/helpers.js.map +1 -1
- package/dist/domain/graph/builder/incremental.d.ts.map +1 -1
- package/dist/domain/graph/builder/incremental.js +3 -2
- package/dist/domain/graph/builder/incremental.js.map +1 -1
- package/dist/domain/graph/builder/pipeline.d.ts.map +1 -1
- package/dist/domain/graph/builder/pipeline.js +95 -7
- package/dist/domain/graph/builder/pipeline.js.map +1 -1
- package/dist/domain/graph/builder/stages/build-edges.d.ts.map +1 -1
- package/dist/domain/graph/builder/stages/build-edges.js +101 -57
- package/dist/domain/graph/builder/stages/build-edges.js.map +1 -1
- package/dist/domain/graph/builder/stages/build-structure.d.ts.map +1 -1
- package/dist/domain/graph/builder/stages/build-structure.js +33 -3
- package/dist/domain/graph/builder/stages/build-structure.js.map +1 -1
- package/dist/domain/graph/builder/stages/collect-files.d.ts.map +1 -1
- package/dist/domain/graph/builder/stages/collect-files.js +70 -6
- package/dist/domain/graph/builder/stages/collect-files.js.map +1 -1
- package/dist/domain/graph/builder/stages/detect-changes.d.ts.map +1 -1
- package/dist/domain/graph/builder/stages/detect-changes.js +36 -14
- package/dist/domain/graph/builder/stages/detect-changes.js.map +1 -1
- package/dist/domain/graph/builder/stages/finalize.d.ts.map +1 -1
- package/dist/domain/graph/builder/stages/finalize.js +130 -88
- package/dist/domain/graph/builder/stages/finalize.js.map +1 -1
- package/dist/domain/graph/builder/stages/insert-nodes.d.ts.map +1 -1
- package/dist/domain/graph/builder/stages/insert-nodes.js +124 -16
- package/dist/domain/graph/builder/stages/insert-nodes.js.map +1 -1
- package/dist/domain/graph/builder/stages/resolve-imports.d.ts.map +1 -1
- package/dist/domain/graph/builder/stages/resolve-imports.js +3 -2
- package/dist/domain/graph/builder/stages/resolve-imports.js.map +1 -1
- package/dist/domain/graph/resolve.d.ts +0 -4
- package/dist/domain/graph/resolve.d.ts.map +1 -1
- package/dist/domain/graph/resolve.js +32 -48
- package/dist/domain/graph/resolve.js.map +1 -1
- package/dist/domain/graph/watcher.d.ts.map +1 -1
- package/dist/domain/graph/watcher.js +12 -12
- package/dist/domain/graph/watcher.js.map +1 -1
- package/dist/domain/parser.d.ts +1 -1
- package/dist/domain/parser.d.ts.map +1 -1
- package/dist/domain/parser.js +165 -101
- package/dist/domain/parser.js.map +1 -1
- package/dist/domain/search/search/cli-formatter.d.ts.map +1 -1
- package/dist/domain/search/search/cli-formatter.js +88 -83
- package/dist/domain/search/search/cli-formatter.js.map +1 -1
- package/dist/extractors/bash.d.ts +6 -0
- package/dist/extractors/bash.d.ts.map +1 -0
- package/dist/extractors/bash.js +91 -0
- package/dist/extractors/bash.js.map +1 -0
- package/dist/extractors/c.d.ts +6 -0
- package/dist/extractors/c.d.ts.map +1 -0
- package/dist/extractors/c.js +204 -0
- package/dist/extractors/c.js.map +1 -0
- package/dist/extractors/cpp.d.ts +6 -0
- package/dist/extractors/cpp.d.ts.map +1 -0
- package/dist/extractors/cpp.js +283 -0
- package/dist/extractors/cpp.js.map +1 -0
- package/dist/extractors/csharp.d.ts.map +1 -1
- package/dist/extractors/csharp.js +42 -54
- package/dist/extractors/csharp.js.map +1 -1
- package/dist/extractors/go.d.ts.map +1 -1
- package/dist/extractors/go.js +126 -130
- package/dist/extractors/go.js.map +1 -1
- package/dist/extractors/hcl.js +6 -6
- package/dist/extractors/hcl.js.map +1 -1
- package/dist/extractors/helpers.d.ts +32 -1
- package/dist/extractors/helpers.d.ts.map +1 -1
- package/dist/extractors/helpers.js +74 -0
- package/dist/extractors/helpers.js.map +1 -1
- package/dist/extractors/index.d.ts +6 -0
- package/dist/extractors/index.d.ts.map +1 -1
- package/dist/extractors/index.js +6 -0
- package/dist/extractors/index.js.map +1 -1
- package/dist/extractors/java.d.ts.map +1 -1
- package/dist/extractors/java.js +32 -47
- package/dist/extractors/java.js.map +1 -1
- package/dist/extractors/javascript.d.ts.map +1 -1
- package/dist/extractors/javascript.js +359 -330
- package/dist/extractors/javascript.js.map +1 -1
- package/dist/extractors/kotlin.d.ts +6 -0
- package/dist/extractors/kotlin.d.ts.map +1 -0
- package/dist/extractors/kotlin.js +275 -0
- package/dist/extractors/kotlin.js.map +1 -0
- package/dist/extractors/php.d.ts.map +1 -1
- package/dist/extractors/php.js +39 -44
- package/dist/extractors/php.js.map +1 -1
- package/dist/extractors/python.d.ts.map +1 -1
- package/dist/extractors/python.js +75 -93
- package/dist/extractors/python.js.map +1 -1
- package/dist/extractors/ruby.js +6 -13
- package/dist/extractors/ruby.js.map +1 -1
- package/dist/extractors/rust.d.ts.map +1 -1
- package/dist/extractors/rust.js +58 -82
- package/dist/extractors/rust.js.map +1 -1
- package/dist/extractors/scala.d.ts +6 -0
- package/dist/extractors/scala.d.ts.map +1 -0
- package/dist/extractors/scala.js +269 -0
- package/dist/extractors/scala.js.map +1 -0
- package/dist/extractors/swift.d.ts +6 -0
- package/dist/extractors/swift.d.ts.map +1 -0
- package/dist/extractors/swift.js +275 -0
- package/dist/extractors/swift.js.map +1 -0
- package/dist/features/ast.d.ts +16 -1
- package/dist/features/ast.d.ts.map +1 -1
- package/dist/features/ast.js +45 -23
- package/dist/features/ast.js.map +1 -1
- package/dist/features/audit.d.ts.map +1 -1
- package/dist/features/audit.js +17 -21
- package/dist/features/audit.js.map +1 -1
- package/dist/features/branch-compare.d.ts.map +1 -1
- package/dist/features/branch-compare.js +50 -4
- package/dist/features/branch-compare.js.map +1 -1
- package/dist/features/cfg.d.ts +7 -1
- package/dist/features/cfg.d.ts.map +1 -1
- package/dist/features/cfg.js +118 -62
- package/dist/features/cfg.js.map +1 -1
- package/dist/features/check.d.ts.map +1 -1
- package/dist/features/check.js +79 -62
- package/dist/features/check.js.map +1 -1
- package/dist/features/complexity-query.d.ts.map +1 -1
- package/dist/features/complexity-query.js +142 -137
- package/dist/features/complexity-query.js.map +1 -1
- package/dist/features/complexity.d.ts +7 -1
- package/dist/features/complexity.d.ts.map +1 -1
- package/dist/features/complexity.js +62 -1
- package/dist/features/complexity.js.map +1 -1
- package/dist/features/dataflow.d.ts +7 -1
- package/dist/features/dataflow.d.ts.map +1 -1
- package/dist/features/dataflow.js +356 -188
- package/dist/features/dataflow.js.map +1 -1
- package/dist/features/graph-enrichment.d.ts.map +1 -1
- package/dist/features/graph-enrichment.js +117 -104
- package/dist/features/graph-enrichment.js.map +1 -1
- package/dist/features/sequence.d.ts.map +1 -1
- package/dist/features/sequence.js +25 -4
- package/dist/features/sequence.js.map +1 -1
- package/dist/features/snapshot.d.ts.map +1 -1
- package/dist/features/snapshot.js +2 -1
- package/dist/features/snapshot.js.map +1 -1
- package/dist/features/structure-query.d.ts.map +1 -1
- package/dist/features/structure-query.js +29 -4
- package/dist/features/structure-query.js.map +1 -1
- package/dist/features/structure.d.ts.map +1 -1
- package/dist/features/structure.js +35 -15
- package/dist/features/structure.js.map +1 -1
- package/dist/graph/algorithms/leiden/adapter.d.ts.map +1 -1
- package/dist/graph/algorithms/leiden/adapter.js +88 -73
- package/dist/graph/algorithms/leiden/adapter.js.map +1 -1
- package/dist/graph/algorithms/leiden/index.js +43 -28
- package/dist/graph/algorithms/leiden/index.js.map +1 -1
- package/dist/graph/algorithms/leiden/optimiser.d.ts.map +1 -1
- package/dist/graph/algorithms/leiden/optimiser.js +90 -104
- package/dist/graph/algorithms/leiden/optimiser.js.map +1 -1
- package/dist/graph/algorithms/leiden/partition.d.ts.map +1 -1
- package/dist/graph/algorithms/leiden/partition.js +89 -106
- package/dist/graph/algorithms/leiden/partition.js.map +1 -1
- package/dist/graph/model.d.ts +2 -0
- package/dist/graph/model.d.ts.map +1 -1
- package/dist/graph/model.js +20 -8
- package/dist/graph/model.js.map +1 -1
- package/dist/infrastructure/config.d.ts +0 -8
- package/dist/infrastructure/config.d.ts.map +1 -1
- package/dist/infrastructure/config.js +73 -62
- package/dist/infrastructure/config.js.map +1 -1
- package/dist/infrastructure/registry.d.ts +0 -8
- package/dist/infrastructure/registry.d.ts.map +1 -1
- package/dist/infrastructure/registry.js +12 -14
- package/dist/infrastructure/registry.js.map +1 -1
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +47 -45
- package/dist/mcp/server.js.map +1 -1
- package/dist/presentation/audit.d.ts.map +1 -1
- package/dist/presentation/audit.js +61 -57
- package/dist/presentation/audit.js.map +1 -1
- package/dist/presentation/branch-compare.d.ts.map +1 -1
- package/dist/presentation/branch-compare.js +56 -38
- package/dist/presentation/branch-compare.js.map +1 -1
- package/dist/presentation/check.d.ts.map +1 -1
- package/dist/presentation/check.js +30 -32
- package/dist/presentation/check.js.map +1 -1
- package/dist/presentation/colors.d.ts.map +1 -1
- package/dist/presentation/colors.js +2 -0
- package/dist/presentation/colors.js.map +1 -1
- package/dist/presentation/complexity.d.ts.map +1 -1
- package/dist/presentation/complexity.js +25 -19
- package/dist/presentation/complexity.js.map +1 -1
- package/dist/presentation/queries-cli/exports.d.ts.map +1 -1
- package/dist/presentation/queries-cli/exports.js +15 -15
- package/dist/presentation/queries-cli/exports.js.map +1 -1
- package/dist/presentation/queries-cli/impact.d.ts.map +1 -1
- package/dist/presentation/queries-cli/impact.js +29 -19
- package/dist/presentation/queries-cli/impact.js.map +1 -1
- package/dist/types.d.ts +406 -3
- package/dist/types.d.ts.map +1 -1
- package/grammars/tree-sitter-bash.wasm +0 -0
- package/grammars/tree-sitter-c.wasm +0 -0
- package/grammars/tree-sitter-cpp.wasm +0 -0
- package/grammars/tree-sitter-kotlin.wasm +0 -0
- package/grammars/tree-sitter-scala.wasm +0 -0
- package/grammars/tree-sitter-swift.wasm +0 -0
- package/package.json +67 -11
- package/src/ast-analysis/engine.ts +147 -138
- package/src/ast-analysis/rules/javascript.ts +1 -0
- package/src/ast-analysis/visitors/ast-store-visitor.ts +116 -34
- package/src/ast-analysis/visitors/complexity-visitor.ts +11 -11
- package/src/db/better-sqlite3.ts +20 -0
- package/src/db/connection.ts +148 -26
- package/src/db/index.ts +4 -1
- package/src/db/migrations.ts +38 -32
- package/src/db/query-builder.ts +30 -5
- package/src/db/repository/index.ts +1 -0
- package/src/db/repository/native-repository.ts +361 -0
- package/src/db/repository/nodes.ts +7 -3
- package/src/domain/analysis/context.ts +73 -75
- package/src/domain/analysis/dependencies.ts +78 -68
- package/src/domain/analysis/exports.ts +45 -34
- package/src/domain/analysis/fn-impact.ts +67 -64
- package/src/domain/analysis/module-map.ts +103 -8
- package/src/domain/analysis/query-helpers.ts +35 -0
- package/src/domain/graph/builder/context.ts +2 -0
- package/src/domain/graph/builder/helpers.ts +12 -6
- package/src/domain/graph/builder/incremental.ts +3 -2
- package/src/domain/graph/builder/pipeline.ts +98 -6
- package/src/domain/graph/builder/stages/build-edges.ts +116 -83
- package/src/domain/graph/builder/stages/build-structure.ts +46 -8
- package/src/domain/graph/builder/stages/collect-files.ts +83 -6
- package/src/domain/graph/builder/stages/detect-changes.ts +44 -21
- package/src/domain/graph/builder/stages/finalize.ts +172 -109
- package/src/domain/graph/builder/stages/insert-nodes.ts +147 -17
- package/src/domain/graph/builder/stages/resolve-imports.ts +3 -2
- package/src/domain/graph/resolve.ts +34 -46
- package/src/domain/graph/watcher.ts +12 -14
- package/src/domain/parser.ts +169 -97
- package/src/domain/search/search/cli-formatter.ts +121 -94
- package/src/extractors/bash.ts +97 -0
- package/src/extractors/c.ts +212 -0
- package/src/extractors/cpp.ts +298 -0
- package/src/extractors/csharp.ts +53 -56
- package/src/extractors/go.ts +152 -134
- package/src/extractors/hcl.ts +6 -6
- package/src/extractors/helpers.ts +93 -1
- package/src/extractors/index.ts +6 -0
- package/src/extractors/java.ts +43 -48
- package/src/extractors/javascript.ts +382 -317
- package/src/extractors/kotlin.ts +293 -0
- package/src/extractors/php.ts +46 -40
- package/src/extractors/python.ts +81 -104
- package/src/extractors/ruby.ts +6 -13
- package/src/extractors/rust.ts +65 -84
- package/src/extractors/scala.ts +285 -0
- package/src/extractors/swift.ts +293 -0
- package/src/features/ast.ts +74 -24
- package/src/features/audit.ts +24 -20
- package/src/features/branch-compare.ts +54 -5
- package/src/features/cfg.ts +158 -65
- package/src/features/check.ts +90 -74
- package/src/features/complexity-query.ts +181 -163
- package/src/features/complexity.ts +64 -1
- package/src/features/dataflow.ts +462 -217
- package/src/features/graph-enrichment.ts +161 -117
- package/src/features/sequence.ts +27 -4
- package/src/features/snapshot.ts +2 -1
- package/src/features/structure-query.ts +43 -4
- package/src/features/structure.ts +50 -22
- package/src/graph/algorithms/leiden/adapter.ts +126 -71
- package/src/graph/algorithms/leiden/index.ts +67 -28
- package/src/graph/algorithms/leiden/optimiser.ts +114 -105
- package/src/graph/algorithms/leiden/partition.ts +131 -98
- package/src/graph/model.ts +19 -7
- package/src/infrastructure/config.ts +60 -58
- package/src/infrastructure/registry.ts +17 -14
- package/src/mcp/server.ts +48 -47
- package/src/presentation/audit.ts +72 -67
- package/src/presentation/branch-compare.ts +54 -50
- package/src/presentation/check.ts +34 -34
- package/src/presentation/colors.ts +2 -0
- package/src/presentation/complexity.ts +39 -33
- package/src/presentation/queries-cli/exports.ts +17 -17
- package/src/presentation/queries-cli/impact.ts +30 -22
- package/src/types.ts +458 -3
package/src/features/cfg.ts
CHANGED
|
@@ -275,16 +275,161 @@ function allCfgNative(fileSymbols: Map<string, FileSymbols>): boolean {
|
|
|
275
275
|
return hasCfgFile;
|
|
276
276
|
}
|
|
277
277
|
|
|
278
|
+
/** Persist native CFG data for a single file (fast path — no tree/visitor needed). */
|
|
279
|
+
function persistNativeFileCfg(
|
|
280
|
+
db: BetterSqlite3Database,
|
|
281
|
+
symbols: FileSymbols,
|
|
282
|
+
relPath: string,
|
|
283
|
+
insertBlock: ReturnType<BetterSqlite3Database['prepare']>,
|
|
284
|
+
insertEdge: ReturnType<BetterSqlite3Database['prepare']>,
|
|
285
|
+
): number {
|
|
286
|
+
let count = 0;
|
|
287
|
+
for (const def of symbols.definitions) {
|
|
288
|
+
if (def.kind !== 'function' && def.kind !== 'method') continue;
|
|
289
|
+
if (!def.line) continue;
|
|
290
|
+
|
|
291
|
+
const nodeId = getFunctionNodeId(db, def.name, relPath, def.line);
|
|
292
|
+
if (!nodeId) continue;
|
|
293
|
+
|
|
294
|
+
deleteCfgForNode(db, nodeId);
|
|
295
|
+
if (!def.cfg?.blocks?.length) continue;
|
|
296
|
+
|
|
297
|
+
persistCfg(
|
|
298
|
+
def.cfg as unknown as { blocks: CfgBuildBlock[]; edges: CfgBuildEdge[] },
|
|
299
|
+
nodeId,
|
|
300
|
+
insertBlock,
|
|
301
|
+
insertEdge,
|
|
302
|
+
);
|
|
303
|
+
count++;
|
|
304
|
+
}
|
|
305
|
+
return count;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/** Resolve CFG for a definition from native data or visitor results. */
|
|
309
|
+
function resolveCfgForDef(
|
|
310
|
+
def: Definition,
|
|
311
|
+
visitorCfgByLine: Map<number, VisitorCfgResult[]> | null,
|
|
312
|
+
): { blocks: CfgBuildBlock[]; edges: CfgBuildEdge[] } | null {
|
|
313
|
+
if (def.cfg?.blocks?.length) {
|
|
314
|
+
return def.cfg as unknown as { blocks: CfgBuildBlock[]; edges: CfgBuildEdge[] };
|
|
315
|
+
}
|
|
316
|
+
if (!visitorCfgByLine) return null;
|
|
317
|
+
const candidates = visitorCfgByLine.get(def.line);
|
|
318
|
+
if (!candidates) return null;
|
|
319
|
+
const r =
|
|
320
|
+
candidates.length === 1
|
|
321
|
+
? candidates[0]
|
|
322
|
+
: (candidates.find((c) => {
|
|
323
|
+
const n = c.funcNode.childForFieldName?.('name');
|
|
324
|
+
return n && n.text === def.name;
|
|
325
|
+
}) ?? candidates[0]);
|
|
326
|
+
return r ? { blocks: r.blocks, edges: r.edges } : null;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/** Persist CFG data for a single file using visitor/native hybrid path. */
|
|
330
|
+
function persistVisitorFileCfg(
|
|
331
|
+
db: BetterSqlite3Database,
|
|
332
|
+
symbols: FileSymbols,
|
|
333
|
+
relPath: string,
|
|
334
|
+
rootDir: string,
|
|
335
|
+
extToLang: Map<string, string>,
|
|
336
|
+
parsers: unknown,
|
|
337
|
+
getParserFn: unknown,
|
|
338
|
+
insertBlock: ReturnType<BetterSqlite3Database['prepare']>,
|
|
339
|
+
insertEdge: ReturnType<BetterSqlite3Database['prepare']>,
|
|
340
|
+
): number {
|
|
341
|
+
const treeLang = getTreeAndLang(symbols, relPath, rootDir, extToLang, parsers, getParserFn);
|
|
342
|
+
if (!treeLang) return 0;
|
|
343
|
+
const { tree, langId } = treeLang;
|
|
344
|
+
|
|
345
|
+
const cfgRules = CFG_RULES.get(langId);
|
|
346
|
+
if (!cfgRules) return 0;
|
|
347
|
+
|
|
348
|
+
const visitorCfgByLine = buildVisitorCfgMap(tree, cfgRules, symbols, langId);
|
|
349
|
+
let count = 0;
|
|
350
|
+
|
|
351
|
+
for (const def of symbols.definitions) {
|
|
352
|
+
if (def.kind !== 'function' && def.kind !== 'method') continue;
|
|
353
|
+
if (!def.line) continue;
|
|
354
|
+
|
|
355
|
+
const nodeId = getFunctionNodeId(db, def.name, relPath, def.line);
|
|
356
|
+
if (!nodeId) continue;
|
|
357
|
+
|
|
358
|
+
const cfg = resolveCfgForDef(def, visitorCfgByLine);
|
|
359
|
+
deleteCfgForNode(db, nodeId);
|
|
360
|
+
if (!cfg || cfg.blocks.length === 0) continue;
|
|
361
|
+
|
|
362
|
+
persistCfg(cfg, nodeId, insertBlock, insertEdge);
|
|
363
|
+
count++;
|
|
364
|
+
}
|
|
365
|
+
return count;
|
|
366
|
+
}
|
|
367
|
+
|
|
278
368
|
export async function buildCFGData(
|
|
279
369
|
db: BetterSqlite3Database,
|
|
280
370
|
fileSymbols: Map<string, FileSymbols>,
|
|
281
371
|
rootDir: string,
|
|
282
|
-
|
|
372
|
+
engineOpts?: {
|
|
373
|
+
nativeDb?: { bulkInsertCfg?(entries: Array<Record<string, unknown>>): number };
|
|
374
|
+
suspendJsDb?: () => void;
|
|
375
|
+
resumeJsDb?: () => void;
|
|
376
|
+
},
|
|
283
377
|
): Promise<void> {
|
|
284
378
|
// Fast path: when all function/method defs already have native CFG data,
|
|
285
379
|
// skip WASM parser init, tree parsing, and JS visitor entirely — just persist.
|
|
286
380
|
const allNative = allCfgNative(fileSymbols);
|
|
287
381
|
|
|
382
|
+
// ── Native bulk-insert fast path ──────────────────────────────────────
|
|
383
|
+
const nativeDb = engineOpts?.nativeDb;
|
|
384
|
+
if (allNative && nativeDb?.bulkInsertCfg) {
|
|
385
|
+
const entries: Array<Record<string, unknown>> = [];
|
|
386
|
+
|
|
387
|
+
for (const [relPath, symbols] of fileSymbols) {
|
|
388
|
+
const ext = path.extname(relPath).toLowerCase();
|
|
389
|
+
if (!CFG_EXTENSIONS.has(ext)) continue;
|
|
390
|
+
|
|
391
|
+
for (const def of symbols.definitions) {
|
|
392
|
+
if (def.kind !== 'function' && def.kind !== 'method') continue;
|
|
393
|
+
if (!def.line) continue;
|
|
394
|
+
|
|
395
|
+
const nodeId = getFunctionNodeId(db, def.name, relPath, def.line);
|
|
396
|
+
if (!nodeId) continue;
|
|
397
|
+
|
|
398
|
+
deleteCfgForNode(db, nodeId);
|
|
399
|
+
if (!def.cfg?.blocks?.length) continue;
|
|
400
|
+
|
|
401
|
+
const cfg = def.cfg as unknown as { blocks: CfgBuildBlock[]; edges: CfgBuildEdge[] };
|
|
402
|
+
entries.push({
|
|
403
|
+
nodeId,
|
|
404
|
+
blocks: cfg.blocks.map((b) => ({
|
|
405
|
+
index: b.index,
|
|
406
|
+
blockType: b.type,
|
|
407
|
+
startLine: b.startLine ?? null,
|
|
408
|
+
endLine: b.endLine ?? null,
|
|
409
|
+
label: b.label ?? null,
|
|
410
|
+
})),
|
|
411
|
+
edges: cfg.edges.map((e) => ({
|
|
412
|
+
sourceIndex: e.sourceIndex,
|
|
413
|
+
targetIndex: e.targetIndex,
|
|
414
|
+
kind: e.kind,
|
|
415
|
+
})),
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
if (entries.length > 0) {
|
|
421
|
+
let inserted: number;
|
|
422
|
+
try {
|
|
423
|
+
engineOpts?.suspendJsDb?.();
|
|
424
|
+
inserted = nativeDb.bulkInsertCfg(entries);
|
|
425
|
+
} finally {
|
|
426
|
+
engineOpts?.resumeJsDb?.();
|
|
427
|
+
}
|
|
428
|
+
info(`CFG (native bulk): ${inserted} blocks across ${entries.length} functions`);
|
|
429
|
+
}
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
|
|
288
433
|
const extToLang = buildExtToLangMap();
|
|
289
434
|
let parsers: unknown = null;
|
|
290
435
|
let getParserFn: unknown = null;
|
|
@@ -308,74 +453,22 @@ export async function buildCFGData(
|
|
|
308
453
|
const ext = path.extname(relPath).toLowerCase();
|
|
309
454
|
if (!CFG_EXTENSIONS.has(ext)) continue;
|
|
310
455
|
|
|
311
|
-
// Native fast path: skip tree/visitor setup when all CFG is pre-computed.
|
|
312
|
-
// Only apply to files without _tree — files with _tree were WASM-parsed
|
|
313
|
-
// and need the slow path (visitor) to compute CFG.
|
|
314
456
|
if (allNative && !symbols._tree) {
|
|
315
|
-
|
|
316
|
-
if (def.kind !== 'function' && def.kind !== 'method') continue;
|
|
317
|
-
if (!def.line) continue;
|
|
318
|
-
|
|
319
|
-
const nodeId = getFunctionNodeId(db, def.name, relPath, def.line);
|
|
320
|
-
if (!nodeId) continue;
|
|
321
|
-
|
|
322
|
-
// Always delete stale CFG rows (handles body-removed case)
|
|
323
|
-
deleteCfgForNode(db, nodeId);
|
|
324
|
-
if (!def.cfg?.blocks?.length) continue;
|
|
325
|
-
|
|
326
|
-
persistCfg(
|
|
327
|
-
def.cfg as unknown as { blocks: CfgBuildBlock[]; edges: CfgBuildEdge[] },
|
|
328
|
-
nodeId,
|
|
329
|
-
insertBlock,
|
|
330
|
-
insertEdge,
|
|
331
|
-
);
|
|
332
|
-
analyzed++;
|
|
333
|
-
}
|
|
457
|
+
analyzed += persistNativeFileCfg(db, symbols, relPath, insertBlock, insertEdge);
|
|
334
458
|
continue;
|
|
335
459
|
}
|
|
336
460
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
for (const def of symbols.definitions) {
|
|
350
|
-
if (def.kind !== 'function' && def.kind !== 'method') continue;
|
|
351
|
-
if (!def.line) continue;
|
|
352
|
-
|
|
353
|
-
const nodeId = getFunctionNodeId(db, def.name, relPath, def.line);
|
|
354
|
-
if (!nodeId) continue;
|
|
355
|
-
|
|
356
|
-
let cfg: { blocks: CfgBuildBlock[]; edges: CfgBuildEdge[] } | null = null;
|
|
357
|
-
if (def.cfg?.blocks?.length) {
|
|
358
|
-
cfg = def.cfg as unknown as { blocks: CfgBuildBlock[]; edges: CfgBuildEdge[] };
|
|
359
|
-
} else if (visitorCfgByLine) {
|
|
360
|
-
const candidates = visitorCfgByLine.get(def.line);
|
|
361
|
-
const r = !candidates
|
|
362
|
-
? undefined
|
|
363
|
-
: candidates.length === 1
|
|
364
|
-
? candidates[0]
|
|
365
|
-
: (candidates.find((c) => {
|
|
366
|
-
const n = c.funcNode.childForFieldName?.('name');
|
|
367
|
-
return n && n.text === def.name;
|
|
368
|
-
}) ?? candidates[0]);
|
|
369
|
-
if (r) cfg = { blocks: r.blocks, edges: r.edges };
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
// Always purge stale rows (handles body-removed case)
|
|
373
|
-
deleteCfgForNode(db, nodeId);
|
|
374
|
-
if (!cfg || cfg.blocks.length === 0) continue;
|
|
375
|
-
|
|
376
|
-
persistCfg(cfg, nodeId, insertBlock, insertEdge);
|
|
377
|
-
analyzed++;
|
|
378
|
-
}
|
|
461
|
+
analyzed += persistVisitorFileCfg(
|
|
462
|
+
db,
|
|
463
|
+
symbols,
|
|
464
|
+
relPath,
|
|
465
|
+
rootDir,
|
|
466
|
+
extToLang,
|
|
467
|
+
parsers,
|
|
468
|
+
getParserFn,
|
|
469
|
+
insertBlock,
|
|
470
|
+
insertEdge,
|
|
471
|
+
);
|
|
379
472
|
}
|
|
380
473
|
});
|
|
381
474
|
|
package/src/features/check.ts
CHANGED
|
@@ -291,6 +291,85 @@ interface CheckOpts {
|
|
|
291
291
|
config?: CodegraphConfig;
|
|
292
292
|
}
|
|
293
293
|
|
|
294
|
+
/** Walk up from repoRoot to find the nearest .git directory. */
|
|
295
|
+
function findGitRoot(repoRoot: string): string | null {
|
|
296
|
+
let dir = repoRoot;
|
|
297
|
+
while (dir) {
|
|
298
|
+
if (fs.existsSync(path.join(dir, '.git'))) return dir;
|
|
299
|
+
const parent = path.dirname(dir);
|
|
300
|
+
if (parent === dir) break;
|
|
301
|
+
dir = parent;
|
|
302
|
+
}
|
|
303
|
+
return null;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/** Run git diff and return the raw output string. */
|
|
307
|
+
function getGitDiff(repoRoot: string, opts: { staged?: boolean; ref?: string }): string {
|
|
308
|
+
const args = opts.staged
|
|
309
|
+
? ['diff', '--cached', '--unified=0', '--no-color']
|
|
310
|
+
: ['diff', opts.ref || 'HEAD', '--unified=0', '--no-color'];
|
|
311
|
+
return execFileSync('git', args, {
|
|
312
|
+
cwd: repoRoot,
|
|
313
|
+
encoding: 'utf-8',
|
|
314
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
315
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/** Resolve which check predicates are enabled from opts + config. */
|
|
320
|
+
function resolveCheckFlags(opts: CheckOpts, config: CodegraphConfig) {
|
|
321
|
+
const checkConfig = config.check || ({} as CodegraphConfig['check']);
|
|
322
|
+
return {
|
|
323
|
+
enableCycles: opts.cycles ?? checkConfig.cycles ?? true,
|
|
324
|
+
enableSignatures: opts.signatures ?? checkConfig.signatures ?? true,
|
|
325
|
+
enableBoundaries: opts.boundaries ?? checkConfig.boundaries ?? true,
|
|
326
|
+
blastRadiusThreshold: opts.blastRadius ?? checkConfig.blastRadius ?? null,
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/** Run all enabled check predicates and return the results. */
|
|
331
|
+
function runPredicates(
|
|
332
|
+
db: BetterSqlite3Database,
|
|
333
|
+
diff: ParsedDiff,
|
|
334
|
+
flags: ReturnType<typeof resolveCheckFlags>,
|
|
335
|
+
repoRoot: string,
|
|
336
|
+
noTests: boolean,
|
|
337
|
+
maxDepth: number,
|
|
338
|
+
): PredicateResult[] {
|
|
339
|
+
const changedFiles = new Set(diff.changedRanges.keys());
|
|
340
|
+
const predicates: PredicateResult[] = [];
|
|
341
|
+
|
|
342
|
+
if (flags.enableCycles) {
|
|
343
|
+
predicates.push({ name: 'cycles', ...checkNoNewCycles(db, changedFiles, noTests) });
|
|
344
|
+
}
|
|
345
|
+
if (flags.blastRadiusThreshold != null) {
|
|
346
|
+
predicates.push({
|
|
347
|
+
name: 'blast-radius',
|
|
348
|
+
...checkMaxBlastRadius(db, diff.changedRanges, flags.blastRadiusThreshold, noTests, maxDepth),
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
if (flags.enableSignatures) {
|
|
352
|
+
predicates.push({
|
|
353
|
+
name: 'signatures',
|
|
354
|
+
...checkNoSignatureChanges(db, diff.oldRanges, noTests),
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
if (flags.enableBoundaries) {
|
|
358
|
+
predicates.push({
|
|
359
|
+
name: 'boundaries',
|
|
360
|
+
...checkNoBoundaryViolations(db, changedFiles, repoRoot, noTests),
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
return predicates;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
const EMPTY_CHECK: CheckResult = {
|
|
368
|
+
predicates: [],
|
|
369
|
+
summary: { total: 0, passed: 0, failed: 0, changedFiles: 0, newFiles: 0 },
|
|
370
|
+
passed: true,
|
|
371
|
+
};
|
|
372
|
+
|
|
294
373
|
export function checkData(customDbPath: string | undefined, opts: CheckOpts = {}): CheckResult {
|
|
295
374
|
const db = openReadonlyOrFail(customDbPath);
|
|
296
375
|
|
|
@@ -301,89 +380,26 @@ export function checkData(customDbPath: string | undefined, opts: CheckOpts = {}
|
|
|
301
380
|
const maxDepth = opts.depth || 3;
|
|
302
381
|
|
|
303
382
|
const config = opts.config || loadConfig(repoRoot);
|
|
304
|
-
const
|
|
305
|
-
|
|
306
|
-
const
|
|
307
|
-
|
|
308
|
-
const enableBoundaries = opts.boundaries ?? checkConfig.boundaries ?? true;
|
|
309
|
-
const blastRadiusThreshold = opts.blastRadius ?? checkConfig.blastRadius ?? null;
|
|
310
|
-
|
|
311
|
-
let checkDir = repoRoot;
|
|
312
|
-
let isGitRepo = false;
|
|
313
|
-
while (checkDir) {
|
|
314
|
-
if (fs.existsSync(path.join(checkDir, '.git'))) {
|
|
315
|
-
isGitRepo = true;
|
|
316
|
-
break;
|
|
317
|
-
}
|
|
318
|
-
const parent = path.dirname(checkDir);
|
|
319
|
-
if (parent === checkDir) break;
|
|
320
|
-
checkDir = parent;
|
|
321
|
-
}
|
|
322
|
-
if (!isGitRepo) {
|
|
383
|
+
const flags = resolveCheckFlags(opts, config);
|
|
384
|
+
|
|
385
|
+
const gitRoot = findGitRoot(repoRoot);
|
|
386
|
+
if (!gitRoot) {
|
|
323
387
|
return { error: `Not a git repository: ${repoRoot}` };
|
|
324
388
|
}
|
|
325
389
|
|
|
326
390
|
let diffOutput: string;
|
|
327
391
|
try {
|
|
328
|
-
|
|
329
|
-
? ['diff', '--cached', '--unified=0', '--no-color']
|
|
330
|
-
: ['diff', opts.ref || 'HEAD', '--unified=0', '--no-color'];
|
|
331
|
-
diffOutput = execFileSync('git', args, {
|
|
332
|
-
cwd: repoRoot,
|
|
333
|
-
encoding: 'utf-8',
|
|
334
|
-
maxBuffer: 10 * 1024 * 1024,
|
|
335
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
336
|
-
});
|
|
392
|
+
diffOutput = getGitDiff(repoRoot, opts);
|
|
337
393
|
} catch (e) {
|
|
338
394
|
return { error: `Failed to run git diff: ${(e as Error).message}` };
|
|
339
395
|
}
|
|
340
396
|
|
|
341
|
-
if (!diffOutput.trim())
|
|
342
|
-
return {
|
|
343
|
-
predicates: [],
|
|
344
|
-
summary: { total: 0, passed: 0, failed: 0, changedFiles: 0, newFiles: 0 },
|
|
345
|
-
passed: true,
|
|
346
|
-
};
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
const { changedRanges, oldRanges, newFiles } = parseDiffOutput(diffOutput);
|
|
350
|
-
if (changedRanges.size === 0) {
|
|
351
|
-
return {
|
|
352
|
-
predicates: [],
|
|
353
|
-
summary: { total: 0, passed: 0, failed: 0, changedFiles: 0, newFiles: 0 },
|
|
354
|
-
passed: true,
|
|
355
|
-
};
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
const changedFiles = new Set(changedRanges.keys());
|
|
397
|
+
if (!diffOutput.trim()) return EMPTY_CHECK;
|
|
359
398
|
|
|
360
|
-
const
|
|
399
|
+
const diff = parseDiffOutput(diffOutput);
|
|
400
|
+
if (diff.changedRanges.size === 0) return EMPTY_CHECK;
|
|
361
401
|
|
|
362
|
-
|
|
363
|
-
const result = checkNoNewCycles(db, changedFiles, noTests);
|
|
364
|
-
predicates.push({ name: 'cycles', ...result });
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
if (blastRadiusThreshold != null) {
|
|
368
|
-
const result = checkMaxBlastRadius(
|
|
369
|
-
db,
|
|
370
|
-
changedRanges,
|
|
371
|
-
blastRadiusThreshold,
|
|
372
|
-
noTests,
|
|
373
|
-
maxDepth,
|
|
374
|
-
);
|
|
375
|
-
predicates.push({ name: 'blast-radius', ...result });
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
if (enableSignatures) {
|
|
379
|
-
const result = checkNoSignatureChanges(db, oldRanges, noTests);
|
|
380
|
-
predicates.push({ name: 'signatures', ...result });
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
if (enableBoundaries) {
|
|
384
|
-
const result = checkNoBoundaryViolations(db, changedFiles, repoRoot, noTests);
|
|
385
|
-
predicates.push({ name: 'boundaries', ...result });
|
|
386
|
-
}
|
|
402
|
+
const predicates = runPredicates(db, diff, flags, repoRoot, noTests, maxDepth);
|
|
387
403
|
|
|
388
404
|
const passedCount = predicates.filter((p) => p.passed).length;
|
|
389
405
|
const failedCount = predicates.length - passedCount;
|
|
@@ -394,8 +410,8 @@ export function checkData(customDbPath: string | undefined, opts: CheckOpts = {}
|
|
|
394
410
|
total: predicates.length,
|
|
395
411
|
passed: passedCount,
|
|
396
412
|
failed: failedCount,
|
|
397
|
-
changedFiles:
|
|
398
|
-
newFiles: newFiles.size,
|
|
413
|
+
changedFiles: diff.changedRanges.size,
|
|
414
|
+
newFiles: diff.newFiles.size,
|
|
399
415
|
},
|
|
400
416
|
passed: failedCount === 0,
|
|
401
417
|
};
|