@optave/codegraph 3.5.0 → 3.7.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 +47 -21
- 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/visitors/ast-store-visitor.d.ts.map +1 -1
- package/dist/ast-analysis/visitors/ast-store-visitor.js +14 -1
- 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/connection.d.ts +12 -2
- package/dist/db/connection.d.ts.map +1 -1
- package/dist/db/connection.js +81 -53
- package/dist/db/connection.js.map +1 -1
- package/dist/db/index.d.ts +1 -1
- 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 +38 -32
- package/dist/db/migrations.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/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 +69 -3
- 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 +7 -51
- 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 +7 -5
- package/dist/domain/graph/builder/stages/build-structure.js.map +1 -1
- package/dist/domain/graph/builder/stages/collect-files.js +2 -2
- 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 +2 -2
- 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 +124 -105
- 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 +28 -15
- 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 +206 -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/dart.d.ts +6 -0
- package/dist/extractors/dart.d.ts.map +1 -0
- package/dist/extractors/dart.js +277 -0
- package/dist/extractors/dart.js.map +1 -0
- package/dist/extractors/elixir.d.ts +9 -0
- package/dist/extractors/elixir.d.ts.map +1 -0
- package/dist/extractors/elixir.js +223 -0
- package/dist/extractors/elixir.js.map +1 -0
- 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/haskell.d.ts +8 -0
- package/dist/extractors/haskell.d.ts.map +1 -0
- package/dist/extractors/haskell.js +217 -0
- package/dist/extractors/haskell.js.map +1 -0
- 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 +12 -0
- package/dist/extractors/index.d.ts.map +1 -1
- package/dist/extractors/index.js +12 -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 +306 -292
- 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/lua.d.ts +6 -0
- package/dist/extractors/lua.d.ts.map +1 -0
- package/dist/extractors/lua.js +162 -0
- package/dist/extractors/lua.js.map +1 -0
- package/dist/extractors/ocaml.d.ts +6 -0
- package/dist/extractors/ocaml.d.ts.map +1 -0
- package/dist/extractors/ocaml.js +236 -0
- package/dist/extractors/ocaml.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 -83
- 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/extractors/zig.d.ts +9 -0
- package/dist/extractors/zig.d.ts.map +1 -0
- package/dist/extractors/zig.js +276 -0
- package/dist/extractors/zig.js.map +1 -0
- package/dist/features/ast.d.ts +2 -0
- package/dist/features/ast.d.ts.map +1 -1
- package/dist/features/ast.js +9 -24
- 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 +47 -3
- 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 +72 -61
- 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/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 +45 -36
- 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 +182 -7
- 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-dart.wasm +0 -0
- package/grammars/tree-sitter-elixir.wasm +0 -0
- package/grammars/tree-sitter-haskell.wasm +0 -0
- package/grammars/tree-sitter-kotlin.wasm +0 -0
- package/grammars/tree-sitter-lua.wasm +0 -0
- package/grammars/tree-sitter-ocaml.wasm +0 -0
- package/grammars/tree-sitter-scala.wasm +0 -0
- package/grammars/tree-sitter-swift.wasm +0 -0
- package/grammars/tree-sitter-zig.wasm +0 -0
- package/package.json +19 -7
- package/src/ast-analysis/engine.ts +147 -138
- package/src/ast-analysis/visitors/ast-store-visitor.ts +15 -2
- package/src/ast-analysis/visitors/complexity-visitor.ts +11 -11
- package/src/db/connection.ts +90 -59
- package/src/db/index.ts +1 -0
- package/src/db/migrations.ts +36 -32
- 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/helpers.ts +12 -6
- package/src/domain/graph/builder/incremental.ts +3 -2
- package/src/domain/graph/builder/pipeline.ts +71 -3
- package/src/domain/graph/builder/stages/build-edges.ts +10 -75
- package/src/domain/graph/builder/stages/build-structure.ts +9 -7
- package/src/domain/graph/builder/stages/collect-files.ts +2 -2
- package/src/domain/graph/builder/stages/detect-changes.ts +7 -2
- package/src/domain/graph/builder/stages/finalize.ts +159 -125
- package/src/domain/graph/builder/stages/insert-nodes.ts +32 -21
- 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 +222 -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/dart.ts +304 -0
- package/src/extractors/elixir.ts +251 -0
- package/src/extractors/go.ts +152 -134
- package/src/extractors/haskell.ts +235 -0
- package/src/extractors/hcl.ts +6 -6
- package/src/extractors/helpers.ts +93 -1
- package/src/extractors/index.ts +12 -0
- package/src/extractors/java.ts +43 -48
- package/src/extractors/javascript.ts +328 -281
- package/src/extractors/kotlin.ts +293 -0
- package/src/extractors/lua.ts +169 -0
- package/src/extractors/ocaml.ts +259 -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 -85
- package/src/extractors/scala.ts +285 -0
- package/src/extractors/swift.ts +293 -0
- package/src/extractors/zig.ts +294 -0
- package/src/features/ast.ts +10 -25
- package/src/features/audit.ts +24 -20
- package/src/features/branch-compare.ts +51 -4
- package/src/features/cfg.ts +113 -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/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 +46 -37
- 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 +195 -7
|
@@ -14,7 +14,7 @@ import { DATAFLOW_RULES } from '../ast-analysis/rules/index.js';
|
|
|
14
14
|
import { makeDataflowRules as _makeDataflowRules, buildExtensionSet, buildExtToLangMap, } from '../ast-analysis/shared.js';
|
|
15
15
|
import { walkWithVisitors } from '../ast-analysis/visitor.js';
|
|
16
16
|
import { createDataflowVisitor } from '../ast-analysis/visitors/dataflow-visitor.js';
|
|
17
|
-
import { hasDataflowTable, openReadonlyOrFail } from '../db/index.js';
|
|
17
|
+
import { hasDataflowTable, openReadonlyOrFail, openReadonlyWithNative } from '../db/index.js';
|
|
18
18
|
import { ALL_SYMBOL_KINDS, normalizeSymbol } from '../domain/queries.js';
|
|
19
19
|
import { debug, info } from '../infrastructure/logger.js';
|
|
20
20
|
import { isTestFile } from '../infrastructure/test-filter.js';
|
|
@@ -125,8 +125,96 @@ function insertDataflowEdges(insert, data, resolveNode) {
|
|
|
125
125
|
return edgeCount;
|
|
126
126
|
}
|
|
127
127
|
// ── buildDataflowEdges ──────────────────────────────────────────────────────
|
|
128
|
-
export async function buildDataflowEdges(db, fileSymbols, rootDir,
|
|
128
|
+
export async function buildDataflowEdges(db, fileSymbols, rootDir, engineOpts) {
|
|
129
129
|
const extToLang = buildExtToLangMap();
|
|
130
|
+
// ── Native bulk-insert fast path ──────────────────────────────────────
|
|
131
|
+
const nativeDb = engineOpts?.nativeDb;
|
|
132
|
+
if (nativeDb?.bulkInsertDataflow) {
|
|
133
|
+
let needsJsFallback = false;
|
|
134
|
+
const nativeEdges = [];
|
|
135
|
+
const getNodeByNameAndFile = db.prepare(`SELECT id, name, kind, file, line FROM nodes
|
|
136
|
+
WHERE name = ? AND file = ? AND kind IN ('function', 'method')`);
|
|
137
|
+
const getNodeByName = db.prepare(`SELECT id, name, kind, file, line FROM nodes
|
|
138
|
+
WHERE name = ? AND kind IN ('function', 'method')
|
|
139
|
+
ORDER BY file, line LIMIT 10`);
|
|
140
|
+
for (const [relPath, symbols] of fileSymbols) {
|
|
141
|
+
const ext = path.extname(relPath).toLowerCase();
|
|
142
|
+
if (!DATAFLOW_EXTENSIONS.has(ext))
|
|
143
|
+
continue;
|
|
144
|
+
if (!symbols.dataflow) {
|
|
145
|
+
needsJsFallback = true;
|
|
146
|
+
break;
|
|
147
|
+
}
|
|
148
|
+
const resolveNode = (funcName) => {
|
|
149
|
+
const local = getNodeByNameAndFile.all(funcName, relPath);
|
|
150
|
+
if (local.length > 0)
|
|
151
|
+
return local[0];
|
|
152
|
+
const global = getNodeByName.all(funcName);
|
|
153
|
+
return global.length > 0 ? global[0] : null;
|
|
154
|
+
};
|
|
155
|
+
const data = symbols.dataflow;
|
|
156
|
+
for (const flow of data.argFlows) {
|
|
157
|
+
const sourceNode = resolveNode(flow.callerFunc);
|
|
158
|
+
const targetNode = resolveNode(flow.calleeName);
|
|
159
|
+
if (sourceNode && targetNode) {
|
|
160
|
+
nativeEdges.push({
|
|
161
|
+
sourceId: sourceNode.id,
|
|
162
|
+
targetId: targetNode.id,
|
|
163
|
+
kind: 'flows_to',
|
|
164
|
+
paramIndex: flow.argIndex,
|
|
165
|
+
expression: flow.expression,
|
|
166
|
+
line: flow.line,
|
|
167
|
+
confidence: flow.confidence,
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
for (const assignment of data.assignments) {
|
|
172
|
+
const producerNode = resolveNode(assignment.sourceCallName);
|
|
173
|
+
const consumerNode = resolveNode(assignment.callerFunc);
|
|
174
|
+
if (producerNode && consumerNode) {
|
|
175
|
+
nativeEdges.push({
|
|
176
|
+
sourceId: producerNode.id,
|
|
177
|
+
targetId: consumerNode.id,
|
|
178
|
+
kind: 'returns',
|
|
179
|
+
paramIndex: null,
|
|
180
|
+
expression: assignment.expression,
|
|
181
|
+
line: assignment.line,
|
|
182
|
+
confidence: 1.0,
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
for (const mut of data.mutations) {
|
|
187
|
+
const mutatorNode = resolveNode(mut.funcName);
|
|
188
|
+
if (mutatorNode && mut.binding?.type === 'param') {
|
|
189
|
+
nativeEdges.push({
|
|
190
|
+
sourceId: mutatorNode.id,
|
|
191
|
+
targetId: mutatorNode.id,
|
|
192
|
+
kind: 'mutates',
|
|
193
|
+
paramIndex: null,
|
|
194
|
+
expression: mut.mutatingExpr,
|
|
195
|
+
line: mut.line,
|
|
196
|
+
confidence: 1.0,
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
if (!needsJsFallback) {
|
|
202
|
+
if (nativeEdges.length > 0) {
|
|
203
|
+
let inserted;
|
|
204
|
+
try {
|
|
205
|
+
engineOpts?.suspendJsDb?.();
|
|
206
|
+
inserted = nativeDb.bulkInsertDataflow(nativeEdges);
|
|
207
|
+
}
|
|
208
|
+
finally {
|
|
209
|
+
engineOpts?.resumeJsDb?.();
|
|
210
|
+
}
|
|
211
|
+
info(`Dataflow (native bulk): ${inserted} edges inserted`);
|
|
212
|
+
}
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
debug('Dataflow: some files lack pre-computed data — falling back to JS');
|
|
216
|
+
}
|
|
217
|
+
// ── JS fallback path ─────────────────────────────────────────────────
|
|
130
218
|
const { parsers, getParserFn } = await initDataflowParsers(fileSymbols);
|
|
131
219
|
const insert = db.prepare(`INSERT INTO dataflow (source_id, target_id, kind, param_index, expression, line, confidence)
|
|
132
220
|
VALUES (?, ?, ?, ?, ?, ?, ?)`);
|
|
@@ -157,10 +245,96 @@ export async function buildDataflowEdges(db, fileSymbols, rootDir, _engineOpts)
|
|
|
157
245
|
tx();
|
|
158
246
|
info(`Dataflow: ${totalEdges} edges inserted`);
|
|
159
247
|
}
|
|
160
|
-
|
|
161
|
-
|
|
248
|
+
function prepareDataflowStmts(db) {
|
|
249
|
+
return {
|
|
250
|
+
flowsToOut: db.prepare(`SELECT d.*, n.name AS target_name, n.kind AS target_kind, n.file AS target_file, n.line AS target_line
|
|
251
|
+
FROM dataflow d JOIN nodes n ON d.target_id = n.id
|
|
252
|
+
WHERE d.source_id = ? AND d.kind = 'flows_to'`),
|
|
253
|
+
flowsToIn: db.prepare(`SELECT d.*, n.name AS source_name, n.kind AS source_kind, n.file AS source_file, n.line AS source_line
|
|
254
|
+
FROM dataflow d JOIN nodes n ON d.source_id = n.id
|
|
255
|
+
WHERE d.target_id = ? AND d.kind = 'flows_to'`),
|
|
256
|
+
returnsOut: db.prepare(`SELECT d.*, n.name AS target_name, n.kind AS target_kind, n.file AS target_file, n.line AS target_line
|
|
257
|
+
FROM dataflow d JOIN nodes n ON d.target_id = n.id
|
|
258
|
+
WHERE d.source_id = ? AND d.kind = 'returns'`),
|
|
259
|
+
returnsIn: db.prepare(`SELECT d.*, n.name AS source_name, n.kind AS source_kind, n.file AS source_file, n.line AS source_line
|
|
260
|
+
FROM dataflow d JOIN nodes n ON d.source_id = n.id
|
|
261
|
+
WHERE d.target_id = ? AND d.kind = 'returns'`),
|
|
262
|
+
mutatesOut: db.prepare(`SELECT d.*, n.name AS target_name, n.kind AS target_kind, n.file AS target_file, n.line AS target_line
|
|
263
|
+
FROM dataflow d JOIN nodes n ON d.target_id = n.id
|
|
264
|
+
WHERE d.source_id = ? AND d.kind = 'mutates'`),
|
|
265
|
+
mutatesIn: db.prepare(`SELECT d.*, n.name AS source_name, n.kind AS source_kind, n.file AS source_file, n.line AS source_line
|
|
266
|
+
FROM dataflow d JOIN nodes n ON d.source_id = n.id
|
|
267
|
+
WHERE d.target_id = ? AND d.kind = 'mutates'`),
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
function buildNodeDataflowResult(node, stmts, db, hc, noTests) {
|
|
271
|
+
const sym = normalizeSymbol(node, db, hc);
|
|
272
|
+
const flowsTo = stmts.flowsToOut.all(node.id).map((r) => ({
|
|
273
|
+
target: r.target_name,
|
|
274
|
+
kind: r.target_kind,
|
|
275
|
+
file: r.target_file,
|
|
276
|
+
line: r.line,
|
|
277
|
+
paramIndex: r.param_index,
|
|
278
|
+
expression: r.expression,
|
|
279
|
+
confidence: r.confidence,
|
|
280
|
+
}));
|
|
281
|
+
const flowsFrom = stmts.flowsToIn.all(node.id).map((r) => ({
|
|
282
|
+
source: r.source_name,
|
|
283
|
+
kind: r.source_kind,
|
|
284
|
+
file: r.source_file,
|
|
285
|
+
line: r.line,
|
|
286
|
+
paramIndex: r.param_index,
|
|
287
|
+
expression: r.expression,
|
|
288
|
+
confidence: r.confidence,
|
|
289
|
+
}));
|
|
290
|
+
const returnConsumers = stmts.returnsOut.all(node.id).map((r) => ({
|
|
291
|
+
consumer: r.target_name,
|
|
292
|
+
kind: r.target_kind,
|
|
293
|
+
file: r.target_file,
|
|
294
|
+
line: r.line,
|
|
295
|
+
expression: r.expression,
|
|
296
|
+
}));
|
|
297
|
+
const returnedBy = stmts.returnsIn.all(node.id).map((r) => ({
|
|
298
|
+
producer: r.source_name,
|
|
299
|
+
kind: r.source_kind,
|
|
300
|
+
file: r.source_file,
|
|
301
|
+
line: r.line,
|
|
302
|
+
expression: r.expression,
|
|
303
|
+
}));
|
|
304
|
+
const mutatesTargets = stmts.mutatesOut.all(node.id).map((r) => ({
|
|
305
|
+
target: r.target_name,
|
|
306
|
+
expression: r.expression,
|
|
307
|
+
line: r.line,
|
|
308
|
+
}));
|
|
309
|
+
const mutatedBy = stmts.mutatesIn.all(node.id).map((r) => ({
|
|
310
|
+
source: r.source_name,
|
|
311
|
+
expression: r.expression,
|
|
312
|
+
line: r.line,
|
|
313
|
+
}));
|
|
314
|
+
if (noTests) {
|
|
315
|
+
const filter = (arr) => arr.filter((r) => !isTestFile(r.file));
|
|
316
|
+
return {
|
|
317
|
+
...sym,
|
|
318
|
+
flowsTo: filter(flowsTo),
|
|
319
|
+
flowsFrom: filter(flowsFrom),
|
|
320
|
+
returns: returnConsumers.filter((r) => !isTestFile(r.file)),
|
|
321
|
+
returnedBy: returnedBy.filter((r) => !isTestFile(r.file)),
|
|
322
|
+
mutates: mutatesTargets,
|
|
323
|
+
mutatedBy,
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
return {
|
|
327
|
+
...sym,
|
|
328
|
+
flowsTo,
|
|
329
|
+
flowsFrom,
|
|
330
|
+
returns: returnConsumers,
|
|
331
|
+
returnedBy,
|
|
332
|
+
mutates: mutatesTargets,
|
|
333
|
+
mutatedBy,
|
|
334
|
+
};
|
|
335
|
+
}
|
|
162
336
|
export function dataflowData(name, customDbPath, opts = {}) {
|
|
163
|
-
const db =
|
|
337
|
+
const { db, nativeDb, close } = openReadonlyWithNative(customDbPath);
|
|
164
338
|
try {
|
|
165
339
|
const noTests = opts.noTests || false;
|
|
166
340
|
if (!hasDataflowTable(db)) {
|
|
@@ -174,98 +348,163 @@ export function dataflowData(name, customDbPath, opts = {}) {
|
|
|
174
348
|
if (nodes.length === 0) {
|
|
175
349
|
return { name, results: [] };
|
|
176
350
|
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
expression: r.expression,
|
|
238
|
-
line: r.line,
|
|
239
|
-
}));
|
|
240
|
-
if (noTests) {
|
|
241
|
-
const filter = (arr) => arr.filter((r) => !isTestFile(r.file));
|
|
351
|
+
// ── Native fast path: 6 queries per node → 1 napi call per node ──
|
|
352
|
+
if (nativeDb?.getDataflowEdges) {
|
|
353
|
+
const hc = new Map();
|
|
354
|
+
const results = nodes.map((node) => {
|
|
355
|
+
const sym = normalizeSymbol(node, db, hc);
|
|
356
|
+
const d = nativeDb.getDataflowEdges(node.id);
|
|
357
|
+
const flowsTo = d.flowsToOut.map((r) => ({
|
|
358
|
+
target: r.name,
|
|
359
|
+
kind: r.kind,
|
|
360
|
+
file: r.file,
|
|
361
|
+
line: r.line,
|
|
362
|
+
paramIndex: r.paramIndex,
|
|
363
|
+
expression: r.expression,
|
|
364
|
+
confidence: r.confidence,
|
|
365
|
+
}));
|
|
366
|
+
const flowsFrom = d.flowsToIn.map((r) => ({
|
|
367
|
+
source: r.name,
|
|
368
|
+
kind: r.kind,
|
|
369
|
+
file: r.file,
|
|
370
|
+
line: r.line,
|
|
371
|
+
paramIndex: r.paramIndex,
|
|
372
|
+
expression: r.expression,
|
|
373
|
+
confidence: r.confidence,
|
|
374
|
+
}));
|
|
375
|
+
const returnConsumers = d.returnsOut.map((r) => ({
|
|
376
|
+
consumer: r.name,
|
|
377
|
+
kind: r.kind,
|
|
378
|
+
file: r.file,
|
|
379
|
+
line: r.line,
|
|
380
|
+
expression: r.expression,
|
|
381
|
+
}));
|
|
382
|
+
const returnedBy = d.returnsIn.map((r) => ({
|
|
383
|
+
producer: r.name,
|
|
384
|
+
kind: r.kind,
|
|
385
|
+
file: r.file,
|
|
386
|
+
line: r.line,
|
|
387
|
+
expression: r.expression,
|
|
388
|
+
}));
|
|
389
|
+
const mutatesTargets = d.mutatesOut.map((r) => ({
|
|
390
|
+
target: r.name,
|
|
391
|
+
expression: r.expression,
|
|
392
|
+
line: r.line,
|
|
393
|
+
}));
|
|
394
|
+
const mutatedBy = d.mutatesIn.map((r) => ({
|
|
395
|
+
source: r.name,
|
|
396
|
+
expression: r.expression,
|
|
397
|
+
line: r.line,
|
|
398
|
+
}));
|
|
399
|
+
if (noTests) {
|
|
400
|
+
const filter = (arr) => arr.filter((r) => !isTestFile(r.file));
|
|
401
|
+
return {
|
|
402
|
+
...sym,
|
|
403
|
+
flowsTo: filter(flowsTo),
|
|
404
|
+
flowsFrom: filter(flowsFrom),
|
|
405
|
+
returns: returnConsumers.filter((r) => !isTestFile(r.file)),
|
|
406
|
+
returnedBy: returnedBy.filter((r) => !isTestFile(r.file)),
|
|
407
|
+
mutates: mutatesTargets,
|
|
408
|
+
mutatedBy,
|
|
409
|
+
};
|
|
410
|
+
}
|
|
242
411
|
return {
|
|
243
412
|
...sym,
|
|
244
|
-
flowsTo
|
|
245
|
-
flowsFrom
|
|
246
|
-
returns: returnConsumers
|
|
247
|
-
returnedBy
|
|
413
|
+
flowsTo,
|
|
414
|
+
flowsFrom,
|
|
415
|
+
returns: returnConsumers,
|
|
416
|
+
returnedBy,
|
|
248
417
|
mutates: mutatesTargets,
|
|
249
418
|
mutatedBy,
|
|
250
419
|
};
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
mutatedBy,
|
|
260
|
-
};
|
|
261
|
-
});
|
|
420
|
+
});
|
|
421
|
+
const base = { name, results };
|
|
422
|
+
return paginateResult(base, 'results', { limit: opts.limit, offset: opts.offset });
|
|
423
|
+
}
|
|
424
|
+
// ── JS fallback ───────────────────────────────────────────────────
|
|
425
|
+
const stmts = prepareDataflowStmts(db);
|
|
426
|
+
const hc = new Map();
|
|
427
|
+
const results = nodes.map((node) => buildNodeDataflowResult(node, stmts, db, hc, noTests));
|
|
262
428
|
const base = { name, results };
|
|
263
429
|
return paginateResult(base, 'results', { limit: opts.limit, offset: opts.offset });
|
|
264
430
|
}
|
|
265
431
|
finally {
|
|
266
|
-
|
|
432
|
+
close();
|
|
267
433
|
}
|
|
268
434
|
}
|
|
435
|
+
/** BFS through dataflow edges to find a path from source to target. */
|
|
436
|
+
function bfsDataflowPath(db, sourceId, targetId, maxDepth, noTests) {
|
|
437
|
+
const neighborStmt = db.prepare(`SELECT n.id, n.name, n.kind, n.file, n.line, d.kind AS edge_kind, d.expression
|
|
438
|
+
FROM dataflow d JOIN nodes n ON d.target_id = n.id
|
|
439
|
+
WHERE d.source_id = ? AND d.kind IN ('flows_to', 'returns')`);
|
|
440
|
+
const visited = new Set([sourceId]);
|
|
441
|
+
const parent = new Map();
|
|
442
|
+
let queue = [sourceId];
|
|
443
|
+
let found = false;
|
|
444
|
+
for (let depth = 1; depth <= maxDepth; depth++) {
|
|
445
|
+
const nextQueue = [];
|
|
446
|
+
for (const currentId of queue) {
|
|
447
|
+
const neighbors = neighborStmt.all(currentId);
|
|
448
|
+
for (const n of neighbors) {
|
|
449
|
+
if (noTests && isTestFile(n.file))
|
|
450
|
+
continue;
|
|
451
|
+
if (n.id === targetId) {
|
|
452
|
+
if (!found) {
|
|
453
|
+
found = true;
|
|
454
|
+
parent.set(n.id, {
|
|
455
|
+
parentId: currentId,
|
|
456
|
+
edgeKind: n.edge_kind,
|
|
457
|
+
expression: n.expression,
|
|
458
|
+
});
|
|
459
|
+
}
|
|
460
|
+
continue;
|
|
461
|
+
}
|
|
462
|
+
if (!visited.has(n.id)) {
|
|
463
|
+
visited.add(n.id);
|
|
464
|
+
parent.set(n.id, {
|
|
465
|
+
parentId: currentId,
|
|
466
|
+
edgeKind: n.edge_kind,
|
|
467
|
+
expression: n.expression,
|
|
468
|
+
});
|
|
469
|
+
nextQueue.push(n.id);
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
if (found)
|
|
474
|
+
break;
|
|
475
|
+
queue = nextQueue;
|
|
476
|
+
if (queue.length === 0)
|
|
477
|
+
break;
|
|
478
|
+
}
|
|
479
|
+
return found ? parent : null;
|
|
480
|
+
}
|
|
481
|
+
/** Reconstruct a path from BFS parent map. */
|
|
482
|
+
function reconstructDataflowPath(db, parent, sourceId, targetId) {
|
|
483
|
+
const nodeById = db.prepare('SELECT * FROM nodes WHERE id = ?');
|
|
484
|
+
const hc = new Map();
|
|
485
|
+
const pathItems = [];
|
|
486
|
+
let cur = targetId;
|
|
487
|
+
while (cur !== undefined) {
|
|
488
|
+
const nodeRow = nodeById.get(cur);
|
|
489
|
+
const parentInfo = parent.get(cur);
|
|
490
|
+
pathItems.unshift({
|
|
491
|
+
...normalizeSymbol(nodeRow, db, hc),
|
|
492
|
+
edgeKind: parentInfo?.edgeKind ?? null,
|
|
493
|
+
expression: parentInfo?.expression ?? null,
|
|
494
|
+
});
|
|
495
|
+
cur = parentInfo?.parentId;
|
|
496
|
+
if (cur === sourceId) {
|
|
497
|
+
const srcRow = nodeById.get(cur);
|
|
498
|
+
pathItems.unshift({
|
|
499
|
+
...normalizeSymbol(srcRow, db, hc),
|
|
500
|
+
edgeKind: null,
|
|
501
|
+
expression: null,
|
|
502
|
+
});
|
|
503
|
+
break;
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
return pathItems;
|
|
507
|
+
}
|
|
269
508
|
export function dataflowPathData(from, to, customDbPath, opts = {}) {
|
|
270
509
|
const db = openReadonlyOrFail(customDbPath);
|
|
271
510
|
try {
|
|
@@ -292,90 +531,44 @@ export function dataflowPathData(from, to, customDbPath, opts = {}) {
|
|
|
292
531
|
if (sourceNode.id === targetNode.id) {
|
|
293
532
|
const hc = new Map();
|
|
294
533
|
const sym = normalizeSymbol(sourceNode, db, hc);
|
|
295
|
-
return {
|
|
296
|
-
from,
|
|
297
|
-
to,
|
|
298
|
-
found: true,
|
|
299
|
-
hops: 0,
|
|
300
|
-
path: [{ ...sym, edgeKind: null }],
|
|
301
|
-
};
|
|
302
|
-
}
|
|
303
|
-
// BFS through flows_to and returns edges
|
|
304
|
-
const neighborStmt = db.prepare(`SELECT n.id, n.name, n.kind, n.file, n.line, d.kind AS edge_kind, d.expression
|
|
305
|
-
FROM dataflow d JOIN nodes n ON d.target_id = n.id
|
|
306
|
-
WHERE d.source_id = ? AND d.kind IN ('flows_to', 'returns')`);
|
|
307
|
-
const visited = new Set([sourceNode.id]);
|
|
308
|
-
const parent = new Map();
|
|
309
|
-
let queue = [sourceNode.id];
|
|
310
|
-
let found = false;
|
|
311
|
-
for (let depth = 1; depth <= maxDepth; depth++) {
|
|
312
|
-
const nextQueue = [];
|
|
313
|
-
for (const currentId of queue) {
|
|
314
|
-
const neighbors = neighborStmt.all(currentId);
|
|
315
|
-
for (const n of neighbors) {
|
|
316
|
-
if (noTests && isTestFile(n.file))
|
|
317
|
-
continue;
|
|
318
|
-
if (n.id === targetNode.id) {
|
|
319
|
-
if (!found) {
|
|
320
|
-
found = true;
|
|
321
|
-
parent.set(n.id, {
|
|
322
|
-
parentId: currentId,
|
|
323
|
-
edgeKind: n.edge_kind,
|
|
324
|
-
expression: n.expression,
|
|
325
|
-
});
|
|
326
|
-
}
|
|
327
|
-
continue;
|
|
328
|
-
}
|
|
329
|
-
if (!visited.has(n.id)) {
|
|
330
|
-
visited.add(n.id);
|
|
331
|
-
parent.set(n.id, {
|
|
332
|
-
parentId: currentId,
|
|
333
|
-
edgeKind: n.edge_kind,
|
|
334
|
-
expression: n.expression,
|
|
335
|
-
});
|
|
336
|
-
nextQueue.push(n.id);
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
if (found)
|
|
341
|
-
break;
|
|
342
|
-
queue = nextQueue;
|
|
343
|
-
if (queue.length === 0)
|
|
344
|
-
break;
|
|
534
|
+
return { from, to, found: true, hops: 0, path: [{ ...sym, edgeKind: null }] };
|
|
345
535
|
}
|
|
346
|
-
|
|
536
|
+
const parent = bfsDataflowPath(db, sourceNode.id, targetNode.id, maxDepth, noTests);
|
|
537
|
+
if (!parent) {
|
|
347
538
|
return { from, to, found: false };
|
|
348
539
|
}
|
|
349
|
-
|
|
350
|
-
const nodeById = db.prepare('SELECT * FROM nodes WHERE id = ?');
|
|
351
|
-
const hc = new Map();
|
|
352
|
-
const pathItems = [];
|
|
353
|
-
let cur = targetNode.id;
|
|
354
|
-
while (cur !== undefined) {
|
|
355
|
-
const nodeRow = nodeById.get(cur);
|
|
356
|
-
const parentInfo = parent.get(cur);
|
|
357
|
-
pathItems.unshift({
|
|
358
|
-
...normalizeSymbol(nodeRow, db, hc),
|
|
359
|
-
edgeKind: parentInfo?.edgeKind ?? null,
|
|
360
|
-
expression: parentInfo?.expression ?? null,
|
|
361
|
-
});
|
|
362
|
-
cur = parentInfo?.parentId;
|
|
363
|
-
if (cur === sourceNode.id) {
|
|
364
|
-
const srcRow = nodeById.get(cur);
|
|
365
|
-
pathItems.unshift({
|
|
366
|
-
...normalizeSymbol(srcRow, db, hc),
|
|
367
|
-
edgeKind: null,
|
|
368
|
-
expression: null,
|
|
369
|
-
});
|
|
370
|
-
break;
|
|
371
|
-
}
|
|
372
|
-
}
|
|
540
|
+
const pathItems = reconstructDataflowPath(db, parent, sourceNode.id, targetNode.id);
|
|
373
541
|
return { from, to, found: true, hops: pathItems.length - 1, path: pathItems };
|
|
374
542
|
}
|
|
375
543
|
finally {
|
|
376
544
|
db.close();
|
|
377
545
|
}
|
|
378
546
|
}
|
|
547
|
+
/** BFS forward through return-value consumers to build impact levels. */
|
|
548
|
+
function bfsReturnConsumers(node, consumersStmt, db, hc, maxDepth, noTests) {
|
|
549
|
+
const visited = new Set([node.id]);
|
|
550
|
+
const levels = {};
|
|
551
|
+
let frontier = [node.id];
|
|
552
|
+
for (let d = 1; d <= maxDepth; d++) {
|
|
553
|
+
const nextFrontier = [];
|
|
554
|
+
for (const fid of frontier) {
|
|
555
|
+
const consumers = consumersStmt.all(fid);
|
|
556
|
+
for (const c of consumers) {
|
|
557
|
+
if (!visited.has(c.id) && (!noTests || !isTestFile(c.file))) {
|
|
558
|
+
visited.add(c.id);
|
|
559
|
+
nextFrontier.push(c.id);
|
|
560
|
+
if (!levels[d])
|
|
561
|
+
levels[d] = [];
|
|
562
|
+
levels[d].push(normalizeSymbol(c, db, hc));
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
frontier = nextFrontier;
|
|
567
|
+
if (frontier.length === 0)
|
|
568
|
+
break;
|
|
569
|
+
}
|
|
570
|
+
return { levels, totalAffected: visited.size - 1 };
|
|
571
|
+
}
|
|
379
572
|
export function dataflowImpactData(name, customDbPath, opts = {}) {
|
|
380
573
|
const db = openReadonlyOrFail(customDbPath);
|
|
381
574
|
try {
|
|
@@ -392,39 +585,14 @@ export function dataflowImpactData(name, customDbPath, opts = {}) {
|
|
|
392
585
|
if (nodes.length === 0) {
|
|
393
586
|
return { name, results: [] };
|
|
394
587
|
}
|
|
395
|
-
// Forward BFS: who consumes this function's return value (directly or transitively)?
|
|
396
588
|
const consumersStmt = db.prepare(`SELECT DISTINCT n.*
|
|
397
589
|
FROM dataflow d JOIN nodes n ON d.target_id = n.id
|
|
398
590
|
WHERE d.source_id = ? AND d.kind = 'returns'`);
|
|
399
591
|
const hc = new Map();
|
|
400
592
|
const results = nodes.map((node) => {
|
|
401
593
|
const sym = normalizeSymbol(node, db, hc);
|
|
402
|
-
const
|
|
403
|
-
|
|
404
|
-
let frontier = [node.id];
|
|
405
|
-
for (let d = 1; d <= maxDepth; d++) {
|
|
406
|
-
const nextFrontier = [];
|
|
407
|
-
for (const fid of frontier) {
|
|
408
|
-
const consumers = consumersStmt.all(fid);
|
|
409
|
-
for (const c of consumers) {
|
|
410
|
-
if (!visited.has(c.id) && (!noTests || !isTestFile(c.file))) {
|
|
411
|
-
visited.add(c.id);
|
|
412
|
-
nextFrontier.push(c.id);
|
|
413
|
-
if (!levels[d])
|
|
414
|
-
levels[d] = [];
|
|
415
|
-
levels[d].push(normalizeSymbol(c, db, hc));
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
frontier = nextFrontier;
|
|
420
|
-
if (frontier.length === 0)
|
|
421
|
-
break;
|
|
422
|
-
}
|
|
423
|
-
return {
|
|
424
|
-
...sym,
|
|
425
|
-
levels,
|
|
426
|
-
totalAffected: visited.size - 1,
|
|
427
|
-
};
|
|
594
|
+
const { levels, totalAffected } = bfsReturnConsumers(node, consumersStmt, db, hc, maxDepth, noTests);
|
|
595
|
+
return { ...sym, levels, totalAffected };
|
|
428
596
|
});
|
|
429
597
|
const base = { name, results };
|
|
430
598
|
return paginateResult(base, 'results', { limit: opts.limit, offset: opts.offset });
|