@optave/codegraph 3.9.0 → 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 +7 -6
- package/dist/ast-analysis/engine.d.ts.map +1 -1
- package/dist/ast-analysis/engine.js +78 -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/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 +35 -0
- package/dist/db/repository/base.d.ts.map +1 -1
- package/dist/db/repository/base.js +8 -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 +7 -1
- package/dist/db/repository/native-repository.d.ts.map +1 -1
- package/dist/db/repository/native-repository.js +46 -1
- package/dist/db/repository/native-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 +12 -0
- 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 +293 -296
- 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 +29 -2
- 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.map +1 -1
- package/dist/domain/parser.js +2 -0
- package/dist/domain/parser.js.map +1 -1
- package/dist/extractors/go.js +53 -35
- package/dist/extractors/go.js.map +1 -1
- package/dist/extractors/javascript.js +66 -27
- 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 +81 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +7 -7
- package/src/ast-analysis/engine.ts +99 -55
- package/src/ast-analysis/visitors/ast-store-visitor.ts +19 -21
- package/src/db/connection.ts +24 -5
- package/src/db/repository/base.ts +43 -0
- package/src/db/repository/index.ts +1 -0
- package/src/db/repository/native-repository.ts +67 -1
- package/src/domain/analysis/dependencies.ts +13 -0
- package/src/domain/graph/builder/incremental.ts +21 -0
- package/src/domain/graph/builder/pipeline.ts +392 -362
- package/src/domain/graph/builder/stages/build-edges.ts +30 -1
- package/src/domain/graph/builder/stages/resolve-imports.ts +20 -20
- package/src/domain/graph/watcher.ts +118 -98
- package/src/domain/parser.ts +2 -0
- package/src/extractors/go.ts +57 -32
- package/src/extractors/javascript.ts +67 -27
- 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 +85 -0
|
@@ -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
|
@@ -334,6 +334,35 @@ export interface Repository {
|
|
|
334
334
|
getFileHash(file: string): string | null;
|
|
335
335
|
hasImplementsEdges(): boolean;
|
|
336
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;
|
|
337
366
|
}
|
|
338
367
|
|
|
339
368
|
/**
|
|
@@ -666,6 +695,12 @@ export interface AnalysisTiming {
|
|
|
666
695
|
complexityMs: number;
|
|
667
696
|
cfgMs: number;
|
|
668
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
|
+
*/
|
|
669
704
|
_unifiedWalkMs?: number;
|
|
670
705
|
}
|
|
671
706
|
|
|
@@ -1878,6 +1913,7 @@ export interface NativeAddon {
|
|
|
1878
1913
|
fileNodeIds: unknown[],
|
|
1879
1914
|
barrelFiles: string[],
|
|
1880
1915
|
rootDir: string,
|
|
1916
|
+
symbolNodes?: Array<{ name: string; file: string; nodeId: number }>,
|
|
1881
1917
|
): unknown[];
|
|
1882
1918
|
engineVersion(): string;
|
|
1883
1919
|
analyzeComplexity(
|
|
@@ -2049,6 +2085,46 @@ export interface NativeComplexityMetrics {
|
|
|
2049
2085
|
halsteadVolume: number | null;
|
|
2050
2086
|
}
|
|
2051
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
|
+
|
|
2052
2128
|
/** Native rusqlite database wrapper instance (Phase 6.13 + 6.14 + 6.15). */
|
|
2053
2129
|
export interface NativeDatabase {
|
|
2054
2130
|
// ── Lifecycle (6.13) ────────────────────────────────────────────────
|
|
@@ -2133,6 +2209,15 @@ export interface NativeDatabase {
|
|
|
2133
2209
|
getComplexityForNode(nodeId: number): NativeComplexityMetrics | null;
|
|
2134
2210
|
getFileHash(file: string): string | null;
|
|
2135
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
|
+
|
|
2136
2221
|
// ── Build pipeline writes (6.15) ───────────────────────────────────
|
|
2137
2222
|
bulkInsertNodes(
|
|
2138
2223
|
batches: Array<{
|