@optave/codegraph 3.8.0 → 3.9.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 +13 -8
- package/dist/ast-analysis/engine.d.ts.map +1 -1
- package/dist/ast-analysis/engine.js +137 -86
- package/dist/ast-analysis/engine.js.map +1 -1
- package/dist/ast-analysis/metrics.d.ts +0 -3
- package/dist/ast-analysis/metrics.d.ts.map +1 -1
- package/dist/ast-analysis/metrics.js +30 -13
- package/dist/ast-analysis/metrics.js.map +1 -1
- package/dist/ast-analysis/shared.d.ts.map +1 -1
- package/dist/ast-analysis/shared.js +24 -19
- package/dist/ast-analysis/shared.js.map +1 -1
- package/dist/ast-analysis/visitor-utils.d.ts.map +1 -1
- package/dist/ast-analysis/visitor-utils.js +55 -39
- package/dist/ast-analysis/visitor-utils.js.map +1 -1
- package/dist/ast-analysis/visitor.d.ts.map +1 -1
- package/dist/ast-analysis/visitor.js +91 -70
- package/dist/ast-analysis/visitor.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 +54 -58
- 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 +81 -39
- package/dist/ast-analysis/visitors/complexity-visitor.js.map +1 -1
- package/dist/ast-analysis/visitors/dataflow-visitor.d.ts.map +1 -1
- package/dist/ast-analysis/visitors/dataflow-visitor.js +57 -38
- package/dist/ast-analysis/visitors/dataflow-visitor.js.map +1 -1
- package/dist/cli/commands/branch-compare.d.ts.map +1 -1
- package/dist/cli/commands/branch-compare.js +4 -0
- package/dist/cli/commands/branch-compare.js.map +1 -1
- package/dist/cli/commands/diff-impact.d.ts.map +1 -1
- package/dist/cli/commands/diff-impact.js +2 -1
- package/dist/cli/commands/diff-impact.js.map +1 -1
- package/dist/cli/commands/info.d.ts.map +1 -1
- package/dist/cli/commands/info.js +3 -2
- package/dist/cli/commands/info.js.map +1 -1
- package/dist/cli/commands/watch.d.ts.map +1 -1
- package/dist/cli/commands/watch.js +16 -2
- package/dist/cli/commands/watch.js.map +1 -1
- package/dist/db/connection.d.ts.map +1 -1
- package/dist/db/connection.js +29 -26
- package/dist/db/connection.js.map +1 -1
- package/dist/db/query-builder.d.ts.map +1 -1
- package/dist/db/query-builder.js +16 -5
- package/dist/db/query-builder.js.map +1 -1
- package/dist/db/repository/base.d.ts +16 -0
- package/dist/db/repository/base.d.ts.map +1 -1
- package/dist/db/repository/base.js +31 -0
- package/dist/db/repository/base.js.map +1 -1
- package/dist/db/repository/native-repository.d.ts +7 -1
- package/dist/db/repository/native-repository.d.ts.map +1 -1
- package/dist/db/repository/native-repository.js +100 -1
- package/dist/db/repository/native-repository.js.map +1 -1
- package/dist/db/repository/nodes.d.ts.map +1 -1
- package/dist/db/repository/nodes.js +8 -4
- package/dist/db/repository/nodes.js.map +1 -1
- package/dist/db/repository/sqlite-repository.d.ts +4 -0
- package/dist/db/repository/sqlite-repository.d.ts.map +1 -1
- package/dist/db/repository/sqlite-repository.js +51 -0
- package/dist/db/repository/sqlite-repository.js.map +1 -1
- package/dist/domain/analysis/brief.d.ts.map +1 -1
- package/dist/domain/analysis/brief.js +13 -17
- package/dist/domain/analysis/brief.js.map +1 -1
- package/dist/domain/analysis/context.d.ts.map +1 -1
- package/dist/domain/analysis/context.js +14 -11
- 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 +64 -59
- package/dist/domain/analysis/dependencies.js.map +1 -1
- package/dist/domain/analysis/fn-impact.d.ts +2 -7
- package/dist/domain/analysis/fn-impact.d.ts.map +1 -1
- package/dist/domain/analysis/fn-impact.js +33 -31
- package/dist/domain/analysis/fn-impact.js.map +1 -1
- package/dist/domain/analysis/implementations.d.ts.map +1 -1
- package/dist/domain/analysis/implementations.js +11 -19
- package/dist/domain/analysis/implementations.js.map +1 -1
- package/dist/domain/analysis/module-map.d.ts.map +1 -1
- package/dist/domain/analysis/module-map.js +55 -76
- package/dist/domain/analysis/module-map.js.map +1 -1
- package/dist/domain/analysis/query-helpers.d.ts +7 -0
- package/dist/domain/analysis/query-helpers.d.ts.map +1 -1
- package/dist/domain/analysis/query-helpers.js +15 -1
- package/dist/domain/analysis/query-helpers.js.map +1 -1
- package/dist/domain/graph/builder/pipeline.d.ts.map +1 -1
- package/dist/domain/graph/builder/pipeline.js +352 -107
- 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 +49 -18
- package/dist/domain/graph/builder/stages/build-edges.js.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.js +2 -2
- 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 +32 -21
- 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 +95 -84
- package/dist/domain/graph/builder/stages/resolve-imports.js.map +1 -1
- package/dist/domain/graph/cycles.d.ts +6 -0
- package/dist/domain/graph/cycles.d.ts.map +1 -1
- package/dist/domain/graph/cycles.js +114 -22
- package/dist/domain/graph/cycles.js.map +1 -1
- package/dist/domain/graph/resolve.js +1 -1
- package/dist/domain/graph/resolve.js.map +1 -1
- package/dist/domain/graph/watcher.d.ts +2 -0
- package/dist/domain/graph/watcher.d.ts.map +1 -1
- package/dist/domain/graph/watcher.js +170 -75
- package/dist/domain/graph/watcher.js.map +1 -1
- package/dist/domain/parser.d.ts +3 -4
- package/dist/domain/parser.d.ts.map +1 -1
- package/dist/domain/parser.js +141 -89
- package/dist/domain/parser.js.map +1 -1
- package/dist/domain/search/generator.js +1 -1
- package/dist/domain/search/generator.js.map +1 -1
- package/dist/domain/search/models.d.ts +4 -3
- package/dist/domain/search/models.d.ts.map +1 -1
- package/dist/domain/search/models.js +23 -8
- package/dist/domain/search/models.js.map +1 -1
- package/dist/domain/search/search/hybrid.d.ts.map +1 -1
- package/dist/domain/search/search/hybrid.js +29 -18
- package/dist/domain/search/search/hybrid.js.map +1 -1
- package/dist/extractors/go.js +36 -33
- package/dist/extractors/go.js.map +1 -1
- package/dist/extractors/helpers.d.ts.map +1 -1
- package/dist/extractors/helpers.js +40 -29
- package/dist/extractors/helpers.js.map +1 -1
- package/dist/extractors/java.js +58 -46
- package/dist/extractors/java.js.map +1 -1
- package/dist/extractors/javascript.js +65 -54
- package/dist/extractors/javascript.js.map +1 -1
- package/dist/extractors/kotlin.js +84 -78
- package/dist/extractors/kotlin.js.map +1 -1
- package/dist/extractors/python.js +29 -24
- package/dist/extractors/python.js.map +1 -1
- package/dist/extractors/rust.js +41 -32
- package/dist/extractors/rust.js.map +1 -1
- package/dist/extractors/solidity.js +58 -67
- package/dist/extractors/solidity.js.map +1 -1
- package/dist/extractors/swift.js +83 -81
- package/dist/extractors/swift.js.map +1 -1
- package/dist/extractors/zig.js +58 -60
- package/dist/extractors/zig.js.map +1 -1
- package/dist/features/ast.d.ts +16 -14
- package/dist/features/ast.d.ts.map +1 -1
- package/dist/features/ast.js +83 -81
- package/dist/features/ast.js.map +1 -1
- package/dist/features/audit.d.ts.map +1 -1
- package/dist/features/audit.js +8 -6
- package/dist/features/audit.js.map +1 -1
- package/dist/features/branch-compare.d.ts.map +1 -1
- package/dist/features/branch-compare.js +69 -72
- package/dist/features/branch-compare.js.map +1 -1
- package/dist/features/communities.d.ts.map +1 -1
- package/dist/features/communities.js +19 -7
- package/dist/features/communities.js.map +1 -1
- package/dist/features/complexity.d.ts.map +1 -1
- package/dist/features/complexity.js +120 -125
- package/dist/features/complexity.js.map +1 -1
- package/dist/features/dataflow.d.ts.map +1 -1
- package/dist/features/dataflow.js +136 -137
- package/dist/features/dataflow.js.map +1 -1
- package/dist/features/flow.d.ts.map +1 -1
- package/dist/features/flow.js +84 -79
- package/dist/features/flow.js.map +1 -1
- package/dist/features/structure-query.d.ts.map +1 -1
- package/dist/features/structure-query.js +69 -65
- package/dist/features/structure-query.js.map +1 -1
- package/dist/graph/algorithms/leiden/optimiser.d.ts.map +1 -1
- package/dist/graph/algorithms/leiden/optimiser.js +70 -55
- 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 +288 -266
- package/dist/graph/algorithms/leiden/partition.js.map +1 -1
- package/dist/graph/model.d.ts.map +1 -1
- package/dist/graph/model.js +5 -1
- package/dist/graph/model.js.map +1 -1
- package/dist/infrastructure/config.d.ts.map +1 -1
- package/dist/infrastructure/config.js +6 -4
- package/dist/infrastructure/config.js.map +1 -1
- package/dist/infrastructure/suppress.d.ts +25 -0
- package/dist/infrastructure/suppress.d.ts.map +1 -0
- package/dist/infrastructure/suppress.js +43 -0
- package/dist/infrastructure/suppress.js.map +1 -0
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +29 -24
- package/dist/mcp/server.js.map +1 -1
- package/dist/presentation/dataflow.d.ts.map +1 -1
- package/dist/presentation/dataflow.js +47 -38
- package/dist/presentation/dataflow.js.map +1 -1
- package/dist/presentation/diff-impact-mermaid.d.ts.map +1 -1
- package/dist/presentation/diff-impact-mermaid.js +60 -51
- package/dist/presentation/diff-impact-mermaid.js.map +1 -1
- package/dist/presentation/queries-cli/exports.d.ts.map +1 -1
- package/dist/presentation/queries-cli/exports.js +20 -14
- 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 +15 -13
- package/dist/presentation/queries-cli/impact.js.map +1 -1
- package/dist/presentation/queries-cli/inspect.d.ts.map +1 -1
- package/dist/presentation/queries-cli/inspect.js +101 -79
- package/dist/presentation/queries-cli/inspect.js.map +1 -1
- package/dist/presentation/queries-cli/overview.d.ts.map +1 -1
- package/dist/presentation/queries-cli/overview.js +25 -16
- package/dist/presentation/queries-cli/overview.js.map +1 -1
- package/dist/presentation/queries-cli/path.js +26 -20
- package/dist/presentation/queries-cli/path.js.map +1 -1
- package/dist/presentation/result-formatter.d.ts +10 -0
- package/dist/presentation/result-formatter.d.ts.map +1 -1
- package/dist/presentation/result-formatter.js +16 -1
- package/dist/presentation/result-formatter.js.map +1 -1
- package/dist/presentation/viewer.d.ts.map +1 -1
- package/dist/presentation/viewer.js +18 -12
- package/dist/presentation/viewer.js.map +1 -1
- package/dist/shared/errors.d.ts +5 -0
- package/dist/shared/errors.d.ts.map +1 -1
- package/dist/shared/errors.js +5 -0
- package/dist/shared/errors.js.map +1 -1
- package/dist/shared/hierarchy.d.ts +8 -2
- package/dist/shared/hierarchy.d.ts.map +1 -1
- package/dist/shared/hierarchy.js +42 -1
- package/dist/shared/hierarchy.js.map +1 -1
- package/dist/shared/normalize.d.ts +6 -1
- package/dist/shared/normalize.d.ts.map +1 -1
- package/dist/shared/normalize.js +20 -12
- package/dist/shared/normalize.js.map +1 -1
- package/dist/shared/paginate.d.ts +0 -9
- package/dist/shared/paginate.d.ts.map +1 -1
- package/dist/shared/paginate.js +0 -15
- package/dist/shared/paginate.js.map +1 -1
- package/dist/types.d.ts +12 -5
- package/dist/types.d.ts.map +1 -1
- package/grammars/tree-sitter-erlang.wasm +0 -0
- package/grammars/tree-sitter-gleam.wasm +0 -0
- package/package.json +9 -9
- package/src/ast-analysis/engine.ts +176 -104
- package/src/ast-analysis/metrics.ts +33 -11
- package/src/ast-analysis/shared.ts +33 -24
- package/src/ast-analysis/visitor-utils.ts +52 -32
- package/src/ast-analysis/visitor.ts +132 -71
- package/src/ast-analysis/visitors/ast-store-visitor.ts +53 -50
- package/src/ast-analysis/visitors/complexity-visitor.ts +89 -40
- package/src/ast-analysis/visitors/dataflow-visitor.ts +87 -43
- package/src/cli/commands/branch-compare.ts +4 -0
- package/src/cli/commands/diff-impact.ts +2 -1
- package/src/cli/commands/info.ts +3 -2
- package/src/cli/commands/watch.ts +16 -2
- package/src/db/connection.ts +29 -28
- package/src/db/query-builder.ts +15 -3
- package/src/db/repository/base.ts +34 -0
- package/src/db/repository/native-repository.ts +104 -1
- package/src/db/repository/nodes.ts +13 -8
- package/src/db/repository/sqlite-repository.ts +55 -0
- package/src/domain/analysis/brief.ts +15 -25
- package/src/domain/analysis/context.ts +17 -10
- package/src/domain/analysis/dependencies.ts +77 -81
- package/src/domain/analysis/fn-impact.ts +36 -43
- package/src/domain/analysis/implementations.ts +11 -17
- package/src/domain/analysis/module-map.ts +58 -92
- package/src/domain/analysis/query-helpers.ts +18 -1
- package/src/domain/graph/builder/pipeline.ts +409 -99
- package/src/domain/graph/builder/stages/build-edges.ts +45 -19
- package/src/domain/graph/builder/stages/detect-changes.ts +2 -2
- package/src/domain/graph/builder/stages/finalize.ts +2 -2
- package/src/domain/graph/builder/stages/insert-nodes.ts +59 -34
- package/src/domain/graph/builder/stages/resolve-imports.ts +122 -100
- package/src/domain/graph/cycles.ts +110 -23
- package/src/domain/graph/resolve.ts +1 -1
- package/src/domain/graph/watcher.ts +202 -96
- package/src/domain/parser.ts +143 -89
- package/src/domain/search/generator.ts +1 -1
- package/src/domain/search/models.ts +26 -7
- package/src/domain/search/search/hybrid.ts +69 -51
- package/src/extractors/go.ts +43 -33
- package/src/extractors/helpers.ts +37 -23
- package/src/extractors/java.ts +66 -47
- package/src/extractors/javascript.ts +66 -54
- package/src/extractors/kotlin.ts +84 -77
- package/src/extractors/python.ts +31 -25
- package/src/extractors/rust.ts +37 -29
- package/src/extractors/solidity.ts +57 -61
- package/src/extractors/swift.ts +81 -80
- package/src/extractors/zig.ts +58 -61
- package/src/features/ast.ts +130 -110
- package/src/features/audit.ts +8 -6
- package/src/features/branch-compare.ts +105 -79
- package/src/features/communities.ts +25 -10
- package/src/features/complexity.ts +171 -134
- package/src/features/dataflow.ts +165 -175
- package/src/features/flow.ts +129 -92
- package/src/features/structure-query.ts +79 -64
- package/src/graph/algorithms/leiden/optimiser.ts +99 -55
- package/src/graph/algorithms/leiden/partition.ts +359 -294
- package/src/graph/model.ts +6 -1
- package/src/infrastructure/config.ts +6 -4
- package/src/infrastructure/suppress.ts +47 -0
- package/src/mcp/server.ts +53 -37
- package/src/presentation/dataflow.ts +50 -44
- package/src/presentation/diff-impact-mermaid.ts +104 -62
- package/src/presentation/queries-cli/exports.ts +21 -13
- package/src/presentation/queries-cli/impact.ts +15 -13
- package/src/presentation/queries-cli/inspect.ts +100 -81
- package/src/presentation/queries-cli/overview.ts +26 -16
- package/src/presentation/queries-cli/path.ts +33 -25
- package/src/presentation/result-formatter.ts +19 -1
- package/src/presentation/viewer.ts +42 -14
- package/src/shared/errors.ts +6 -0
- package/src/shared/hierarchy.ts +50 -2
- package/src/shared/normalize.ts +31 -12
- package/src/shared/paginate.ts +0 -17
- package/src/types.ts +26 -5
|
@@ -68,6 +68,67 @@ interface DirectoryEntry {
|
|
|
68
68
|
subdirectories: string[];
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
+
function buildDirectoryEntry(
|
|
72
|
+
d: DirRow,
|
|
73
|
+
filesStmt: { all(...params: unknown[]): unknown[] },
|
|
74
|
+
subdirsStmt: { all(...params: unknown[]): unknown[] },
|
|
75
|
+
noTests: boolean,
|
|
76
|
+
): DirectoryEntry {
|
|
77
|
+
let files = filesStmt.all(d.id) as FileMetricRow[];
|
|
78
|
+
if (noTests) files = files.filter((f) => !isTestFile(f.name));
|
|
79
|
+
|
|
80
|
+
const subdirs = subdirsStmt.all(d.id) as { name: string }[];
|
|
81
|
+
|
|
82
|
+
const fileCount = noTests ? files.length : d.file_count || 0;
|
|
83
|
+
return {
|
|
84
|
+
directory: d.name,
|
|
85
|
+
fileCount,
|
|
86
|
+
symbolCount: d.symbol_count || 0,
|
|
87
|
+
fanIn: d.fan_in || 0,
|
|
88
|
+
fanOut: d.fan_out || 0,
|
|
89
|
+
cohesion: d.cohesion,
|
|
90
|
+
density: fileCount > 0 ? (d.symbol_count || 0) / fileCount : 0,
|
|
91
|
+
files: files.map((f) => ({
|
|
92
|
+
file: f.name,
|
|
93
|
+
lineCount: f.line_count || 0,
|
|
94
|
+
symbolCount: f.symbol_count || 0,
|
|
95
|
+
importCount: f.import_count || 0,
|
|
96
|
+
exportCount: f.export_count || 0,
|
|
97
|
+
fanIn: f.fan_in || 0,
|
|
98
|
+
fanOut: f.fan_out || 0,
|
|
99
|
+
})),
|
|
100
|
+
subdirectories: subdirs.map((s) => s.name),
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function applyFileLimit(
|
|
105
|
+
result: DirectoryEntry[],
|
|
106
|
+
fileLimit: number,
|
|
107
|
+
): { directories: DirectoryEntry[]; count: number; suppressed: number; warning: string } | null {
|
|
108
|
+
const totalFiles = result.reduce((sum, d) => sum + d.files.length, 0);
|
|
109
|
+
if (totalFiles <= fileLimit) return null;
|
|
110
|
+
|
|
111
|
+
let shown = 0;
|
|
112
|
+
for (const d of result) {
|
|
113
|
+
const remaining = fileLimit - shown;
|
|
114
|
+
if (remaining <= 0) {
|
|
115
|
+
d.files = [];
|
|
116
|
+
} else if (d.files.length > remaining) {
|
|
117
|
+
d.files = d.files.slice(0, remaining);
|
|
118
|
+
shown = fileLimit;
|
|
119
|
+
} else {
|
|
120
|
+
shown += d.files.length;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
const suppressed = totalFiles - fileLimit;
|
|
124
|
+
return {
|
|
125
|
+
directories: result,
|
|
126
|
+
count: result.length,
|
|
127
|
+
suppressed,
|
|
128
|
+
warning: `${suppressed} files omitted (showing ${fileLimit}/${totalFiles}). Use --full to show all files, or narrow with --directory.`,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
71
132
|
export function structureData(
|
|
72
133
|
customDbPath?: string,
|
|
73
134
|
opts: StructureDataOpts = {},
|
|
@@ -115,73 +176,27 @@ export function structureData(
|
|
|
115
176
|
dirs.sort(sortFn);
|
|
116
177
|
|
|
117
178
|
// Get file metrics for each directory
|
|
118
|
-
const
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
JOIN nodes n ON e.target_id = n.id
|
|
135
|
-
WHERE e.source_id = ? AND e.kind = 'contains' AND n.kind = 'directory'
|
|
136
|
-
`)
|
|
137
|
-
.all(d.id) as { name: string }[];
|
|
138
|
-
|
|
139
|
-
const fileCount = noTests ? files.length : d.file_count || 0;
|
|
140
|
-
return {
|
|
141
|
-
directory: d.name,
|
|
142
|
-
fileCount,
|
|
143
|
-
symbolCount: d.symbol_count || 0,
|
|
144
|
-
fanIn: d.fan_in || 0,
|
|
145
|
-
fanOut: d.fan_out || 0,
|
|
146
|
-
cohesion: d.cohesion,
|
|
147
|
-
density: fileCount > 0 ? (d.symbol_count || 0) / fileCount : 0,
|
|
148
|
-
files: files.map((f) => ({
|
|
149
|
-
file: f.name,
|
|
150
|
-
lineCount: f.line_count || 0,
|
|
151
|
-
symbolCount: f.symbol_count || 0,
|
|
152
|
-
importCount: f.import_count || 0,
|
|
153
|
-
exportCount: f.export_count || 0,
|
|
154
|
-
fanIn: f.fan_in || 0,
|
|
155
|
-
fanOut: f.fan_out || 0,
|
|
156
|
-
})),
|
|
157
|
-
subdirectories: subdirs.map((s) => s.name),
|
|
158
|
-
};
|
|
159
|
-
});
|
|
179
|
+
const filesStmt = db.prepare(`
|
|
180
|
+
SELECT n.name, nm.line_count, nm.symbol_count, nm.import_count, nm.export_count, nm.fan_in, nm.fan_out
|
|
181
|
+
FROM edges e
|
|
182
|
+
JOIN nodes n ON e.target_id = n.id
|
|
183
|
+
LEFT JOIN node_metrics nm ON n.id = nm.node_id
|
|
184
|
+
WHERE e.source_id = ? AND e.kind = 'contains' AND n.kind = 'file'
|
|
185
|
+
`);
|
|
186
|
+
const subdirsStmt = db.prepare(`
|
|
187
|
+
SELECT n.name
|
|
188
|
+
FROM edges e
|
|
189
|
+
JOIN nodes n ON e.target_id = n.id
|
|
190
|
+
WHERE e.source_id = ? AND e.kind = 'contains' AND n.kind = 'directory'
|
|
191
|
+
`);
|
|
192
|
+
const result: DirectoryEntry[] = dirs.map((d) =>
|
|
193
|
+
buildDirectoryEntry(d, filesStmt, subdirsStmt, noTests),
|
|
194
|
+
);
|
|
160
195
|
|
|
161
196
|
// Apply global file limit unless full mode
|
|
162
197
|
if (!full) {
|
|
163
|
-
const
|
|
164
|
-
if (
|
|
165
|
-
let shown = 0;
|
|
166
|
-
for (const d of result) {
|
|
167
|
-
const remaining = fileLimit - shown;
|
|
168
|
-
if (remaining <= 0) {
|
|
169
|
-
d.files = [];
|
|
170
|
-
} else if (d.files.length > remaining) {
|
|
171
|
-
d.files = d.files.slice(0, remaining);
|
|
172
|
-
shown = fileLimit;
|
|
173
|
-
} else {
|
|
174
|
-
shown += d.files.length;
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
const suppressed = totalFiles - fileLimit;
|
|
178
|
-
return {
|
|
179
|
-
directories: result,
|
|
180
|
-
count: result.length,
|
|
181
|
-
suppressed,
|
|
182
|
-
warning: `${suppressed} files omitted (showing ${fileLimit}/${totalFiles}). Use --full to show all files, or narrow with --directory.`,
|
|
183
|
-
};
|
|
184
|
-
}
|
|
198
|
+
const limited = applyFileLimit(result, fileLimit);
|
|
199
|
+
if (limited) return limited;
|
|
185
200
|
}
|
|
186
201
|
|
|
187
202
|
const base = { directories: result, count: result.length };
|
|
@@ -18,6 +18,13 @@ const DEFAULT_MAX_LEVELS: number = 50;
|
|
|
18
18
|
const DEFAULT_MAX_LOCAL_PASSES: number = 20;
|
|
19
19
|
const GAIN_EPSILON: number = 1e-12;
|
|
20
20
|
|
|
21
|
+
/** Pre-allocated scratch buffers for refinement candidate collection. */
|
|
22
|
+
interface RefinementScratch {
|
|
23
|
+
candC: Int32Array;
|
|
24
|
+
candGain: Float64Array;
|
|
25
|
+
candWeight: Float64Array;
|
|
26
|
+
}
|
|
27
|
+
|
|
21
28
|
const CandidateStrategy = {
|
|
22
29
|
Neighbors: 0,
|
|
23
30
|
All: 1,
|
|
@@ -335,6 +342,79 @@ function buildCoarseGraph(g: GraphAdapter, p: Partition): CodeGraph {
|
|
|
335
342
|
return coarse;
|
|
336
343
|
}
|
|
337
344
|
|
|
345
|
+
/**
|
|
346
|
+
* Collect eligible candidate communities for node `v` during refinement.
|
|
347
|
+
* A candidate must: (a) be in the same macro-community, (b) respect the size
|
|
348
|
+
* limit, and (c) produce a positive quality gain above GAIN_EPSILON.
|
|
349
|
+
* Returns the number of collected candidates written into `scratch`.
|
|
350
|
+
*/
|
|
351
|
+
function collectRefinementCandidates(
|
|
352
|
+
p: Partition,
|
|
353
|
+
g: GraphAdapter,
|
|
354
|
+
v: number,
|
|
355
|
+
touchedCount: number,
|
|
356
|
+
macroV: number,
|
|
357
|
+
commMacro: Int32Array,
|
|
358
|
+
maxSize: number,
|
|
359
|
+
opts: NormalizedOptions,
|
|
360
|
+
scratch: RefinementScratch,
|
|
361
|
+
): number {
|
|
362
|
+
let candLen: number = 0;
|
|
363
|
+
for (let t = 0; t < touchedCount; t++) {
|
|
364
|
+
const c: number = p.getCandidateCommunityAt(t);
|
|
365
|
+
if (c === p.nodeCommunity[v]!) continue;
|
|
366
|
+
if (commMacro[c]! !== macroV) continue;
|
|
367
|
+
if (maxSize < Infinity) {
|
|
368
|
+
const nextSize: number = p.getCommunityTotalSize(c) + g.size[v]!;
|
|
369
|
+
if (nextSize > maxSize) continue;
|
|
370
|
+
}
|
|
371
|
+
const gain: number = computeQualityGain(p, v, c, opts);
|
|
372
|
+
if (gain > GAIN_EPSILON) {
|
|
373
|
+
scratch.candC[candLen] = c;
|
|
374
|
+
scratch.candGain[candLen] = gain;
|
|
375
|
+
candLen++;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
return candLen;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* Boltzmann probabilistic selection from collected candidates (Algorithm 3).
|
|
383
|
+
* Returns the chosen community ID, or -1 if the node should stay as singleton.
|
|
384
|
+
*
|
|
385
|
+
* p(v, C) is proportional to exp(deltaH / theta), with the "stay as singleton"
|
|
386
|
+
* option (deltaH = 0) included. For numerical stability, the max gain is
|
|
387
|
+
* subtracted before exponentiation.
|
|
388
|
+
*/
|
|
389
|
+
function boltzmannSelectCandidate(
|
|
390
|
+
candLen: number,
|
|
391
|
+
theta: number,
|
|
392
|
+
rng: () => number,
|
|
393
|
+
scratch: RefinementScratch,
|
|
394
|
+
): number {
|
|
395
|
+
let maxGain: number = 0;
|
|
396
|
+
for (let i = 0; i < candLen; i++) {
|
|
397
|
+
if (scratch.candGain[i]! > maxGain) maxGain = scratch.candGain[i]!;
|
|
398
|
+
}
|
|
399
|
+
// "Stay as singleton" weight: exp((0 - maxGain) / theta)
|
|
400
|
+
const stayWeight: number = Math.exp((0 - maxGain) / theta);
|
|
401
|
+
let totalWeight: number = stayWeight;
|
|
402
|
+
for (let i = 0; i < candLen; i++) {
|
|
403
|
+
scratch.candWeight[i] = Math.exp((scratch.candGain[i]! - maxGain) / theta);
|
|
404
|
+
totalWeight += scratch.candWeight[i]!;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
const r: number = rng() * totalWeight;
|
|
408
|
+
if (r < stayWeight) return -1; // node stays as singleton
|
|
409
|
+
|
|
410
|
+
let cumulative: number = stayWeight;
|
|
411
|
+
for (let i = 0; i < candLen; i++) {
|
|
412
|
+
cumulative += scratch.candWeight[i]!;
|
|
413
|
+
if (r < cumulative) return scratch.candC[i]!;
|
|
414
|
+
}
|
|
415
|
+
return scratch.candC[candLen - 1]!; // fallback
|
|
416
|
+
}
|
|
417
|
+
|
|
338
418
|
/**
|
|
339
419
|
* True Leiden refinement phase (Algorithm 3, Traag et al. 2019).
|
|
340
420
|
*
|
|
@@ -380,10 +460,12 @@ function refineWithinCoarseCommunities(
|
|
|
380
460
|
shuffleArrayInPlace(order, rng);
|
|
381
461
|
|
|
382
462
|
// Pre-allocate flat arrays for candidate collection to avoid per-node GC pressure.
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
463
|
+
const scratch: RefinementScratch = {
|
|
464
|
+
candC: new Int32Array(g.n),
|
|
465
|
+
candGain: new Float64Array(g.n),
|
|
466
|
+
candWeight: new Float64Array(g.n),
|
|
467
|
+
};
|
|
468
|
+
const maxSize: number = Number.isFinite(opts.maxCommunitySize) ? opts.maxCommunitySize : Infinity;
|
|
387
469
|
|
|
388
470
|
for (let idx = 0; idx < order.length; idx++) {
|
|
389
471
|
const v: number = order[idx]!;
|
|
@@ -394,59 +476,21 @@ function refineWithinCoarseCommunities(
|
|
|
394
476
|
|
|
395
477
|
const macroV: number = macro[v]!;
|
|
396
478
|
const touchedCount: number = p.accumulateNeighborCommunityEdgeWeights(v);
|
|
397
|
-
const
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
const nextSize: number = p.getCommunityTotalSize(c) + g.size[v]!;
|
|
409
|
-
if (nextSize > maxSize) continue;
|
|
410
|
-
}
|
|
411
|
-
const gain: number = computeQualityGain(p, v, c, opts);
|
|
412
|
-
if (gain > GAIN_EPSILON) {
|
|
413
|
-
candC[candLen] = c;
|
|
414
|
-
candGain[candLen] = gain;
|
|
415
|
-
candLen++;
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
|
|
479
|
+
const candLen: number = collectRefinementCandidates(
|
|
480
|
+
p,
|
|
481
|
+
g,
|
|
482
|
+
v,
|
|
483
|
+
touchedCount,
|
|
484
|
+
macroV,
|
|
485
|
+
commMacro,
|
|
486
|
+
maxSize,
|
|
487
|
+
opts,
|
|
488
|
+
scratch,
|
|
489
|
+
);
|
|
419
490
|
if (candLen === 0) continue;
|
|
420
491
|
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
// For numerical stability, subtract the max gain before exponentiation.
|
|
424
|
-
let maxGain: number = 0;
|
|
425
|
-
for (let i = 0; i < candLen; i++) {
|
|
426
|
-
if (candGain[i]! > maxGain) maxGain = candGain[i]!;
|
|
427
|
-
}
|
|
428
|
-
// "Stay as singleton" weight: exp((0 - maxGain) / theta)
|
|
429
|
-
const stayWeight: number = Math.exp((0 - maxGain) / theta);
|
|
430
|
-
let totalWeight: number = stayWeight;
|
|
431
|
-
for (let i = 0; i < candLen; i++) {
|
|
432
|
-
candWeight[i] = Math.exp((candGain[i]! - maxGain) / theta);
|
|
433
|
-
totalWeight += candWeight[i]!;
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
const r: number = rng() * totalWeight;
|
|
437
|
-
if (r < stayWeight) continue; // node stays as singleton
|
|
438
|
-
|
|
439
|
-
let cumulative: number = stayWeight;
|
|
440
|
-
let chosenC: number = candC[candLen - 1]!; // fallback
|
|
441
|
-
for (let i = 0; i < candLen; i++) {
|
|
442
|
-
cumulative += candWeight[i]!;
|
|
443
|
-
if (r < cumulative) {
|
|
444
|
-
chosenC = candC[i]!;
|
|
445
|
-
break;
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
p.moveNodeToCommunity(v, chosenC);
|
|
492
|
+
const chosenC: number = boltzmannSelectCandidate(candLen, theta, rng, scratch);
|
|
493
|
+
if (chosenC >= 0) p.moveNodeToCommunity(v, chosenC);
|
|
450
494
|
}
|
|
451
495
|
return p;
|
|
452
496
|
}
|