@kodus/kodus-graph 0.2.8 → 0.2.10

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.
Files changed (171) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +252 -0
  3. package/dist/analysis/blast-radius.d.ts +2 -0
  4. package/dist/analysis/blast-radius.js +55 -0
  5. package/dist/analysis/communities.d.ts +28 -0
  6. package/dist/analysis/communities.js +100 -0
  7. package/dist/analysis/context-builder.d.ts +34 -0
  8. package/dist/analysis/context-builder.js +92 -0
  9. package/dist/analysis/diff.d.ts +41 -0
  10. package/dist/analysis/diff.js +155 -0
  11. package/dist/analysis/enrich.d.ts +5 -0
  12. package/dist/analysis/enrich.js +126 -0
  13. package/dist/analysis/flows.d.ts +27 -0
  14. package/dist/analysis/flows.js +86 -0
  15. package/dist/analysis/inheritance.d.ts +3 -0
  16. package/dist/analysis/inheritance.js +31 -0
  17. package/dist/analysis/prompt-formatter.d.ts +2 -0
  18. package/dist/analysis/prompt-formatter.js +173 -0
  19. package/dist/analysis/risk-score.d.ts +4 -0
  20. package/dist/analysis/risk-score.js +51 -0
  21. package/dist/analysis/search.d.ts +11 -0
  22. package/dist/analysis/search.js +64 -0
  23. package/dist/analysis/test-gaps.d.ts +2 -0
  24. package/dist/analysis/test-gaps.js +14 -0
  25. package/dist/cli.d.ts +2 -0
  26. package/dist/cli.js +210 -0
  27. package/dist/commands/analyze.d.ts +9 -0
  28. package/dist/commands/analyze.js +116 -0
  29. package/dist/commands/communities.d.ts +8 -0
  30. package/dist/commands/communities.js +9 -0
  31. package/dist/commands/context.d.ts +12 -0
  32. package/dist/commands/context.js +130 -0
  33. package/dist/commands/diff.d.ts +9 -0
  34. package/dist/commands/diff.js +89 -0
  35. package/dist/commands/flows.d.ts +8 -0
  36. package/dist/commands/flows.js +9 -0
  37. package/dist/commands/parse.d.ts +11 -0
  38. package/dist/commands/parse.js +101 -0
  39. package/dist/commands/search.d.ts +12 -0
  40. package/dist/commands/search.js +27 -0
  41. package/dist/commands/update.d.ts +7 -0
  42. package/dist/commands/update.js +154 -0
  43. package/dist/graph/builder.d.ts +6 -0
  44. package/dist/graph/builder.js +248 -0
  45. package/dist/graph/edges.d.ts +23 -0
  46. package/dist/graph/edges.js +159 -0
  47. package/dist/graph/json-writer.d.ts +9 -0
  48. package/dist/graph/json-writer.js +38 -0
  49. package/dist/graph/loader.d.ts +13 -0
  50. package/dist/graph/loader.js +101 -0
  51. package/dist/graph/merger.d.ts +7 -0
  52. package/dist/graph/merger.js +18 -0
  53. package/dist/graph/types.d.ts +252 -0
  54. package/dist/graph/types.js +1 -0
  55. package/dist/parser/batch.d.ts +5 -0
  56. package/dist/parser/batch.js +93 -0
  57. package/dist/parser/discovery.d.ts +7 -0
  58. package/dist/parser/discovery.js +61 -0
  59. package/dist/parser/extractor.d.ts +4 -0
  60. package/dist/parser/extractor.js +33 -0
  61. package/dist/parser/extractors/generic.d.ts +8 -0
  62. package/dist/parser/extractors/generic.js +471 -0
  63. package/dist/parser/extractors/python.d.ts +8 -0
  64. package/dist/parser/extractors/python.js +133 -0
  65. package/dist/parser/extractors/ruby.d.ts +8 -0
  66. package/dist/parser/extractors/ruby.js +153 -0
  67. package/dist/parser/extractors/typescript.d.ts +10 -0
  68. package/dist/parser/extractors/typescript.js +365 -0
  69. package/dist/parser/languages.d.ts +32 -0
  70. package/dist/parser/languages.js +304 -0
  71. package/dist/resolver/call-resolver.d.ts +36 -0
  72. package/dist/resolver/call-resolver.js +178 -0
  73. package/dist/resolver/external-detector.d.ts +11 -0
  74. package/dist/resolver/external-detector.js +820 -0
  75. package/dist/resolver/fs-cache.d.ts +8 -0
  76. package/dist/resolver/fs-cache.js +36 -0
  77. package/dist/resolver/import-map.d.ts +12 -0
  78. package/dist/resolver/import-map.js +21 -0
  79. package/dist/resolver/import-resolver.d.ts +19 -0
  80. package/dist/resolver/import-resolver.js +310 -0
  81. package/dist/resolver/languages/csharp.d.ts +3 -0
  82. package/dist/resolver/languages/csharp.js +94 -0
  83. package/dist/resolver/languages/go.d.ts +3 -0
  84. package/dist/resolver/languages/go.js +197 -0
  85. package/dist/resolver/languages/java.d.ts +1 -0
  86. package/dist/resolver/languages/java.js +193 -0
  87. package/dist/resolver/languages/php.d.ts +3 -0
  88. package/dist/resolver/languages/php.js +75 -0
  89. package/dist/resolver/languages/python.d.ts +11 -0
  90. package/dist/resolver/languages/python.js +127 -0
  91. package/dist/resolver/languages/ruby.d.ts +24 -0
  92. package/dist/resolver/languages/ruby.js +110 -0
  93. package/dist/resolver/languages/rust.d.ts +1 -0
  94. package/dist/resolver/languages/rust.js +197 -0
  95. package/dist/resolver/languages/typescript.d.ts +35 -0
  96. package/dist/resolver/languages/typescript.js +416 -0
  97. package/dist/resolver/re-export-resolver.d.ts +24 -0
  98. package/dist/resolver/re-export-resolver.js +57 -0
  99. package/dist/resolver/symbol-table.d.ts +17 -0
  100. package/dist/resolver/symbol-table.js +60 -0
  101. package/dist/shared/extract-calls.d.ts +26 -0
  102. package/dist/shared/extract-calls.js +57 -0
  103. package/dist/shared/file-hash.d.ts +3 -0
  104. package/dist/shared/file-hash.js +10 -0
  105. package/dist/shared/filters.d.ts +3 -0
  106. package/dist/shared/filters.js +240 -0
  107. package/dist/shared/logger.d.ts +6 -0
  108. package/dist/shared/logger.js +17 -0
  109. package/dist/shared/qualified-name.d.ts +1 -0
  110. package/dist/shared/qualified-name.js +9 -0
  111. package/dist/shared/safe-path.d.ts +6 -0
  112. package/dist/shared/safe-path.js +29 -0
  113. package/dist/shared/schemas.d.ts +43 -0
  114. package/dist/shared/schemas.js +30 -0
  115. package/dist/shared/temp.d.ts +11 -0
  116. package/{src/shared/temp.ts → dist/shared/temp.js} +4 -5
  117. package/package.json +20 -6
  118. package/src/analysis/blast-radius.ts +0 -54
  119. package/src/analysis/communities.ts +0 -135
  120. package/src/analysis/context-builder.ts +0 -130
  121. package/src/analysis/diff.ts +0 -169
  122. package/src/analysis/enrich.ts +0 -110
  123. package/src/analysis/flows.ts +0 -112
  124. package/src/analysis/inheritance.ts +0 -34
  125. package/src/analysis/prompt-formatter.ts +0 -175
  126. package/src/analysis/risk-score.ts +0 -62
  127. package/src/analysis/search.ts +0 -76
  128. package/src/analysis/test-gaps.ts +0 -21
  129. package/src/cli.ts +0 -210
  130. package/src/commands/analyze.ts +0 -128
  131. package/src/commands/communities.ts +0 -19
  132. package/src/commands/context.ts +0 -182
  133. package/src/commands/diff.ts +0 -96
  134. package/src/commands/flows.ts +0 -19
  135. package/src/commands/parse.ts +0 -124
  136. package/src/commands/search.ts +0 -41
  137. package/src/commands/update.ts +0 -166
  138. package/src/graph/builder.ts +0 -209
  139. package/src/graph/edges.ts +0 -101
  140. package/src/graph/json-writer.ts +0 -43
  141. package/src/graph/loader.ts +0 -113
  142. package/src/graph/merger.ts +0 -25
  143. package/src/graph/types.ts +0 -283
  144. package/src/parser/batch.ts +0 -82
  145. package/src/parser/discovery.ts +0 -75
  146. package/src/parser/extractor.ts +0 -37
  147. package/src/parser/extractors/generic.ts +0 -132
  148. package/src/parser/extractors/python.ts +0 -133
  149. package/src/parser/extractors/ruby.ts +0 -147
  150. package/src/parser/extractors/typescript.ts +0 -350
  151. package/src/parser/languages.ts +0 -122
  152. package/src/resolver/call-resolver.ts +0 -244
  153. package/src/resolver/import-map.ts +0 -27
  154. package/src/resolver/import-resolver.ts +0 -72
  155. package/src/resolver/languages/csharp.ts +0 -7
  156. package/src/resolver/languages/go.ts +0 -7
  157. package/src/resolver/languages/java.ts +0 -7
  158. package/src/resolver/languages/php.ts +0 -7
  159. package/src/resolver/languages/python.ts +0 -35
  160. package/src/resolver/languages/ruby.ts +0 -21
  161. package/src/resolver/languages/rust.ts +0 -7
  162. package/src/resolver/languages/typescript.ts +0 -168
  163. package/src/resolver/re-export-resolver.ts +0 -66
  164. package/src/resolver/symbol-table.ts +0 -67
  165. package/src/shared/extract-calls.ts +0 -75
  166. package/src/shared/file-hash.ts +0 -12
  167. package/src/shared/filters.ts +0 -243
  168. package/src/shared/logger.ts +0 -17
  169. package/src/shared/qualified-name.ts +0 -5
  170. package/src/shared/safe-path.ts +0 -31
  171. package/src/shared/schemas.ts +0 -32
