@optave/codegraph 3.8.1 → 3.9.1
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 +12 -7
- package/dist/ast-analysis/engine.d.ts.map +1 -1
- package/dist/ast-analysis/engine.js +121 -48
- 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 +15 -18
- 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 +50 -1
- package/dist/ast-analysis/visitors/complexity-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/db/connection.d.ts +1 -0
- package/dist/db/connection.d.ts.map +1 -1
- package/dist/db/connection.js +22 -4
- package/dist/db/connection.js.map +1 -1
- package/dist/db/repository/base.d.ts +41 -0
- package/dist/db/repository/base.d.ts.map +1 -1
- package/dist/db/repository/base.js +22 -0
- package/dist/db/repository/base.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.map +1 -1
- package/dist/db/repository/native-repository.d.ts +8 -1
- package/dist/db/repository/native-repository.d.ts.map +1 -1
- package/dist/db/repository/native-repository.js +69 -1
- package/dist/db/repository/native-repository.js.map +1 -1
- package/dist/db/repository/sqlite-repository.d.ts +1 -0
- package/dist/db/repository/sqlite-repository.d.ts.map +1 -1
- package/dist/db/repository/sqlite-repository.js +25 -0
- package/dist/db/repository/sqlite-repository.js.map +1 -1
- package/dist/domain/analysis/dependencies.d.ts +1 -28
- package/dist/domain/analysis/dependencies.d.ts.map +1 -1
- package/dist/domain/analysis/dependencies.js +24 -8
- package/dist/domain/analysis/dependencies.js.map +1 -1
- package/dist/domain/graph/builder/incremental.d.ts.map +1 -1
- package/dist/domain/graph/builder/incremental.js +18 -0
- 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 +298 -206
- 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 +56 -3
- package/dist/domain/graph/builder/stages/build-edges.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 +19 -23
- package/dist/domain/graph/builder/stages/resolve-imports.js.map +1 -1
- package/dist/domain/graph/watcher.d.ts.map +1 -1
- package/dist/domain/graph/watcher.js +99 -95
- package/dist/domain/graph/watcher.js.map +1 -1
- package/dist/domain/parser.d.ts +4 -0
- package/dist/domain/parser.d.ts.map +1 -1
- package/dist/domain/parser.js +130 -61
- package/dist/domain/parser.js.map +1 -1
- package/dist/domain/search/models.d.ts.map +1 -1
- package/dist/domain/search/models.js +7 -5
- package/dist/domain/search/models.js.map +1 -1
- package/dist/extractors/go.js +53 -35
- package/dist/extractors/go.js.map +1 -1
- package/dist/extractors/javascript.js +85 -36
- package/dist/extractors/javascript.js.map +1 -1
- package/dist/features/complexity.d.ts.map +1 -1
- package/dist/features/complexity.js +78 -58
- package/dist/features/complexity.js.map +1 -1
- package/dist/features/dataflow.d.ts.map +1 -1
- package/dist/features/dataflow.js +109 -118
- package/dist/features/dataflow.js.map +1 -1
- package/dist/features/structure.d.ts.map +1 -1
- package/dist/features/structure.js +147 -97
- package/dist/features/structure.js.map +1 -1
- package/dist/graph/algorithms/louvain.d.ts.map +1 -1
- package/dist/graph/algorithms/louvain.js +4 -2
- package/dist/graph/algorithms/louvain.js.map +1 -1
- package/dist/graph/classifiers/roles.d.ts +2 -0
- package/dist/graph/classifiers/roles.d.ts.map +1 -1
- package/dist/graph/classifiers/roles.js +13 -5
- package/dist/graph/classifiers/roles.js.map +1 -1
- package/dist/presentation/communities.d.ts.map +1 -1
- package/dist/presentation/communities.js +38 -34
- package/dist/presentation/communities.js.map +1 -1
- package/dist/presentation/manifesto.d.ts.map +1 -1
- package/dist/presentation/manifesto.js +31 -33
- package/dist/presentation/manifesto.js.map +1 -1
- package/dist/presentation/queries-cli/inspect.d.ts.map +1 -1
- package/dist/presentation/queries-cli/inspect.js +47 -46
- package/dist/presentation/queries-cli/inspect.js.map +1 -1
- package/dist/shared/file-utils.d.ts.map +1 -1
- package/dist/shared/file-utils.js +94 -72
- package/dist/shared/file-utils.js.map +1 -1
- package/dist/types.d.ts +83 -2
- 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 +150 -55
- package/src/ast-analysis/visitors/ast-store-visitor.ts +19 -21
- package/src/ast-analysis/visitors/complexity-visitor.ts +55 -1
- 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/db/connection.ts +24 -5
- package/src/db/repository/base.ts +57 -0
- package/src/db/repository/index.ts +1 -0
- package/src/db/repository/native-repository.ts +92 -1
- package/src/db/repository/sqlite-repository.ts +26 -0
- package/src/domain/analysis/dependencies.ts +24 -6
- package/src/domain/graph/builder/incremental.ts +21 -0
- package/src/domain/graph/builder/pipeline.ts +396 -245
- package/src/domain/graph/builder/stages/build-edges.ts +53 -2
- package/src/domain/graph/builder/stages/resolve-imports.ts +20 -20
- package/src/domain/graph/watcher.ts +118 -98
- package/src/domain/parser.ts +131 -63
- package/src/domain/search/models.ts +11 -5
- package/src/extractors/go.ts +57 -32
- package/src/extractors/javascript.ts +88 -35
- package/src/features/complexity.ts +94 -58
- package/src/features/dataflow.ts +153 -132
- package/src/features/structure.ts +167 -95
- package/src/graph/algorithms/louvain.ts +5 -2
- package/src/graph/classifiers/roles.ts +14 -5
- package/src/presentation/communities.ts +44 -39
- package/src/presentation/manifesto.ts +35 -38
- package/src/presentation/queries-cli/inspect.ts +48 -46
- package/src/shared/file-utils.ts +116 -77
- package/src/types.ts +87 -1
|
@@ -182,6 +182,39 @@ interface InterfacesData {
|
|
|
182
182
|
results: InterfacesResult[];
|
|
183
183
|
}
|
|
184
184
|
|
|
185
|
+
function renderWhereSymbolResults(results: WhereSymbolResult[]): void {
|
|
186
|
+
for (const r of results) {
|
|
187
|
+
const roleTag = r.role ? ` [${r.role}]` : '';
|
|
188
|
+
const tag = r.exported ? ' (exported)' : '';
|
|
189
|
+
console.log(`\n${kindIcon(r.kind)} ${r.name}${roleTag} ${r.file}:${r.line}${tag}`);
|
|
190
|
+
if (r.uses.length > 0) {
|
|
191
|
+
const useStrs = r.uses.map((u) => `${u.file}:${u.line}`);
|
|
192
|
+
console.log(` Used in: ${useStrs.join(', ')}`);
|
|
193
|
+
} else {
|
|
194
|
+
console.log(' No uses found');
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function renderWhereFileResults(results: WhereFileResult[]): void {
|
|
200
|
+
for (const r of results) {
|
|
201
|
+
console.log(`\n# ${r.file}`);
|
|
202
|
+
if (r.symbols.length > 0) {
|
|
203
|
+
const symStrs = r.symbols.map((s) => `${s.name}:${s.line}`);
|
|
204
|
+
console.log(` Symbols: ${symStrs.join(', ')}`);
|
|
205
|
+
}
|
|
206
|
+
if (r.imports.length > 0) {
|
|
207
|
+
console.log(` Imports: ${r.imports.join(', ')}`);
|
|
208
|
+
}
|
|
209
|
+
if (r.importedBy.length > 0) {
|
|
210
|
+
console.log(` Imported by: ${r.importedBy.join(', ')}`);
|
|
211
|
+
}
|
|
212
|
+
if (r.exported.length > 0) {
|
|
213
|
+
console.log(` Exported: ${r.exported.join(', ')}`);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
185
218
|
export function where(target: string, customDbPath: string, opts: OutputOpts = {}): void {
|
|
186
219
|
const data = whereData(target, customDbPath, opts as Record<string, unknown>) as WhereData;
|
|
187
220
|
if (outputResult(data as unknown as Record<string, unknown>, 'results', opts)) return;
|
|
@@ -196,34 +229,9 @@ export function where(target: string, customDbPath: string, opts: OutputOpts = {
|
|
|
196
229
|
}
|
|
197
230
|
|
|
198
231
|
if (data.mode === 'symbol') {
|
|
199
|
-
|
|
200
|
-
const roleTag = r.role ? ` [${r.role}]` : '';
|
|
201
|
-
const tag = r.exported ? ' (exported)' : '';
|
|
202
|
-
console.log(`\n${kindIcon(r.kind)} ${r.name}${roleTag} ${r.file}:${r.line}${tag}`);
|
|
203
|
-
if (r.uses.length > 0) {
|
|
204
|
-
const useStrs = r.uses.map((u) => `${u.file}:${u.line}`);
|
|
205
|
-
console.log(` Used in: ${useStrs.join(', ')}`);
|
|
206
|
-
} else {
|
|
207
|
-
console.log(' No uses found');
|
|
208
|
-
}
|
|
209
|
-
}
|
|
232
|
+
renderWhereSymbolResults(data.results as WhereSymbolResult[]);
|
|
210
233
|
} else {
|
|
211
|
-
|
|
212
|
-
console.log(`\n# ${r.file}`);
|
|
213
|
-
if (r.symbols.length > 0) {
|
|
214
|
-
const symStrs = r.symbols.map((s) => `${s.name}:${s.line}`);
|
|
215
|
-
console.log(` Symbols: ${symStrs.join(', ')}`);
|
|
216
|
-
}
|
|
217
|
-
if (r.imports.length > 0) {
|
|
218
|
-
console.log(` Imports: ${r.imports.join(', ')}`);
|
|
219
|
-
}
|
|
220
|
-
if (r.importedBy.length > 0) {
|
|
221
|
-
console.log(` Imported by: ${r.importedBy.join(', ')}`);
|
|
222
|
-
}
|
|
223
|
-
if (r.exported.length > 0) {
|
|
224
|
-
console.log(` Exported: ${r.exported.join(', ')}`);
|
|
225
|
-
}
|
|
226
|
-
}
|
|
234
|
+
renderWhereFileResults(data.results as WhereFileResult[]);
|
|
227
235
|
}
|
|
228
236
|
console.log();
|
|
229
237
|
}
|
|
@@ -402,6 +410,17 @@ function renderContextResult(r: ContextResult): void {
|
|
|
402
410
|
}
|
|
403
411
|
}
|
|
404
412
|
|
|
413
|
+
function renderExplainSymbolList(label: string, symbols: ExplainSymbol[]): void {
|
|
414
|
+
if (symbols.length === 0) return;
|
|
415
|
+
console.log(`\n## ${label}`);
|
|
416
|
+
for (const s of symbols) {
|
|
417
|
+
const sig = s.signature?.params != null ? `(${s.signature.params})` : '';
|
|
418
|
+
const roleTag = s.role ? ` [${s.role}]` : '';
|
|
419
|
+
const summary = s.summary ? ` -- ${s.summary}` : '';
|
|
420
|
+
console.log(` ${kindIcon(s.kind)} ${s.name}${sig}${roleTag} :${s.line}${summary}`);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
405
424
|
function renderFileExplain(r: FileExplainResult): void {
|
|
406
425
|
const publicCount = r.publicApi.length;
|
|
407
426
|
const internalCount = r.internal.length;
|
|
@@ -418,25 +437,8 @@ function renderFileExplain(r: FileExplainResult): void {
|
|
|
418
437
|
console.log(` Imported by: ${r.importedBy.map((i) => i.file).join(', ')}`);
|
|
419
438
|
}
|
|
420
439
|
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
for (const s of r.publicApi) {
|
|
424
|
-
const sig = s.signature?.params != null ? `(${s.signature.params})` : '';
|
|
425
|
-
const roleTag = s.role ? ` [${s.role}]` : '';
|
|
426
|
-
const summary = s.summary ? ` -- ${s.summary}` : '';
|
|
427
|
-
console.log(` ${kindIcon(s.kind)} ${s.name}${sig}${roleTag} :${s.line}${summary}`);
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
if (r.internal.length > 0) {
|
|
432
|
-
console.log(`\n## Internal`);
|
|
433
|
-
for (const s of r.internal) {
|
|
434
|
-
const sig = s.signature?.params != null ? `(${s.signature.params})` : '';
|
|
435
|
-
const roleTag = s.role ? ` [${s.role}]` : '';
|
|
436
|
-
const summary = s.summary ? ` -- ${s.summary}` : '';
|
|
437
|
-
console.log(` ${kindIcon(s.kind)} ${s.name}${sig}${roleTag} :${s.line}${summary}`);
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
+
renderExplainSymbolList('Exported', r.publicApi);
|
|
441
|
+
renderExplainSymbolList('Internal', r.internal);
|
|
440
442
|
|
|
441
443
|
if (r.dataFlow.length > 0) {
|
|
442
444
|
console.log(`\n## Data Flow`);
|
package/src/shared/file-utils.ts
CHANGED
|
@@ -45,56 +45,97 @@ interface ExtractSummaryOpts {
|
|
|
45
45
|
summaryMaxChars?: number;
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
48
|
+
/** Truncate text to maxChars, appending "..." if truncated. */
|
|
49
|
+
function truncate(text: string, maxChars: number): string {
|
|
50
|
+
return text.length > maxChars ? `${text.slice(0, maxChars)}...` : text;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Try to extract a single-line comment (// or #) above the definition. */
|
|
54
|
+
function extractSingleLineComment(
|
|
55
|
+
fileLines: string[],
|
|
56
|
+
idx: number,
|
|
57
|
+
scanLines: number,
|
|
58
|
+
maxChars: number,
|
|
52
59
|
): string | null {
|
|
53
|
-
|
|
54
|
-
const idx = line - 2; // line above the definition (0-indexed)
|
|
55
|
-
const jsdocEndScanLines = opts.jsdocEndScanLines ?? 10;
|
|
56
|
-
const jsdocOpenScanLines = opts.jsdocOpenScanLines ?? 20;
|
|
57
|
-
const summaryMaxChars = opts.summaryMaxChars ?? 100;
|
|
58
|
-
// Scan up for JSDoc or comment
|
|
59
|
-
let jsdocEnd = -1;
|
|
60
|
-
for (let i = idx; i >= Math.max(0, idx - jsdocEndScanLines); i--) {
|
|
60
|
+
for (let i = idx; i >= Math.max(0, idx - scanLines); i--) {
|
|
61
61
|
const trimmed = fileLines[i]!.trim();
|
|
62
|
-
if (trimmed.endsWith('*/'))
|
|
63
|
-
jsdocEnd = i;
|
|
64
|
-
break;
|
|
65
|
-
}
|
|
62
|
+
if (trimmed.endsWith('*/')) return null; // hit a block comment — defer to JSDoc extractor
|
|
66
63
|
if (trimmed.startsWith('//') || trimmed.startsWith('#')) {
|
|
67
|
-
// Single-line comment immediately above
|
|
68
64
|
const text = trimmed
|
|
69
65
|
.replace(/^\/\/\s*/, '')
|
|
70
66
|
.replace(/^#\s*/, '')
|
|
71
67
|
.trim();
|
|
72
|
-
return text
|
|
68
|
+
return truncate(text, maxChars);
|
|
73
69
|
}
|
|
74
|
-
if (trimmed !== '' && !trimmed.startsWith('*') && !trimmed.startsWith('/*'))
|
|
70
|
+
if (trimmed !== '' && !trimmed.startsWith('*') && !trimmed.startsWith('/*')) return null;
|
|
75
71
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** Find the line index where a block comment (*/) ends, scanning upward from idx. */
|
|
76
|
+
function findJsdocEndLine(fileLines: string[], idx: number, scanLines: number): number {
|
|
77
|
+
for (let i = idx; i >= Math.max(0, idx - scanLines); i--) {
|
|
78
|
+
const trimmed = fileLines[i]!.trim();
|
|
79
|
+
if (trimmed.endsWith('*/')) return i;
|
|
80
|
+
if (
|
|
81
|
+
trimmed !== '' &&
|
|
82
|
+
!trimmed.startsWith('*') &&
|
|
83
|
+
!trimmed.startsWith('/*') &&
|
|
84
|
+
!trimmed.startsWith('//') &&
|
|
85
|
+
!trimmed.startsWith('#')
|
|
86
|
+
) {
|
|
87
|
+
break;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return -1;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** Extract the first description line from a JSDoc block ending at jsdocEnd. */
|
|
94
|
+
function extractJsdocDescription(
|
|
95
|
+
fileLines: string[],
|
|
96
|
+
jsdocEnd: number,
|
|
97
|
+
openScanLines: number,
|
|
98
|
+
maxChars: number,
|
|
99
|
+
): string | null {
|
|
100
|
+
for (let i = jsdocEnd; i >= Math.max(0, jsdocEnd - openScanLines); i--) {
|
|
101
|
+
if (!fileLines[i]!.trim().startsWith('/**')) continue;
|
|
102
|
+
for (let j = i + 1; j <= jsdocEnd; j++) {
|
|
103
|
+
const docLine = fileLines[j]!.trim()
|
|
104
|
+
.replace(/^\*\s?/, '')
|
|
105
|
+
.trim();
|
|
106
|
+
if (docLine && !docLine.startsWith('@') && docLine !== '/' && docLine !== '*/') {
|
|
107
|
+
return truncate(docLine, maxChars);
|
|
92
108
|
}
|
|
93
109
|
}
|
|
110
|
+
break;
|
|
94
111
|
}
|
|
95
112
|
return null;
|
|
96
113
|
}
|
|
97
114
|
|
|
115
|
+
export function extractSummary(
|
|
116
|
+
fileLines: string[] | null,
|
|
117
|
+
line: number | undefined,
|
|
118
|
+
opts: ExtractSummaryOpts = {},
|
|
119
|
+
): string | null {
|
|
120
|
+
if (!fileLines || !line || line <= 1) return null;
|
|
121
|
+
const idx = line - 2; // line above the definition (0-indexed)
|
|
122
|
+
const jsdocEndScanLines = opts.jsdocEndScanLines ?? 10;
|
|
123
|
+
const jsdocOpenScanLines = opts.jsdocOpenScanLines ?? 20;
|
|
124
|
+
const summaryMaxChars = opts.summaryMaxChars ?? 100;
|
|
125
|
+
|
|
126
|
+
// Try single-line comment first
|
|
127
|
+
const singleLine = extractSingleLineComment(fileLines, idx, jsdocEndScanLines, summaryMaxChars);
|
|
128
|
+
if (singleLine) return singleLine;
|
|
129
|
+
|
|
130
|
+
// Try JSDoc block comment
|
|
131
|
+
const jsdocEnd = findJsdocEndLine(fileLines, idx, jsdocEndScanLines);
|
|
132
|
+
if (jsdocEnd >= 0) {
|
|
133
|
+
return extractJsdocDescription(fileLines, jsdocEnd, jsdocOpenScanLines, summaryMaxChars);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
|
|
98
139
|
interface ExtractSignatureOpts {
|
|
99
140
|
signatureGatherLines?: number;
|
|
100
141
|
}
|
|
@@ -104,6 +145,38 @@ export interface Signature {
|
|
|
104
145
|
returnType: string | null;
|
|
105
146
|
}
|
|
106
147
|
|
|
148
|
+
/** Per-language signature patterns. Each entry has a regex and an extractor for return type. */
|
|
149
|
+
const SIGNATURE_PATTERNS: Array<{
|
|
150
|
+
regex: RegExp;
|
|
151
|
+
returnType: (m: RegExpMatchArray) => string | null;
|
|
152
|
+
}> = [
|
|
153
|
+
// JS/TS: function name(params) or async function
|
|
154
|
+
{
|
|
155
|
+
regex: /(?:export\s+)?(?:async\s+)?function\s*\*?\s*\w*\s*\(([^)]*)\)\s*(?::\s*([^\n{]+))?/,
|
|
156
|
+
returnType: (m) => (m[2] ? m[2].trim().replace(/\s*\{$/, '') : null),
|
|
157
|
+
},
|
|
158
|
+
// Arrow: const name = (params) => or (params):ReturnType =>
|
|
159
|
+
{
|
|
160
|
+
regex: /=\s*(?:async\s+)?\(([^)]*)\)\s*(?::\s*([^=>\n{]+))?\s*=>/,
|
|
161
|
+
returnType: (m) => (m[2] ? m[2].trim() : null),
|
|
162
|
+
},
|
|
163
|
+
// Python: def name(params) -> return:
|
|
164
|
+
{
|
|
165
|
+
regex: /def\s+\w+\s*\(([^)]*)\)\s*(?:->\s*([^:\n]+))?/,
|
|
166
|
+
returnType: (m) => (m[2] ? m[2].trim() : null),
|
|
167
|
+
},
|
|
168
|
+
// Go: func (recv) name(params) (returns)
|
|
169
|
+
{
|
|
170
|
+
regex: /func\s+(?:\([^)]*\)\s+)?\w+\s*\(([^)]*)\)\s*(?:\(([^)]+)\)|(\w[^\n{]*))?/,
|
|
171
|
+
returnType: (m) => (m[2] || m[3] || '').trim() || null,
|
|
172
|
+
},
|
|
173
|
+
// Rust: fn name(params) -> ReturnType
|
|
174
|
+
{
|
|
175
|
+
regex: /fn\s+\w+\s*\(([^)]*)\)\s*(?:->\s*([^\n{]+))?/,
|
|
176
|
+
returnType: (m) => (m[2] ? m[2].trim() : null),
|
|
177
|
+
},
|
|
178
|
+
];
|
|
179
|
+
|
|
107
180
|
export function extractSignature(
|
|
108
181
|
fileLines: string[] | null,
|
|
109
182
|
line: number | undefined,
|
|
@@ -112,52 +185,18 @@ export function extractSignature(
|
|
|
112
185
|
if (!fileLines || !line) return null;
|
|
113
186
|
const idx = line - 1;
|
|
114
187
|
const signatureGatherLines = opts.signatureGatherLines ?? 5;
|
|
115
|
-
// Gather lines to handle multi-line params
|
|
116
188
|
const chunk = fileLines
|
|
117
189
|
.slice(idx, Math.min(fileLines.length, idx + signatureGatherLines))
|
|
118
190
|
.join('\n');
|
|
119
191
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
// Arrow: const name = (params) => or (params):ReturnType =>
|
|
131
|
-
m = chunk.match(/=\s*(?:async\s+)?\(([^)]*)\)\s*(?::\s*([^=>\n{]+))?\s*=>/);
|
|
132
|
-
if (m) {
|
|
133
|
-
return {
|
|
134
|
-
params: m[1]!.trim() || null,
|
|
135
|
-
returnType: m[2] ? m[2].trim() : null,
|
|
136
|
-
};
|
|
137
|
-
}
|
|
138
|
-
// Python: def name(params) -> return:
|
|
139
|
-
m = chunk.match(/def\s+\w+\s*\(([^)]*)\)\s*(?:->\s*([^:\n]+))?/);
|
|
140
|
-
if (m) {
|
|
141
|
-
return {
|
|
142
|
-
params: m[1]!.trim() || null,
|
|
143
|
-
returnType: m[2] ? m[2].trim() : null,
|
|
144
|
-
};
|
|
145
|
-
}
|
|
146
|
-
// Go: func (recv) name(params) (returns)
|
|
147
|
-
m = chunk.match(/func\s+(?:\([^)]*\)\s+)?\w+\s*\(([^)]*)\)\s*(?:\(([^)]+)\)|(\w[^\n{]*))?/);
|
|
148
|
-
if (m) {
|
|
149
|
-
return {
|
|
150
|
-
params: m[1]!.trim() || null,
|
|
151
|
-
returnType: (m[2] || m[3] || '').trim() || null,
|
|
152
|
-
};
|
|
153
|
-
}
|
|
154
|
-
// Rust: fn name(params) -> ReturnType
|
|
155
|
-
m = chunk.match(/fn\s+\w+\s*\(([^)]*)\)\s*(?:->\s*([^\n{]+))?/);
|
|
156
|
-
if (m) {
|
|
157
|
-
return {
|
|
158
|
-
params: m[1]!.trim() || null,
|
|
159
|
-
returnType: m[2] ? m[2].trim() : null,
|
|
160
|
-
};
|
|
192
|
+
for (const pattern of SIGNATURE_PATTERNS) {
|
|
193
|
+
const m = chunk.match(pattern.regex);
|
|
194
|
+
if (m) {
|
|
195
|
+
return {
|
|
196
|
+
params: m[1]!.trim() || null,
|
|
197
|
+
returnType: pattern.returnType(m),
|
|
198
|
+
};
|
|
199
|
+
}
|
|
161
200
|
}
|
|
162
201
|
return null;
|
|
163
202
|
}
|
package/src/types.ts
CHANGED
|
@@ -302,6 +302,7 @@ export interface Repository {
|
|
|
302
302
|
// ── Edge queries ──────────────────────────────────────────────────
|
|
303
303
|
findCallees(nodeId: number): RelatedNodeRow[];
|
|
304
304
|
findCallers(nodeId: number): RelatedNodeRow[];
|
|
305
|
+
findCallersBatch(nodeIds: number[]): Map<number, RelatedNodeRow[]>;
|
|
305
306
|
findDistinctCallers(nodeId: number): RelatedNodeRow[];
|
|
306
307
|
findAllOutgoingEdges(nodeId: number): AdjacentEdgeRow[];
|
|
307
308
|
findAllIncomingEdges(nodeId: number): AdjacentEdgeRow[];
|
|
@@ -333,6 +334,35 @@ export interface Repository {
|
|
|
333
334
|
getFileHash(file: string): string | null;
|
|
334
335
|
hasImplementsEdges(): boolean;
|
|
335
336
|
hasCoChangesTable(): boolean;
|
|
337
|
+
|
|
338
|
+
// ── Composite queries ──────────────────────────────────────────────
|
|
339
|
+
fnDeps(
|
|
340
|
+
name: string,
|
|
341
|
+
opts?: { depth?: number; noTests?: boolean; file?: string; kind?: string },
|
|
342
|
+
): {
|
|
343
|
+
name: string;
|
|
344
|
+
results: Array<{
|
|
345
|
+
name: string;
|
|
346
|
+
kind: string;
|
|
347
|
+
file: string;
|
|
348
|
+
line: number | null;
|
|
349
|
+
endLine: number | null;
|
|
350
|
+
role: string | null;
|
|
351
|
+
fileHash: string | null;
|
|
352
|
+
callees: Array<{ name: string; kind: string; file: string; line: number | null }>;
|
|
353
|
+
callers: Array<{
|
|
354
|
+
name: string;
|
|
355
|
+
kind: string;
|
|
356
|
+
file: string;
|
|
357
|
+
line: number | null;
|
|
358
|
+
viaHierarchy?: string;
|
|
359
|
+
}>;
|
|
360
|
+
transitiveCallers: Record<
|
|
361
|
+
number,
|
|
362
|
+
Array<{ name: string; kind: string; file: string; line: number | null }>
|
|
363
|
+
>;
|
|
364
|
+
}>;
|
|
365
|
+
} | null;
|
|
336
366
|
}
|
|
337
367
|
|
|
338
368
|
/**
|
|
@@ -408,7 +438,7 @@ export interface DefinitionComplexity {
|
|
|
408
438
|
cognitive: number;
|
|
409
439
|
cyclomatic: number;
|
|
410
440
|
maxNesting: number;
|
|
411
|
-
halstead?: HalsteadMetrics;
|
|
441
|
+
halstead?: HalsteadDerivedMetrics | HalsteadMetrics;
|
|
412
442
|
loc?: LOCMetrics;
|
|
413
443
|
maintainabilityIndex?: number;
|
|
414
444
|
}
|
|
@@ -665,6 +695,12 @@ export interface AnalysisTiming {
|
|
|
665
695
|
complexityMs: number;
|
|
666
696
|
cfgMs: number;
|
|
667
697
|
dataflowMs: number;
|
|
698
|
+
/**
|
|
699
|
+
* Diagnostic: total wall-clock time for the unified walk loop (includes
|
|
700
|
+
* setupVisitors overhead). Walk time is already distributed equally into
|
|
701
|
+
* the per-phase timers above, so this overlaps — it is not an additive
|
|
702
|
+
* bucket. Useful for cross-checking that Σ phase timers ≈ this value.
|
|
703
|
+
*/
|
|
668
704
|
_unifiedWalkMs?: number;
|
|
669
705
|
}
|
|
670
706
|
|
|
@@ -1877,6 +1913,7 @@ export interface NativeAddon {
|
|
|
1877
1913
|
fileNodeIds: unknown[],
|
|
1878
1914
|
barrelFiles: string[],
|
|
1879
1915
|
rootDir: string,
|
|
1916
|
+
symbolNodes?: Array<{ name: string; file: string; nodeId: number }>,
|
|
1880
1917
|
): unknown[];
|
|
1881
1918
|
engineVersion(): string;
|
|
1882
1919
|
analyzeComplexity(
|
|
@@ -2048,6 +2085,46 @@ export interface NativeComplexityMetrics {
|
|
|
2048
2085
|
halsteadVolume: number | null;
|
|
2049
2086
|
}
|
|
2050
2087
|
|
|
2088
|
+
// ── Native composite query types (fnDeps) ──────────────────────────────
|
|
2089
|
+
|
|
2090
|
+
export interface NativeFnDepsNode {
|
|
2091
|
+
name: string;
|
|
2092
|
+
kind: string;
|
|
2093
|
+
file: string;
|
|
2094
|
+
line: number | null;
|
|
2095
|
+
}
|
|
2096
|
+
|
|
2097
|
+
export interface NativeFnDepsCallerNode {
|
|
2098
|
+
name: string;
|
|
2099
|
+
kind: string;
|
|
2100
|
+
file: string;
|
|
2101
|
+
line: number | null;
|
|
2102
|
+
viaHierarchy: string | null;
|
|
2103
|
+
}
|
|
2104
|
+
|
|
2105
|
+
export interface NativeFnDepsTransitiveGroup {
|
|
2106
|
+
depth: number;
|
|
2107
|
+
callers: NativeFnDepsNode[];
|
|
2108
|
+
}
|
|
2109
|
+
|
|
2110
|
+
export interface NativeFnDepsEntry {
|
|
2111
|
+
name: string;
|
|
2112
|
+
kind: string;
|
|
2113
|
+
file: string;
|
|
2114
|
+
line: number | null;
|
|
2115
|
+
endLine: number | null;
|
|
2116
|
+
role: string | null;
|
|
2117
|
+
fileHash: string | null;
|
|
2118
|
+
callees: NativeFnDepsNode[];
|
|
2119
|
+
callers: NativeFnDepsCallerNode[];
|
|
2120
|
+
transitiveCallers: NativeFnDepsTransitiveGroup[];
|
|
2121
|
+
}
|
|
2122
|
+
|
|
2123
|
+
export interface NativeFnDepsResult {
|
|
2124
|
+
name: string;
|
|
2125
|
+
results: NativeFnDepsEntry[];
|
|
2126
|
+
}
|
|
2127
|
+
|
|
2051
2128
|
/** Native rusqlite database wrapper instance (Phase 6.13 + 6.14 + 6.15). */
|
|
2052
2129
|
export interface NativeDatabase {
|
|
2053
2130
|
// ── Lifecycle (6.13) ────────────────────────────────────────────────
|
|
@@ -2132,6 +2209,15 @@ export interface NativeDatabase {
|
|
|
2132
2209
|
getComplexityForNode(nodeId: number): NativeComplexityMetrics | null;
|
|
2133
2210
|
getFileHash(file: string): string | null;
|
|
2134
2211
|
|
|
2212
|
+
// ── Composite queries ──────────────────────────────────────────────
|
|
2213
|
+
fnDeps(
|
|
2214
|
+
name: string,
|
|
2215
|
+
depth: number | null | undefined,
|
|
2216
|
+
noTests: boolean | null | undefined,
|
|
2217
|
+
file: string | null | undefined,
|
|
2218
|
+
kind: string | null | undefined,
|
|
2219
|
+
): NativeFnDepsResult;
|
|
2220
|
+
|
|
2135
2221
|
// ── Build pipeline writes (6.15) ───────────────────────────────────
|
|
2136
2222
|
bulkInsertNodes(
|
|
2137
2223
|
batches: Array<{
|