@@ -0,0 +1,32 @@
1
+ import type { SgNode } from '@ast-grep/napi';
2
+ import { Lang } from '@ast-grep/napi';
3
+ export type HeritageFinder = (node: SgNode) => string | string[] | undefined;
4
+ export interface TestConfig {
5
+ filePatterns?: RegExp[];
6
+ funcPatterns?: RegExp[];
7
+ /** When both filePatterns and funcPatterns are set: 'and' requires both, 'or' (default) requires either */
8
+ matchMode?: 'and' | 'or';
9
+ annotationKind?: string;
10
+ annotationNames?: string[];
11
+ }
12
+ export interface LangConfig {
13
+ class?: string[];
14
+ function?: string[];
15
+ method?: string[];
16
+ constructorKinds?: string[];
17
+ interface?: string[];
18
+ enum?: string[];
19
+ import?: string[];
20
+ heritage?: {
21
+ extends?: HeritageFinder;
22
+ implements?: HeritageFinder;
23
+ };
24
+ tests?: TestConfig;
25
+ }
26
+ export declare function getLanguage(ext: string): Lang | string | null;
27
+ export declare function getSupportedExtensions(): string[];
28
+ export declare function getLanguageName(lang: Lang | string): string;
29
+ export declare function isTypeScriptLike(lang: Lang | string): boolean;
30
+ export declare const LANG_CONFIGS: Record<string, LangConfig>;
31
+ export declare const LANG_KINDS: Record<string, Record<string, string>>;
32
+ export { Lang };
@@ -0,0 +1,304 @@
1
+ import csharp from '@ast-grep/lang-csharp';
2
+ import go from '@ast-grep/lang-go';
3
+ import java from '@ast-grep/lang-java';
4
+ import php from '@ast-grep/lang-php';
5
+ import python from '@ast-grep/lang-python';
6
+ import ruby from '@ast-grep/lang-ruby';
7
+ import rust from '@ast-grep/lang-rust';
8
+ import { Lang, registerDynamicLanguage } from '@ast-grep/napi';
9
+ // Register dynamic languages at import time (side effect).
10
+ // This must happen before parseAsync can parse these languages.
11
+ registerDynamicLanguage({ python, ruby, go, java, rust, php, csharp });
12
+ // ---------------------------------------------------------------------------
13
+ // Extension -> language identifier
14
+ // Built-in langs use Lang enum, dynamic langs use lowercase string
15
+ // ---------------------------------------------------------------------------
16
+ const EXT_TO_LANG = {
17
+ '.ts': Lang.TypeScript,
18
+ '.tsx': Lang.Tsx,
19
+ '.js': Lang.JavaScript,
20
+ '.jsx': Lang.JavaScript,
21
+ '.mjs': Lang.JavaScript,
22
+ '.cjs': Lang.JavaScript,
23
+ '.py': 'python',
24
+ '.rb': 'ruby',
25
+ '.go': 'go',
26
+ '.java': 'java',
27
+ '.rs': 'rust',
28
+ '.cs': 'csharp',
29
+ '.php': 'php',
30
+ };
31
+ export function getLanguage(ext) {
32
+ return EXT_TO_LANG[ext] ?? null;
33
+ }
34
+ export function getSupportedExtensions() {
35
+ return Object.keys(EXT_TO_LANG);
36
+ }
37
+ export function getLanguageName(lang) {
38
+ if (typeof lang === 'string') {
39
+ return lang;
40
+ }
41
+ if (lang === Lang.TypeScript || lang === Lang.Tsx) {
42
+ return 'typescript';
43
+ }
44
+ if (lang === Lang.JavaScript) {
45
+ return 'javascript';
46
+ }
47
+ return 'unknown';
48
+ }
49
+ export function isTypeScriptLike(lang) {
50
+ return lang === Lang.TypeScript || lang === Lang.Tsx || lang === Lang.JavaScript;
51
+ }
52
+ // ---------------------------------------------------------------------------
53
+ // Per-language LangConfig definitions
54
+ // ---------------------------------------------------------------------------
55
+ const typescriptConfig = {
56
+ class: ['class_declaration', 'abstract_class_declaration'],
57
+ method: ['method_definition'],
58
+ function: ['function_declaration'],
59
+ interface: ['interface_declaration'],
60
+ enum: ['enum_declaration'],
61
+ import: ['import_statement'],
62
+ };
63
+ const pythonConfig = {
64
+ class: ['class_definition'],
65
+ method: ['function_definition'],
66
+ function: ['function_definition'],
67
+ import: ['import_from_statement', 'import_statement'],
68
+ tests: {
69
+ funcPatterns: [/^test_/],
70
+ filePatterns: [/test_.*\.py$/, /_test\.py$/],
71
+ },
72
+ };
73
+ const rubyConfig = {
74
+ class: ['class'],
75
+ method: ['method', 'singleton_method'],
76
+ import: [],
77
+ tests: {
78
+ filePatterns: [/_spec\.rb$/, /spec_.*\.rb$/],
79
+ },
80
+ };
81
+ const goConfig = {
82
+ function: ['function_declaration'],
83
+ method: ['method_declaration'],
84
+ class: ['type_declaration'],
85
+ interface: ['type_declaration'],
86
+ import: ['import_declaration'],
87
+ tests: {
88
+ filePatterns: [/_test\.go$/],
89
+ funcPatterns: [/^Test/, /^Benchmark/],
90
+ matchMode: 'and',
91
+ },
92
+ };
93
+ const javaConfig = {
94
+ class: ['class_declaration'],
95
+ interface: ['interface_declaration'],
96
+ method: ['method_declaration'],
97
+ constructorKinds: ['constructor_declaration'],
98
+ import: ['import_declaration'],
99
+ enum: ['enum_declaration'],
100
+ heritage: {
101
+ extends: (node) => {
102
+ const superclass = node.children().find((c) => c.kind() === 'superclass');
103
+ if (!superclass) {
104
+ return undefined;
105
+ }
106
+ const typeId = superclass.children().find((c) => c.kind() === 'type_identifier');
107
+ return typeId?.text();
108
+ },
109
+ implements: (node) => {
110
+ const superInterfaces = node.children().find((c) => c.kind() === 'super_interfaces');
111
+ if (!superInterfaces) {
112
+ return [];
113
+ }
114
+ // type_identifiers may be direct children or nested in a type_list
115
+ const typeList = superInterfaces.children().find((c) => c.kind() === 'type_list');
116
+ const container = typeList || superInterfaces;
117
+ return container
118
+ .children()
119
+ .filter((c) => c.kind() === 'type_identifier')
120
+ .map((c) => c.text());
121
+ },
122
+ },
123
+ tests: {
124
+ annotationKind: 'marker_annotation',
125
+ annotationNames: ['Test', 'ParameterizedTest'],
126
+ },
127
+ };
128
+ const rustConfig = {
129
+ function: ['function_item'],
130
+ class: ['struct_item', 'impl_item'],
131
+ interface: ['trait_item'],
132
+ enum: ['enum_item'],
133
+ import: ['use_declaration'],
134
+ tests: {
135
+ annotationKind: 'attribute_item',
136
+ annotationNames: ['test'],
137
+ },
138
+ };
139
+ const csharpConfig = {
140
+ class: ['class_declaration'],
141
+ interface: ['interface_declaration'],
142
+ method: ['method_declaration'],
143
+ constructorKinds: ['constructor_declaration'],
144
+ import: ['using_directive'],
145
+ enum: ['enum_declaration'],
146
+ heritage: {
147
+ // C# base_list doesn't distinguish extends vs implements syntactically.
148
+ // Heuristic: names starting with 'I' + uppercase are interfaces (C# convention).
149
+ // First non-interface type is treated as the base class.
150
+ extends: (node) => {
151
+ const baseList = node.children().find((c) => c.kind() === 'base_list');
152
+ if (!baseList) {
153
+ return undefined;
154
+ }
155
+ const types = baseList
156
+ .children()
157
+ .filter((c) => c.kind() === 'type_identifier' || c.kind() === 'identifier')
158
+ .map((c) => c.text());
159
+ // First non-interface name is the base class
160
+ return types.find((t) => !(t.length >= 2 && t[0] === 'I' && t[1] === t[1].toUpperCase()));
161
+ },
162
+ implements: (node) => {
163
+ const baseList = node.children().find((c) => c.kind() === 'base_list');
164
+ if (!baseList) {
165
+ return undefined;
166
+ }
167
+ const types = baseList
168
+ .children()
169
+ .filter((c) => c.kind() === 'type_identifier' || c.kind() === 'identifier')
170
+ .map((c) => c.text());
171
+ // Names matching I+uppercase convention are interfaces
172
+ return types.filter((t) => t.length >= 2 && t[0] === 'I' && t[1] === t[1].toUpperCase());
173
+ },
174
+ },
175
+ tests: {
176
+ annotationKind: 'attribute',
177
+ annotationNames: ['TestMethod', 'Fact', 'Test', 'Theory'],
178
+ },
179
+ };
180
+ const phpConfig = {
181
+ class: ['class_declaration'],
182
+ interface: ['interface_declaration'],
183
+ method: ['method_declaration'],
184
+ function: ['function_definition'],
185
+ import: ['namespace_use_declaration'],
186
+ heritage: {
187
+ extends: (node) => {
188
+ const baseClause = node.children().find((c) => c.kind() === 'base_clause');
189
+ if (!baseClause) {
190
+ return undefined;
191
+ }
192
+ const name = baseClause.children().find((c) => c.kind() === 'name');
193
+ return name?.text();
194
+ },
195
+ implements: (node) => {
196
+ const ifaceClause = node.children().find((c) => c.kind() === 'class_interface_clause');
197
+ if (!ifaceClause) {
198
+ return undefined;
199
+ }
200
+ return ifaceClause
201
+ .children()
202
+ .filter((c) => c.kind() === 'name')
203
+ .map((c) => c.text());
204
+ },
205
+ },
206
+ tests: {
207
+ funcPatterns: [/^test/],
208
+ filePatterns: [/Test\.php$/],
209
+ },
210
+ };
211
+ // ---------------------------------------------------------------------------
212
+ // LANG_CONFIGS export
213
+ // ---------------------------------------------------------------------------
214
+ export const LANG_CONFIGS = {
215
+ typescript: typescriptConfig,
216
+ python: pythonConfig,
217
+ ruby: rubyConfig,
218
+ go: goConfig,
219
+ java: javaConfig,
220
+ rust: rustConfig,
221
+ csharp: csharpConfig,
222
+ php: phpConfig,
223
+ };
224
+ // ---------------------------------------------------------------------------
225
+ // Backward-compat LANG_KINDS derived from LANG_CONFIGS
226
+ // (used by the dedicated typescript.ts, python.ts, ruby.ts extractors)
227
+ // Takes the first element of each array to match the old single-string format,
228
+ // then merges in language-specific extras.
229
+ // ---------------------------------------------------------------------------
230
+ function firstOf(arr) {
231
+ return arr?.[0];
232
+ }
233
+ function derivedKinds(config) {
234
+ const result = {};
235
+ if (firstOf(config.class)) {
236
+ result.class = firstOf(config.class);
237
+ }
238
+ if (firstOf(config.function)) {
239
+ result.function = firstOf(config.function);
240
+ }
241
+ if (firstOf(config.method)) {
242
+ result.method = firstOf(config.method);
243
+ }
244
+ if (firstOf(config.constructorKinds)) {
245
+ // biome-ignore lint/complexity/useLiteralKeys: bracket notation required — dot notation resolves to Function.prototype.constructor (TS2322)
246
+ result['constructor'] = firstOf(config.constructorKinds);
247
+ }
248
+ if (firstOf(config.interface)) {
249
+ result.interface = firstOf(config.interface);
250
+ }
251
+ if (firstOf(config.enum)) {
252
+ result.enum = firstOf(config.enum);
253
+ }
254
+ if (firstOf(config.import)) {
255
+ result.import = firstOf(config.import);
256
+ }
257
+ return result;
258
+ }
259
+ export const LANG_KINDS = {
260
+ typescript: {
261
+ ...derivedKinds(typescriptConfig),
262
+ abstractClass: 'abstract_class_declaration',
263
+ arrowContainer: 'variable_declarator',
264
+ arrowFunction: 'arrow_function',
265
+ export: 'export_statement',
266
+ methodSignature: 'method_signature',
267
+ },
268
+ python: {
269
+ ...derivedKinds(pythonConfig),
270
+ importRegular: 'import_statement',
271
+ decorator: 'decorator',
272
+ },
273
+ ruby: {
274
+ ...derivedKinds(rubyConfig),
275
+ module: 'module',
276
+ singletonMethod: 'singleton_method',
277
+ call: 'call',
278
+ },
279
+ go: {
280
+ ...derivedKinds(goConfig),
281
+ struct: 'type_declaration',
282
+ },
283
+ java: {
284
+ ...derivedKinds(javaConfig),
285
+ annotation: 'marker_annotation',
286
+ },
287
+ rust: {
288
+ ...derivedKinds(rustConfig),
289
+ impl: 'impl_item',
290
+ struct: 'struct_item',
291
+ trait: 'trait_item',
292
+ use: 'use_declaration',
293
+ },
294
+ csharp: {
295
+ ...derivedKinds(csharpConfig),
296
+ using: 'using_directive',
297
+ attribute: 'attribute',
298
+ },
299
+ php: {
300
+ ...derivedKinds(phpConfig),
301
+ namespace: 'namespace_use_declaration',
302
+ },
303
+ };
304
+ export { Lang };
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Call resolution with 5-tier confidence cascade.
3
+ *
4
+ * Cascade: DI (0.90-0.95) → same-file (0.85) → import-resolved (0.70-0.90)
5
+ * → unique-name (0.50) → ambiguous (0.30)
6
+ *
7
+ * Pure resolution logic — no file I/O, no parsing.
8
+ * Raw call sites are provided by the batch parser.
9
+ */
10
+ import type { RawCallEdge, RawCallSite } from '../graph/types';
11
+ import type { ImportMap } from './import-map';
12
+ import type { SymbolTable } from './symbol-table';
13
+ interface CallResolverStats {
14
+ di: number;
15
+ same: number;
16
+ import: number;
17
+ unique: number;
18
+ ambiguous: number;
19
+ noise: number;
20
+ }
21
+ interface ResolveAllResult {
22
+ callEdges: RawCallEdge[];
23
+ stats: CallResolverStats;
24
+ }
25
+ /**
26
+ * Resolve all raw call sites via the 5-tier cascade.
27
+ *
28
+ * Accepts pre-extracted RawCallSite[] from the batch parser.
29
+ * No file reads, no parseAsync — pure iteration + lookup.
30
+ */
31
+ export declare function resolveAllCalls(rawCalls: RawCallSite[], diMaps: Map<string, Map<string, string>>, symbolTable: SymbolTable, importMap: ImportMap): ResolveAllResult;
32
+ export declare function resolveCall(callName: string, currentFile: string, symbolTable: SymbolTable, importMap: ImportMap): {
33
+ target: string;
34
+ confidence: number;
35
+ } | null;
36
+ export {};
@@ -0,0 +1,178 @@
1
+ /**
2
+ * Call resolution with 5-tier confidence cascade.
3
+ *
4
+ * Cascade: DI (0.90-0.95) → same-file (0.85) → import-resolved (0.70-0.90)
5
+ * → unique-name (0.50) → ambiguous (0.30)
6
+ *
7
+ * Pure resolution logic — no file I/O, no parsing.
8
+ * Raw call sites are provided by the batch parser.
9
+ */
10
+ import { NOISE } from '../shared/filters';
11
+ // ── Batch resolution (pure, no I/O) ──
12
+ /**
13
+ * Resolve all raw call sites via the 5-tier cascade.
14
+ *
15
+ * Accepts pre-extracted RawCallSite[] from the batch parser.
16
+ * No file reads, no parseAsync — pure iteration + lookup.
17
+ */
18
+ export function resolveAllCalls(rawCalls, diMaps, symbolTable, importMap) {
19
+ const callEdges = [];
20
+ const stats = { di: 0, same: 0, import: 0, unique: 0, ambiguous: 0, noise: 0 };
21
+ for (const call of rawCalls) {
22
+ if (NOISE.has(call.callName)) {
23
+ stats.noise++;
24
+ continue;
25
+ }
26
+ const fp = call.source;
27
+ const diMap = diMaps.get(fp);
28
+ // Try DI resolution first if diField is present
29
+ if (call.diField) {
30
+ const resolved = resolveDICall(call.diField, call.callName, fp, diMap, symbolTable);
31
+ if (resolved) {
32
+ callEdges.push({
33
+ source: fp,
34
+ target: resolved.target,
35
+ callName: call.callName,
36
+ line: call.line,
37
+ confidence: resolved.confidence,
38
+ });
39
+ stats.di++;
40
+ continue;
41
+ }
42
+ }
43
+ // Class-aware resolution for self.X() and super().X()
44
+ if (call.resolveInClass) {
45
+ const classResolved = resolveInClass(call.callName, fp, call.resolveInClass, symbolTable);
46
+ if (classResolved) {
47
+ callEdges.push({
48
+ source: fp,
49
+ target: classResolved.target,
50
+ callName: call.callName,
51
+ line: call.line,
52
+ confidence: classResolved.confidence,
53
+ });
54
+ stats[classResolved.strategy]++;
55
+ continue;
56
+ }
57
+ }
58
+ // Name-based cascade fallback
59
+ const resolved = resolveByName(call.callName, fp, symbolTable, importMap);
60
+ if (resolved) {
61
+ callEdges.push({
62
+ source: fp,
63
+ target: resolved.target,
64
+ callName: call.callName,
65
+ line: call.line,
66
+ confidence: resolved.confidence,
67
+ });
68
+ stats[resolved.strategy]++;
69
+ }
70
+ }
71
+ return { callEdges, stats };
72
+ }
73
+ // ── Class-aware resolution (self./super.) ──
74
+ function resolveInClass(callName, currentFile, className, symbolTable) {
75
+ // Try same-file class method first (self.method() or super().method())
76
+ const inFile = symbolTable.lookupInFile(currentFile, callName, className);
77
+ if (inFile) {
78
+ return { target: inFile, confidence: 0.9, strategy: 'same' };
79
+ }
80
+ // Class might be in another file (imported parent class for super())
81
+ const candidates = symbolTable.lookupGlobal(callName);
82
+ const match = candidates.find((q) => q.includes(`::${className}.${callName}`));
83
+ if (match) {
84
+ return { target: match, confidence: 0.85, strategy: 'import' };
85
+ }
86
+ return null;
87
+ }
88
+ // ── DI resolution ──
89
+ function resolveDICall(fieldName, methodName, _currentFile, diMap, symbolTable) {
90
+ if (!diMap?.has(fieldName)) {
91
+ return null;
92
+ }
93
+ const typeName = diMap.get(fieldName);
94
+ // Direct class match
95
+ const candidates = symbolTable.lookupGlobal(typeName);
96
+ if (candidates.length >= 1) {
97
+ const typeFile = candidates[0].split('::')[0];
98
+ return { target: `${typeFile}::${typeName}.${methodName}`, confidence: 0.95, strategy: 'di' };
99
+ }
100
+ // ISomething → Something heuristic for interface → implementation
101
+ if (typeName.startsWith('I') && typeName[1] === typeName[1]?.toUpperCase()) {
102
+ const implName = typeName.substring(1);
103
+ const implCandidates = symbolTable.lookupGlobal(implName);
104
+ if (implCandidates.length >= 1) {
105
+ const implFile = implCandidates[0].split('::')[0];
106
+ return { target: `${implFile}::${implName}.${methodName}`, confidence: 0.9, strategy: 'di' };
107
+ }
108
+ }
109
+ return null;
110
+ }
111
+ // ── Name-based resolution (4-tier cascade) ──
112
+ function resolveByName(callName, currentFile, symbolTable, importMap) {
113
+ // Strategy 1: Same file (0.85)
114
+ const sameFile = symbolTable.lookupExact(currentFile, callName);
115
+ if (sameFile) {
116
+ return { target: sameFile, confidence: 0.85, strategy: 'same' };
117
+ }
118
+ // Strategy 2: Import-resolved (0.70-0.90)
119
+ const importedFrom = importMap.lookup(currentFile, callName);
120
+ if (importedFrom) {
121
+ const targetSym = symbolTable.lookupExact(importedFrom, callName);
122
+ if (targetSym) {
123
+ return { target: targetSym, confidence: 0.9, strategy: 'import' };
124
+ }
125
+ return { target: `${importedFrom}::${callName}`, confidence: 0.7, strategy: 'import' };
126
+ }
127
+ // Strategy 3: Unique global name (0.50)
128
+ if (symbolTable.isUnique(callName)) {
129
+ const candidates = symbolTable.lookupGlobal(callName);
130
+ return { target: candidates[0], confidence: 0.5, strategy: 'unique' };
131
+ }
132
+ // Strategy 4: Ambiguous (0.30) — pick closest candidate by directory proximity
133
+ const candidates = symbolTable.lookupGlobal(callName);
134
+ if (candidates.length > 1) {
135
+ const best = pickClosestCandidate(candidates, currentFile);
136
+ return { target: best, confidence: 0.3, strategy: 'ambiguous' };
137
+ }
138
+ return null;
139
+ }
140
+ // ── Proximity-based candidate selection ──
141
+ /**
142
+ * Pick the candidate whose file path is closest to the caller's file.
143
+ * Counts shared leading path segments — more shared = closer.
144
+ */
145
+ function pickClosestCandidate(candidates, callerFile) {
146
+ const callerParts = callerFile.split('/');
147
+ let best = candidates[0];
148
+ let bestScore = -1;
149
+ for (const candidate of candidates) {
150
+ const candidateFile = candidate.includes('::') ? candidate.split('::')[0] : candidate;
151
+ const parts = candidateFile.split('/');
152
+ let shared = 0;
153
+ for (let i = 0; i < Math.min(callerParts.length, parts.length); i++) {
154
+ if (callerParts[i] === parts[i]) {
155
+ shared++;
156
+ }
157
+ else {
158
+ break;
159
+ }
160
+ }
161
+ if (shared > bestScore) {
162
+ bestScore = shared;
163
+ best = candidate;
164
+ }
165
+ }
166
+ return best;
167
+ }
168
+ // ── Public wrapper for unit testing ──
169
+ export function resolveCall(callName, currentFile, symbolTable, importMap) {
170
+ if (NOISE.has(callName)) {
171
+ return null;
172
+ }
173
+ const result = resolveByName(callName, currentFile, symbolTable, importMap);
174
+ if (!result) {
175
+ return null;
176
+ }
177
+ return { target: result.target, confidence: result.confidence };
178
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * External package detector.
3
+ * Reads dependency manifests (package.json, requirements.txt, go.mod, etc.)
4
+ * to determine if an import target is an external (third-party) package.
5
+ */
6
+ export declare function clearExternalCache(): void;
7
+ /**
8
+ * Check if an import is an external (third-party) package.
9
+ * Returns the package name if external, null if not detected as external.
10
+ */
11
+ export declare function detectExternal(modulePath: string, lang: string, repoRoot: string): string | null;