gitnexus 1.4.9 → 1.5.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 +6 -5
- package/dist/cli/ai-context.d.ts +4 -1
- package/dist/cli/ai-context.js +19 -11
- package/dist/cli/analyze.d.ts +6 -0
- package/dist/cli/analyze.js +105 -251
- package/dist/cli/eval-server.js +20 -11
- package/dist/cli/index-repo.js +20 -22
- package/dist/cli/index.js +8 -7
- package/dist/cli/mcp.js +1 -1
- package/dist/cli/serve.js +29 -1
- package/dist/cli/setup.js +9 -9
- package/dist/cli/skill-gen.js +15 -9
- package/dist/cli/wiki.d.ts +2 -0
- package/dist/cli/wiki.js +141 -26
- package/dist/config/ignore-service.js +102 -22
- package/dist/config/supported-languages.d.ts +8 -42
- package/dist/config/supported-languages.js +8 -43
- package/dist/core/augmentation/engine.js +19 -7
- package/dist/core/embeddings/embedder.js +19 -15
- package/dist/core/embeddings/embedding-pipeline.js +6 -6
- package/dist/core/embeddings/http-client.js +3 -3
- package/dist/core/embeddings/text-generator.js +9 -24
- package/dist/core/embeddings/types.d.ts +1 -1
- package/dist/core/embeddings/types.js +1 -7
- package/dist/core/graph/graph.js +6 -2
- package/dist/core/graph/types.d.ts +9 -59
- package/dist/core/ingestion/ast-cache.js +3 -3
- package/dist/core/ingestion/call-processor.d.ts +20 -2
- package/dist/core/ingestion/call-processor.js +347 -144
- package/dist/core/ingestion/call-routing.js +10 -4
- package/dist/core/ingestion/call-sites/extract-language-call-site.d.ts +10 -0
- package/dist/core/ingestion/call-sites/extract-language-call-site.js +22 -0
- package/dist/core/ingestion/call-sites/java.d.ts +9 -0
- package/dist/core/ingestion/call-sites/java.js +30 -0
- package/dist/core/ingestion/cluster-enricher.js +6 -8
- package/dist/core/ingestion/cobol/cobol-copy-expander.js +10 -3
- package/dist/core/ingestion/cobol/cobol-preprocessor.js +287 -81
- package/dist/core/ingestion/cobol/jcl-parser.js +1 -1
- package/dist/core/ingestion/cobol/jcl-processor.js +1 -1
- package/dist/core/ingestion/cobol-processor.js +102 -56
- package/dist/core/ingestion/community-processor.js +21 -15
- package/dist/core/ingestion/entry-point-scoring.d.ts +1 -1
- package/dist/core/ingestion/entry-point-scoring.js +5 -6
- package/dist/core/ingestion/export-detection.js +32 -9
- package/dist/core/ingestion/field-extractor.d.ts +1 -1
- package/dist/core/ingestion/field-extractors/configs/c-cpp.js +8 -12
- package/dist/core/ingestion/field-extractors/configs/csharp.js +45 -2
- package/dist/core/ingestion/field-extractors/configs/dart.js +5 -3
- package/dist/core/ingestion/field-extractors/configs/go.js +3 -7
- package/dist/core/ingestion/field-extractors/configs/helpers.d.ts +5 -0
- package/dist/core/ingestion/field-extractors/configs/helpers.js +14 -0
- package/dist/core/ingestion/field-extractors/configs/jvm.js +7 -7
- package/dist/core/ingestion/field-extractors/configs/php.js +9 -11
- package/dist/core/ingestion/field-extractors/configs/python.js +1 -1
- package/dist/core/ingestion/field-extractors/configs/ruby.js +4 -3
- package/dist/core/ingestion/field-extractors/configs/rust.js +2 -5
- package/dist/core/ingestion/field-extractors/configs/swift.js +9 -7
- package/dist/core/ingestion/field-extractors/configs/typescript-javascript.js +2 -6
- package/dist/core/ingestion/field-extractors/generic.d.ts +5 -2
- package/dist/core/ingestion/field-extractors/generic.js +6 -0
- package/dist/core/ingestion/field-extractors/typescript.d.ts +1 -1
- package/dist/core/ingestion/field-extractors/typescript.js +1 -1
- package/dist/core/ingestion/field-types.d.ts +4 -2
- package/dist/core/ingestion/filesystem-walker.js +3 -3
- package/dist/core/ingestion/framework-detection.d.ts +1 -1
- package/dist/core/ingestion/framework-detection.js +355 -85
- package/dist/core/ingestion/heritage-processor.d.ts +24 -0
- package/dist/core/ingestion/heritage-processor.js +99 -8
- package/dist/core/ingestion/import-processor.js +44 -15
- package/dist/core/ingestion/import-resolvers/csharp.js +7 -3
- package/dist/core/ingestion/import-resolvers/dart.js +1 -1
- package/dist/core/ingestion/import-resolvers/go.js +4 -2
- package/dist/core/ingestion/import-resolvers/jvm.js +4 -4
- package/dist/core/ingestion/import-resolvers/php.js +4 -4
- package/dist/core/ingestion/import-resolvers/python.js +1 -1
- package/dist/core/ingestion/import-resolvers/rust.js +9 -3
- package/dist/core/ingestion/import-resolvers/standard.d.ts +1 -1
- package/dist/core/ingestion/import-resolvers/standard.js +6 -5
- package/dist/core/ingestion/import-resolvers/swift.js +2 -1
- package/dist/core/ingestion/import-resolvers/utils.js +26 -7
- package/dist/core/ingestion/language-config.js +5 -4
- package/dist/core/ingestion/language-provider.d.ts +7 -2
- package/dist/core/ingestion/languages/c-cpp.js +106 -21
- package/dist/core/ingestion/languages/cobol.js +1 -1
- package/dist/core/ingestion/languages/csharp.js +96 -19
- package/dist/core/ingestion/languages/dart.js +23 -7
- package/dist/core/ingestion/languages/go.js +1 -1
- package/dist/core/ingestion/languages/index.d.ts +1 -1
- package/dist/core/ingestion/languages/index.js +2 -3
- package/dist/core/ingestion/languages/java.js +4 -1
- package/dist/core/ingestion/languages/kotlin.js +60 -13
- package/dist/core/ingestion/languages/php.js +102 -25
- package/dist/core/ingestion/languages/python.js +28 -5
- package/dist/core/ingestion/languages/ruby.js +56 -14
- package/dist/core/ingestion/languages/rust.js +55 -11
- package/dist/core/ingestion/languages/swift.js +112 -27
- package/dist/core/ingestion/languages/typescript.js +95 -19
- package/dist/core/ingestion/markdown-processor.js +5 -5
- package/dist/core/ingestion/method-extractors/configs/csharp.d.ts +2 -0
- package/dist/core/ingestion/method-extractors/configs/csharp.js +283 -0
- package/dist/core/ingestion/method-extractors/configs/jvm.d.ts +3 -0
- package/dist/core/ingestion/method-extractors/configs/jvm.js +326 -0
- package/dist/core/ingestion/method-extractors/generic.d.ts +5 -0
- package/dist/core/ingestion/method-extractors/generic.js +137 -0
- package/dist/core/ingestion/method-types.d.ts +61 -0
- package/dist/core/ingestion/method-types.js +2 -0
- package/dist/core/ingestion/mro-processor.d.ts +1 -1
- package/dist/core/ingestion/mro-processor.js +12 -8
- package/dist/core/ingestion/named-binding-processor.js +2 -2
- package/dist/core/ingestion/named-bindings/rust.js +3 -1
- package/dist/core/ingestion/parsing-processor.js +74 -24
- package/dist/core/ingestion/pipeline.d.ts +2 -1
- package/dist/core/ingestion/pipeline.js +208 -102
- package/dist/core/ingestion/process-processor.js +12 -10
- package/dist/core/ingestion/resolution-context.js +3 -3
- package/dist/core/ingestion/route-extractors/middleware.js +31 -7
- package/dist/core/ingestion/route-extractors/php.js +2 -1
- package/dist/core/ingestion/route-extractors/response-shapes.js +8 -4
- package/dist/core/ingestion/structure-processor.d.ts +1 -1
- package/dist/core/ingestion/structure-processor.js +4 -4
- package/dist/core/ingestion/symbol-table.d.ts +1 -1
- package/dist/core/ingestion/symbol-table.js +22 -6
- package/dist/core/ingestion/tree-sitter-queries.d.ts +1 -1
- package/dist/core/ingestion/tree-sitter-queries.js +1 -1
- package/dist/core/ingestion/type-env.d.ts +2 -2
- package/dist/core/ingestion/type-env.js +75 -50
- package/dist/core/ingestion/type-extractors/c-cpp.js +33 -30
- package/dist/core/ingestion/type-extractors/csharp.js +24 -14
- package/dist/core/ingestion/type-extractors/dart.js +6 -8
- package/dist/core/ingestion/type-extractors/go.js +7 -6
- package/dist/core/ingestion/type-extractors/jvm.js +10 -21
- package/dist/core/ingestion/type-extractors/php.js +26 -13
- package/dist/core/ingestion/type-extractors/python.js +11 -15
- package/dist/core/ingestion/type-extractors/ruby.js +8 -3
- package/dist/core/ingestion/type-extractors/rust.js +6 -8
- package/dist/core/ingestion/type-extractors/shared.js +134 -50
- package/dist/core/ingestion/type-extractors/swift.js +16 -13
- package/dist/core/ingestion/type-extractors/typescript.js +23 -15
- package/dist/core/ingestion/utils/ast-helpers.d.ts +8 -8
- package/dist/core/ingestion/utils/ast-helpers.js +72 -35
- package/dist/core/ingestion/utils/call-analysis.d.ts +2 -0
- package/dist/core/ingestion/utils/call-analysis.js +96 -49
- package/dist/core/ingestion/utils/event-loop.js +1 -1
- package/dist/core/ingestion/workers/parse-worker.d.ts +7 -2
- package/dist/core/ingestion/workers/parse-worker.js +364 -84
- package/dist/core/ingestion/workers/worker-pool.js +5 -10
- package/dist/core/lbug/csv-generator.js +54 -15
- package/dist/core/lbug/lbug-adapter.d.ts +5 -0
- package/dist/core/lbug/lbug-adapter.js +86 -23
- package/dist/core/lbug/schema.d.ts +3 -6
- package/dist/core/lbug/schema.js +6 -30
- package/dist/core/run-analyze.d.ts +49 -0
- package/dist/core/run-analyze.js +257 -0
- package/dist/core/tree-sitter/parser-loader.d.ts +1 -1
- package/dist/core/tree-sitter/parser-loader.js +1 -1
- package/dist/core/wiki/cursor-client.js +2 -7
- package/dist/core/wiki/generator.js +38 -23
- package/dist/core/wiki/graph-queries.js +10 -10
- package/dist/core/wiki/html-viewer.js +7 -3
- package/dist/core/wiki/llm-client.d.ts +23 -2
- package/dist/core/wiki/llm-client.js +96 -26
- package/dist/core/wiki/prompts.js +7 -6
- package/dist/mcp/core/embedder.js +1 -1
- package/dist/mcp/core/lbug-adapter.d.ts +4 -1
- package/dist/mcp/core/lbug-adapter.js +17 -7
- package/dist/mcp/local/local-backend.js +247 -95
- package/dist/mcp/resources.js +14 -6
- package/dist/mcp/server.js +13 -5
- package/dist/mcp/staleness.js +5 -1
- package/dist/mcp/tools.js +100 -23
- package/dist/server/analyze-job.d.ts +53 -0
- package/dist/server/analyze-job.js +146 -0
- package/dist/server/analyze-worker.d.ts +13 -0
- package/dist/server/analyze-worker.js +59 -0
- package/dist/server/api.js +795 -44
- package/dist/server/git-clone.d.ts +25 -0
- package/dist/server/git-clone.js +91 -0
- package/dist/storage/git.js +1 -3
- package/dist/storage/repo-manager.d.ts +5 -2
- package/dist/storage/repo-manager.js +4 -4
- package/dist/types/pipeline.d.ts +1 -21
- package/dist/types/pipeline.js +1 -18
- package/hooks/claude/gitnexus-hook.cjs +52 -22
- package/package.json +13 -13
- package/dist/core/ingestion/utils/language-detection.d.ts +0 -9
- package/dist/core/ingestion/utils/language-detection.js +0 -70
|
@@ -12,7 +12,7 @@ import Rust from 'tree-sitter-rust';
|
|
|
12
12
|
import PHP from 'tree-sitter-php';
|
|
13
13
|
import Ruby from 'tree-sitter-ruby';
|
|
14
14
|
import { createRequire } from 'node:module';
|
|
15
|
-
import { SupportedLanguages } from '
|
|
15
|
+
import { SupportedLanguages } from 'gitnexus-shared';
|
|
16
16
|
import { getProvider } from '../languages/index.js';
|
|
17
17
|
import { getTreeSitterBufferSize, TREE_SITTER_MAX_BUFFER } from '../constants.js';
|
|
18
18
|
// tree-sitter-swift is an optionalDependency — may not be installed
|
|
@@ -34,9 +34,10 @@ try {
|
|
|
34
34
|
Kotlin = _require('tree-sitter-kotlin');
|
|
35
35
|
}
|
|
36
36
|
catch { }
|
|
37
|
-
import { getLanguageFromFilename } from '
|
|
37
|
+
import { getLanguageFromFilename } from 'gitnexus-shared';
|
|
38
38
|
import { FUNCTION_NODE_TYPES, extractFunctionName, getDefinitionNodeFromCaptures, findEnclosingClassId, getLabelFromCaptures, extractMethodSignature, findDescendant, extractStringContent, } from '../utils/ast-helpers.js';
|
|
39
|
-
import { countCallArguments, inferCallForm, extractReceiverName, extractReceiverNode, extractMixedChain, } from '../utils/call-analysis.js';
|
|
39
|
+
import { countCallArguments, inferCallForm, extractReceiverName, extractReceiverNode, extractMixedChain, extractCallArgTypes, } from '../utils/call-analysis.js';
|
|
40
|
+
import { extractParsedCallSite } from '../call-sites/extract-language-call-site.js';
|
|
40
41
|
import { buildTypeEnv } from '../type-env.js';
|
|
41
42
|
import { detectFrameworkFromAST } from '../framework-detection.js';
|
|
42
43
|
import { generateId } from '../../../lib/utils.js';
|
|
@@ -93,7 +94,13 @@ const setLanguage = (language, filePath) => {
|
|
|
93
94
|
const classIdCache = new Map();
|
|
94
95
|
const functionIdCache = new Map();
|
|
95
96
|
const exportCache = new Map();
|
|
96
|
-
const clearCaches = () => {
|
|
97
|
+
const clearCaches = () => {
|
|
98
|
+
classIdCache.clear();
|
|
99
|
+
functionIdCache.clear();
|
|
100
|
+
exportCache.clear();
|
|
101
|
+
fieldInfoCache.clear();
|
|
102
|
+
methodInfoCache.clear();
|
|
103
|
+
};
|
|
97
104
|
// ============================================================================
|
|
98
105
|
// FieldExtractor cache — extract field metadata once per class, reuse for each property.
|
|
99
106
|
// Keyed by class node startIndex (unique per AST node within a file).
|
|
@@ -145,6 +152,34 @@ function getFieldInfo(classNode, provider, context) {
|
|
|
145
152
|
fieldInfoCache.set(cacheKey, cached);
|
|
146
153
|
return cached;
|
|
147
154
|
}
|
|
155
|
+
// ============================================================================
|
|
156
|
+
// MethodExtractor cache — extract method metadata once per class, reuse for each method.
|
|
157
|
+
// Keyed by class node startIndex (unique per AST node within a file).
|
|
158
|
+
// ============================================================================
|
|
159
|
+
const methodInfoCache = new Map();
|
|
160
|
+
/**
|
|
161
|
+
* Get (or extract and cache) method info for a class node.
|
|
162
|
+
* Returns a "name:line" → MethodInfo map, or undefined if the provider has no method extractor
|
|
163
|
+
* or the class yielded no methods.
|
|
164
|
+
* Keyed by name:line (not name alone) to support overloaded methods in Java/Kotlin.
|
|
165
|
+
*/
|
|
166
|
+
function getMethodInfo(classNode, provider, context) {
|
|
167
|
+
if (!provider.methodExtractor)
|
|
168
|
+
return undefined;
|
|
169
|
+
const cacheKey = classNode.startIndex;
|
|
170
|
+
let cached = methodInfoCache.get(cacheKey);
|
|
171
|
+
if (cached)
|
|
172
|
+
return cached;
|
|
173
|
+
const result = provider.methodExtractor.extract(classNode, context);
|
|
174
|
+
if (!result?.methods?.length)
|
|
175
|
+
return undefined;
|
|
176
|
+
cached = new Map();
|
|
177
|
+
for (const method of result.methods) {
|
|
178
|
+
cached.set(`${method.name}:${method.line}`, method);
|
|
179
|
+
}
|
|
180
|
+
methodInfoCache.set(cacheKey, cached);
|
|
181
|
+
return cached;
|
|
182
|
+
}
|
|
148
183
|
/** Walk up AST to find enclosing function, return its generateId or null for top-level.
|
|
149
184
|
* Applies provider.labelOverride so the label matches the definition phase (single source of truth). */
|
|
150
185
|
const findEnclosingFunctionId = (node, filePath, provider) => {
|
|
@@ -248,13 +283,15 @@ const processBatch = (files, onProgress) => {
|
|
|
248
283
|
let totalProcessed = 0;
|
|
249
284
|
let lastReported = 0;
|
|
250
285
|
const PROGRESS_INTERVAL = 100; // report every 100 files
|
|
251
|
-
const onFileProcessed = onProgress
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
lastReported
|
|
255
|
-
|
|
286
|
+
const onFileProcessed = onProgress
|
|
287
|
+
? () => {
|
|
288
|
+
totalProcessed++;
|
|
289
|
+
if (totalProcessed - lastReported >= PROGRESS_INTERVAL) {
|
|
290
|
+
lastReported = totalProcessed;
|
|
291
|
+
onProgress(totalProcessed);
|
|
292
|
+
}
|
|
256
293
|
}
|
|
257
|
-
|
|
294
|
+
: undefined;
|
|
258
295
|
for (const [language, langFiles] of byLanguage) {
|
|
259
296
|
const provider = getProvider(language);
|
|
260
297
|
const queryString = provider.treeSitterQueries;
|
|
@@ -288,7 +325,8 @@ const processBatch = (files, onProgress) => {
|
|
|
288
325
|
}
|
|
289
326
|
}
|
|
290
327
|
else {
|
|
291
|
-
result.skippedLanguages[language] =
|
|
328
|
+
result.skippedLanguages[language] =
|
|
329
|
+
(result.skippedLanguages[language] || 0) + regularFiles.length;
|
|
292
330
|
}
|
|
293
331
|
}
|
|
294
332
|
// Process tsx files separately (different grammar)
|
|
@@ -303,18 +341,35 @@ const processBatch = (files, onProgress) => {
|
|
|
303
341
|
}
|
|
304
342
|
}
|
|
305
343
|
else {
|
|
306
|
-
result.skippedLanguages[language] =
|
|
344
|
+
result.skippedLanguages[language] =
|
|
345
|
+
(result.skippedLanguages[language] || 0) + tsxFiles.length;
|
|
307
346
|
}
|
|
308
347
|
}
|
|
309
348
|
}
|
|
310
349
|
return result;
|
|
311
350
|
};
|
|
312
351
|
const ROUTE_HTTP_METHODS = new Set([
|
|
313
|
-
'get',
|
|
352
|
+
'get',
|
|
353
|
+
'post',
|
|
354
|
+
'put',
|
|
355
|
+
'patch',
|
|
356
|
+
'delete',
|
|
357
|
+
'options',
|
|
358
|
+
'any',
|
|
359
|
+
'match',
|
|
314
360
|
]);
|
|
315
361
|
const ROUTE_RESOURCE_METHODS = new Set(['resource', 'apiResource']);
|
|
316
362
|
// Express/Hono method names that register routes
|
|
317
|
-
const EXPRESS_ROUTE_METHODS = new Set([
|
|
363
|
+
const EXPRESS_ROUTE_METHODS = new Set([
|
|
364
|
+
'get',
|
|
365
|
+
'post',
|
|
366
|
+
'put',
|
|
367
|
+
'delete',
|
|
368
|
+
'patch',
|
|
369
|
+
'all',
|
|
370
|
+
'use',
|
|
371
|
+
'route',
|
|
372
|
+
]);
|
|
318
373
|
// HTTP client methods that are ONLY used by clients, not Express route registration.
|
|
319
374
|
// Methods like get/post/put/delete/patch overlap with Express — those are captured by
|
|
320
375
|
// the express_route handler as route definitions, not consumers. The fetch() global
|
|
@@ -322,9 +377,23 @@ const EXPRESS_ROUTE_METHODS = new Set(['get', 'post', 'put', 'delete', 'patch',
|
|
|
322
377
|
const HTTP_CLIENT_ONLY_METHODS = new Set(['head', 'options', 'request', 'ajax']);
|
|
323
378
|
// Decorator names that indicate HTTP route handlers (NestJS, Flask, FastAPI, Spring)
|
|
324
379
|
const ROUTE_DECORATOR_NAMES = new Set([
|
|
325
|
-
'Get',
|
|
326
|
-
'
|
|
327
|
-
'
|
|
380
|
+
'Get',
|
|
381
|
+
'Post',
|
|
382
|
+
'Put',
|
|
383
|
+
'Delete',
|
|
384
|
+
'Patch',
|
|
385
|
+
'Route',
|
|
386
|
+
'get',
|
|
387
|
+
'post',
|
|
388
|
+
'put',
|
|
389
|
+
'delete',
|
|
390
|
+
'patch',
|
|
391
|
+
'route',
|
|
392
|
+
'RequestMapping',
|
|
393
|
+
'GetMapping',
|
|
394
|
+
'PostMapping',
|
|
395
|
+
'PutMapping',
|
|
396
|
+
'DeleteMapping',
|
|
328
397
|
]);
|
|
329
398
|
const RESOURCE_ACTIONS = ['index', 'create', 'store', 'show', 'edit', 'update', 'destroy'];
|
|
330
399
|
const API_RESOURCE_ACTIONS = ['index', 'store', 'show', 'update', 'destroy'];
|
|
@@ -337,8 +406,7 @@ function isRouteStaticCall(node) {
|
|
|
337
406
|
}
|
|
338
407
|
/** Get the method name from a scoped_call_expression or member_call_expression */
|
|
339
408
|
function getCallMethodName(node) {
|
|
340
|
-
const nameNode = node.childForFieldName?.('name') ??
|
|
341
|
-
node.children?.find((c) => c.type === 'name');
|
|
409
|
+
const nameNode = node.childForFieldName?.('name') ?? node.children?.find((c) => c.type === 'name');
|
|
342
410
|
return nameNode?.text ?? null;
|
|
343
411
|
}
|
|
344
412
|
/** Get the arguments node from a call expression */
|
|
@@ -352,17 +420,17 @@ function findClosureBody(argsNode) {
|
|
|
352
420
|
for (const child of argsNode.children ?? []) {
|
|
353
421
|
if (child.type === 'argument') {
|
|
354
422
|
for (const inner of child.children ?? []) {
|
|
355
|
-
if (inner.type === 'anonymous_function' ||
|
|
356
|
-
inner.
|
|
357
|
-
|
|
358
|
-
|
|
423
|
+
if (inner.type === 'anonymous_function' || inner.type === 'arrow_function') {
|
|
424
|
+
return (inner.childForFieldName?.('body') ??
|
|
425
|
+
inner.children?.find((c) => c.type === 'compound_statement') ??
|
|
426
|
+
null);
|
|
359
427
|
}
|
|
360
428
|
}
|
|
361
429
|
}
|
|
362
|
-
if (child.type === 'anonymous_function' ||
|
|
363
|
-
child.
|
|
364
|
-
|
|
365
|
-
|
|
430
|
+
if (child.type === 'anonymous_function' || child.type === 'arrow_function') {
|
|
431
|
+
return (child.childForFieldName?.('body') ??
|
|
432
|
+
child.children?.find((c) => c.type === 'compound_statement') ??
|
|
433
|
+
null);
|
|
366
434
|
}
|
|
367
435
|
}
|
|
368
436
|
return null;
|
|
@@ -591,7 +659,9 @@ function extractLaravelRoutes(tree, filePath) {
|
|
|
591
659
|
const actions = httpMethod === 'apiResource' ? API_RESOURCE_ACTIONS : RESOURCE_ACTIONS;
|
|
592
660
|
for (const action of actions) {
|
|
593
661
|
routes.push({
|
|
594
|
-
filePath,
|
|
662
|
+
filePath,
|
|
663
|
+
httpMethod,
|
|
664
|
+
routePath,
|
|
595
665
|
controllerName: target.controller ?? effective.controller,
|
|
596
666
|
methodName: action,
|
|
597
667
|
middleware: [...effective.middleware],
|
|
@@ -603,7 +673,9 @@ function extractLaravelRoutes(tree, filePath) {
|
|
|
603
673
|
else {
|
|
604
674
|
const target = extractControllerTarget(argsNode);
|
|
605
675
|
routes.push({
|
|
606
|
-
filePath,
|
|
676
|
+
filePath,
|
|
677
|
+
httpMethod,
|
|
678
|
+
routePath,
|
|
607
679
|
controllerName: target.controller ?? effective.controller,
|
|
608
680
|
methodName: target.method,
|
|
609
681
|
middleware: [...effective.middleware],
|
|
@@ -653,7 +725,8 @@ function extractLaravelRoutes(tree, filePath) {
|
|
|
653
725
|
}
|
|
654
726
|
return;
|
|
655
727
|
}
|
|
656
|
-
if (ROUTE_HTTP_METHODS.has(chain.terminalMethod) ||
|
|
728
|
+
if (ROUTE_HTTP_METHODS.has(chain.terminalMethod) ||
|
|
729
|
+
ROUTE_RESOURCE_METHODS.has(chain.terminalMethod)) {
|
|
657
730
|
emitRoute(chain.terminalMethod, chain.terminalArgs, node.startPosition.row, groupStack, chain.attributes);
|
|
658
731
|
return;
|
|
659
732
|
}
|
|
@@ -736,7 +809,9 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
|
|
|
736
809
|
clearCaches(); // Reset memoization before each new file
|
|
737
810
|
let tree;
|
|
738
811
|
try {
|
|
739
|
-
tree = parser.parse(file.content, undefined, {
|
|
812
|
+
tree = parser.parse(file.content, undefined, {
|
|
813
|
+
bufferSize: getTreeSitterBufferSize(file.content.length),
|
|
814
|
+
});
|
|
740
815
|
}
|
|
741
816
|
catch (err) {
|
|
742
817
|
console.warn(`Failed to parse file ${file.path}: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -783,10 +858,16 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
|
|
|
783
858
|
// Constructor bindings are verified against the SymbolTable in processCallsFromExtracted.
|
|
784
859
|
const parentMap = fileParentMap;
|
|
785
860
|
const provider = getProvider(language);
|
|
786
|
-
const typeEnv = buildTypeEnv(tree, language, {
|
|
861
|
+
const typeEnv = buildTypeEnv(tree, language, {
|
|
862
|
+
parentMap,
|
|
863
|
+
enclosingFunctionFinder: provider?.enclosingFunctionFinder,
|
|
864
|
+
});
|
|
787
865
|
const callRouter = provider.callRouter;
|
|
788
866
|
if (typeEnv.constructorBindings.length > 0) {
|
|
789
|
-
result.constructorBindings.push({
|
|
867
|
+
result.constructorBindings.push({
|
|
868
|
+
filePath: file.path,
|
|
869
|
+
bindings: [...typeEnv.constructorBindings],
|
|
870
|
+
});
|
|
790
871
|
}
|
|
791
872
|
// Extract file-scope bindings for ExportedTypeMap (closes worker/sequential quality gap).
|
|
792
873
|
// Sequential path uses collectExportedBindings(typeEnv) directly; worker path serializes
|
|
@@ -821,12 +902,14 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
|
|
|
821
902
|
continue;
|
|
822
903
|
}
|
|
823
904
|
// Extract assignment sites (field write access)
|
|
824
|
-
if (captureMap['assignment'] &&
|
|
905
|
+
if (captureMap['assignment'] &&
|
|
906
|
+
captureMap['assignment.receiver'] &&
|
|
907
|
+
captureMap['assignment.property']) {
|
|
825
908
|
const receiverText = captureMap['assignment.receiver'].text;
|
|
826
909
|
const propertyName = captureMap['assignment.property'].text;
|
|
827
910
|
if (receiverText && propertyName) {
|
|
828
|
-
const srcId = findEnclosingFunctionId(captureMap['assignment'], file.path, provider)
|
|
829
|
-
|
|
911
|
+
const srcId = findEnclosingFunctionId(captureMap['assignment'], file.path, provider) ||
|
|
912
|
+
generateId('File', file.path);
|
|
830
913
|
let receiverTypeName;
|
|
831
914
|
if (typeEnv) {
|
|
832
915
|
receiverTypeName = typeEnv.lookup(receiverText, captureMap['assignment']) ?? undefined;
|
|
@@ -848,11 +931,16 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
|
|
|
848
931
|
const decoratorArg = captureMap['decorator.arg']?.text;
|
|
849
932
|
const decoratorNode = captureMap['decorator'];
|
|
850
933
|
// Store by the decorator's end line — the definition follows immediately after
|
|
851
|
-
fileDecorators.set(decoratorNode.endPosition.row, {
|
|
934
|
+
fileDecorators.set(decoratorNode.endPosition.row, {
|
|
935
|
+
name: decoratorName,
|
|
936
|
+
arg: decoratorArg,
|
|
937
|
+
});
|
|
852
938
|
if (ROUTE_DECORATOR_NAMES.has(decoratorName)) {
|
|
853
939
|
const routePath = decoratorArg || '';
|
|
854
940
|
const method = decoratorName.replace('Mapping', '').toUpperCase();
|
|
855
|
-
const httpMethod = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'].includes(method)
|
|
941
|
+
const httpMethod = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'].includes(method)
|
|
942
|
+
? method
|
|
943
|
+
: 'GET';
|
|
856
944
|
result.decoratorRoutes.push({
|
|
857
945
|
filePath: file.path,
|
|
858
946
|
routePath,
|
|
@@ -864,7 +952,11 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
|
|
|
864
952
|
// MCP/RPC tool detection: @mcp.tool(), @app.tool(), @server.tool()
|
|
865
953
|
if (decoratorName === 'tool') {
|
|
866
954
|
// Re-store with isTool flag for the definition handler
|
|
867
|
-
fileDecorators.set(decoratorNode.endPosition.row, {
|
|
955
|
+
fileDecorators.set(decoratorNode.endPosition.row, {
|
|
956
|
+
name: decoratorName,
|
|
957
|
+
arg: decoratorArg,
|
|
958
|
+
isTool: true,
|
|
959
|
+
});
|
|
868
960
|
}
|
|
869
961
|
continue;
|
|
870
962
|
}
|
|
@@ -896,11 +988,15 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
|
|
|
896
988
|
continue;
|
|
897
989
|
}
|
|
898
990
|
// Express/Hono route registration: app.get('/path', handler)
|
|
899
|
-
if (captureMap['express_route'] &&
|
|
991
|
+
if (captureMap['express_route'] &&
|
|
992
|
+
captureMap['express_route.method'] &&
|
|
993
|
+
captureMap['express_route.path']) {
|
|
900
994
|
const method = captureMap['express_route.method'].text;
|
|
901
995
|
const routePath = captureMap['express_route.path'].text;
|
|
902
996
|
if (EXPRESS_ROUTE_METHODS.has(method) && routePath.startsWith('/')) {
|
|
903
|
-
const httpMethod = method === 'all' || method === 'use' || method === 'route'
|
|
997
|
+
const httpMethod = method === 'all' || method === 'use' || method === 'route'
|
|
998
|
+
? 'GET'
|
|
999
|
+
: method.toUpperCase();
|
|
904
1000
|
result.decoratorRoutes.push({
|
|
905
1001
|
filePath: file.path,
|
|
906
1002
|
routePath,
|
|
@@ -913,6 +1009,38 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
|
|
|
913
1009
|
}
|
|
914
1010
|
// Extract call sites
|
|
915
1011
|
if (captureMap['call']) {
|
|
1012
|
+
const callNode0 = captureMap['call'];
|
|
1013
|
+
const languageSeed = extractParsedCallSite(language, callNode0);
|
|
1014
|
+
if (languageSeed) {
|
|
1015
|
+
if (!provider.isBuiltInName(languageSeed.calledName)) {
|
|
1016
|
+
const sourceId = findEnclosingFunctionId(callNode0, file.path, provider) ||
|
|
1017
|
+
generateId('File', file.path);
|
|
1018
|
+
const receiverName = languageSeed.callForm === 'member' ? languageSeed.receiverName : undefined;
|
|
1019
|
+
let receiverTypeName = receiverName
|
|
1020
|
+
? typeEnv.lookup(receiverName, callNode0)
|
|
1021
|
+
: undefined;
|
|
1022
|
+
// Type-as-receiver (e.g. Java `User::getName`): no TypeEnv binding for the class name
|
|
1023
|
+
if (receiverName !== undefined &&
|
|
1024
|
+
receiverTypeName === undefined &&
|
|
1025
|
+
languageSeed.callForm === 'member' &&
|
|
1026
|
+
(language === SupportedLanguages.Java ||
|
|
1027
|
+
language === SupportedLanguages.CSharp ||
|
|
1028
|
+
language === SupportedLanguages.Kotlin)) {
|
|
1029
|
+
const c0 = receiverName.charCodeAt(0);
|
|
1030
|
+
if (c0 >= 65 && c0 <= 90)
|
|
1031
|
+
receiverTypeName = receiverName;
|
|
1032
|
+
}
|
|
1033
|
+
result.calls.push({
|
|
1034
|
+
filePath: file.path,
|
|
1035
|
+
calledName: languageSeed.calledName,
|
|
1036
|
+
sourceId,
|
|
1037
|
+
callForm: languageSeed.callForm,
|
|
1038
|
+
...(receiverName !== undefined ? { receiverName } : {}),
|
|
1039
|
+
...(receiverTypeName !== undefined ? { receiverTypeName } : {}),
|
|
1040
|
+
});
|
|
1041
|
+
}
|
|
1042
|
+
continue;
|
|
1043
|
+
}
|
|
916
1044
|
const callNameNode = captureMap['call.name'];
|
|
917
1045
|
if (callNameNode) {
|
|
918
1046
|
const calledName = callNameNode.text;
|
|
@@ -948,7 +1076,10 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
|
|
|
948
1076
|
const classNode = findEnclosingClassNode(captureMap['call']);
|
|
949
1077
|
if (classNode) {
|
|
950
1078
|
routedFieldMap = getFieldInfo(classNode, provider, {
|
|
951
|
-
typeEnv,
|
|
1079
|
+
typeEnv,
|
|
1080
|
+
symbolTable: NOOP_SYMBOL_TABLE,
|
|
1081
|
+
filePath: file.path,
|
|
1082
|
+
language,
|
|
952
1083
|
});
|
|
953
1084
|
}
|
|
954
1085
|
}
|
|
@@ -966,10 +1097,20 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
|
|
|
966
1097
|
language,
|
|
967
1098
|
isExported: true,
|
|
968
1099
|
description: item.accessorType,
|
|
969
|
-
...(item.declaredType
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
1100
|
+
...(item.declaredType
|
|
1101
|
+
? { declaredType: item.declaredType }
|
|
1102
|
+
: routedFieldInfo?.type
|
|
1103
|
+
? { declaredType: routedFieldInfo.type }
|
|
1104
|
+
: {}),
|
|
1105
|
+
...(routedFieldInfo?.visibility !== undefined
|
|
1106
|
+
? { visibility: routedFieldInfo.visibility }
|
|
1107
|
+
: {}),
|
|
1108
|
+
...(routedFieldInfo?.isStatic !== undefined
|
|
1109
|
+
? { isStatic: routedFieldInfo.isStatic }
|
|
1110
|
+
: {}),
|
|
1111
|
+
...(routedFieldInfo?.isReadonly !== undefined
|
|
1112
|
+
? { isReadonly: routedFieldInfo.isReadonly }
|
|
1113
|
+
: {}),
|
|
973
1114
|
},
|
|
974
1115
|
});
|
|
975
1116
|
result.symbols.push({
|
|
@@ -978,10 +1119,20 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
|
|
|
978
1119
|
nodeId,
|
|
979
1120
|
type: 'Property',
|
|
980
1121
|
...(propEnclosingClassId ? { ownerId: propEnclosingClassId } : {}),
|
|
981
|
-
...(item.declaredType
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
1122
|
+
...(item.declaredType
|
|
1123
|
+
? { declaredType: item.declaredType }
|
|
1124
|
+
: routedFieldInfo?.type
|
|
1125
|
+
? { declaredType: routedFieldInfo.type }
|
|
1126
|
+
: {}),
|
|
1127
|
+
...(routedFieldInfo?.visibility !== undefined
|
|
1128
|
+
? { visibility: routedFieldInfo.visibility }
|
|
1129
|
+
: {}),
|
|
1130
|
+
...(routedFieldInfo?.isStatic !== undefined
|
|
1131
|
+
? { isStatic: routedFieldInfo.isStatic }
|
|
1132
|
+
: {}),
|
|
1133
|
+
...(routedFieldInfo?.isReadonly !== undefined
|
|
1134
|
+
? { isReadonly: routedFieldInfo.isReadonly }
|
|
1135
|
+
: {}),
|
|
985
1136
|
});
|
|
986
1137
|
const fileId = generateId('File', file.path);
|
|
987
1138
|
const relId = generateId('DEFINES', `${fileId}->${nodeId}`);
|
|
@@ -1010,11 +1161,13 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
|
|
|
1010
1161
|
}
|
|
1011
1162
|
if (!provider.isBuiltInName(calledName)) {
|
|
1012
1163
|
const callNode = captureMap['call'];
|
|
1013
|
-
const sourceId = findEnclosingFunctionId(callNode, file.path, provider)
|
|
1014
|
-
|
|
1164
|
+
const sourceId = findEnclosingFunctionId(callNode, file.path, provider) ||
|
|
1165
|
+
generateId('File', file.path);
|
|
1015
1166
|
const callForm = inferCallForm(callNode, callNameNode);
|
|
1016
1167
|
let receiverName = callForm === 'member' ? extractReceiverName(callNameNode) : undefined;
|
|
1017
|
-
let receiverTypeName = receiverName
|
|
1168
|
+
let receiverTypeName = receiverName
|
|
1169
|
+
? typeEnv.lookup(receiverName, callNode)
|
|
1170
|
+
: undefined;
|
|
1018
1171
|
let receiverMixedChain;
|
|
1019
1172
|
// When the receiver is a complex expression (call chain, field chain, or mixed),
|
|
1020
1173
|
// extractReceiverName returns undefined. Walk the receiver node to build a unified
|
|
@@ -1034,6 +1187,14 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
|
|
|
1034
1187
|
}
|
|
1035
1188
|
}
|
|
1036
1189
|
}
|
|
1190
|
+
const inferLiteralType = provider.typeConfig?.inferLiteralType;
|
|
1191
|
+
const argCountForOverloadHints = countCallArguments(callNode);
|
|
1192
|
+
// Skip when no arg list / zero args: nothing to infer for overload typing; saves AST walks + payload size.
|
|
1193
|
+
const argTypes = inferLiteralType &&
|
|
1194
|
+
argCountForOverloadHints !== undefined &&
|
|
1195
|
+
argCountForOverloadHints > 0
|
|
1196
|
+
? extractCallArgTypes(callNode, inferLiteralType, (varName, cn) => typeEnv.lookup(varName, cn))
|
|
1197
|
+
: undefined;
|
|
1037
1198
|
result.calls.push({
|
|
1038
1199
|
filePath: file.path,
|
|
1039
1200
|
calledName,
|
|
@@ -1043,6 +1204,7 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
|
|
|
1043
1204
|
...(receiverName !== undefined ? { receiverName } : {}),
|
|
1044
1205
|
...(receiverTypeName !== undefined ? { receiverTypeName } : {}),
|
|
1045
1206
|
...(receiverMixedChain !== undefined ? { receiverMixedChain } : {}),
|
|
1207
|
+
...(argTypes !== undefined ? { argTypes } : {}),
|
|
1046
1208
|
});
|
|
1047
1209
|
}
|
|
1048
1210
|
}
|
|
@@ -1056,8 +1218,7 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
|
|
|
1056
1218
|
// Named fields like `Breed string` also match — skip them.
|
|
1057
1219
|
const extendsNode = captureMap['heritage.extends'];
|
|
1058
1220
|
const fieldDecl = extendsNode.parent;
|
|
1059
|
-
const isNamedField = fieldDecl?.type === 'field_declaration'
|
|
1060
|
-
&& fieldDecl.childForFieldName('name');
|
|
1221
|
+
const isNamedField = fieldDecl?.type === 'field_declaration' && fieldDecl.childForFieldName('name');
|
|
1061
1222
|
if (!isNamedField) {
|
|
1062
1223
|
result.heritage.push({
|
|
1063
1224
|
filePath: file.path,
|
|
@@ -1083,7 +1244,9 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
|
|
|
1083
1244
|
kind: 'trait-impl',
|
|
1084
1245
|
});
|
|
1085
1246
|
}
|
|
1086
|
-
if (captureMap['heritage.extends'] ||
|
|
1247
|
+
if (captureMap['heritage.extends'] ||
|
|
1248
|
+
captureMap['heritage.implements'] ||
|
|
1249
|
+
captureMap['heritage.trait']) {
|
|
1087
1250
|
continue;
|
|
1088
1251
|
}
|
|
1089
1252
|
}
|
|
@@ -1096,7 +1259,11 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
|
|
|
1096
1259
|
continue;
|
|
1097
1260
|
const nodeName = nameNode ? nameNode.text : 'init';
|
|
1098
1261
|
const definitionNode = getDefinitionNodeFromCaptures(captureMap);
|
|
1099
|
-
const startLine = definitionNode
|
|
1262
|
+
const startLine = definitionNode
|
|
1263
|
+
? definitionNode.startPosition.row
|
|
1264
|
+
: nameNode
|
|
1265
|
+
? nameNode.startPosition.row
|
|
1266
|
+
: 0;
|
|
1100
1267
|
const nodeId = generateId(nodeLabel, `${file.path}:${nodeName}`);
|
|
1101
1268
|
const description = provider.descriptionExtractor?.(nodeLabel, nodeName, captureMap);
|
|
1102
1269
|
let frameworkHint = definitionNode
|
|
@@ -1139,15 +1306,70 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
|
|
|
1139
1306
|
let visibility;
|
|
1140
1307
|
let isStatic;
|
|
1141
1308
|
let isReadonly;
|
|
1309
|
+
let isAbstract;
|
|
1310
|
+
let isFinal;
|
|
1311
|
+
let isVirtual;
|
|
1312
|
+
let isOverride;
|
|
1313
|
+
let isAsync;
|
|
1314
|
+
let isPartial;
|
|
1315
|
+
let annotations;
|
|
1142
1316
|
if (nodeLabel === 'Function' || nodeLabel === 'Method' || nodeLabel === 'Constructor') {
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1317
|
+
// Try MethodExtractor first — it provides everything extractMethodSignature does, plus
|
|
1318
|
+
// isAbstract/isFinal/annotations. Only fall back to extractMethodSignature when no
|
|
1319
|
+
// MethodExtractor is available or the method isn't inside a class body.
|
|
1320
|
+
let enrichedByMethodExtractor = false;
|
|
1321
|
+
if (provider.methodExtractor && definitionNode) {
|
|
1322
|
+
const classNode = findEnclosingClassNode(definitionNode);
|
|
1323
|
+
if (classNode) {
|
|
1324
|
+
const methodMap = getMethodInfo(classNode, provider, {
|
|
1325
|
+
filePath: file.path,
|
|
1326
|
+
language,
|
|
1327
|
+
});
|
|
1328
|
+
const defLine = definitionNode.startPosition.row + 1;
|
|
1329
|
+
const info = methodMap?.get(`${nodeName}:${defLine}`);
|
|
1330
|
+
if (info) {
|
|
1331
|
+
enrichedByMethodExtractor = true;
|
|
1332
|
+
parameterCount = info.parameters.length;
|
|
1333
|
+
const types = [];
|
|
1334
|
+
let optionalCount = 0;
|
|
1335
|
+
for (const p of info.parameters) {
|
|
1336
|
+
if (p.type !== null)
|
|
1337
|
+
types.push(p.type);
|
|
1338
|
+
if (p.isOptional)
|
|
1339
|
+
optionalCount++;
|
|
1340
|
+
}
|
|
1341
|
+
parameterTypes = types.length > 0 ? types : undefined;
|
|
1342
|
+
requiredParameterCount =
|
|
1343
|
+
optionalCount > 0 ? parameterCount - optionalCount : undefined;
|
|
1344
|
+
returnType = info.returnType ?? undefined;
|
|
1345
|
+
visibility = info.visibility;
|
|
1346
|
+
isStatic = info.isStatic;
|
|
1347
|
+
isAbstract = info.isAbstract;
|
|
1348
|
+
isFinal = info.isFinal;
|
|
1349
|
+
if (info.isVirtual)
|
|
1350
|
+
isVirtual = info.isVirtual;
|
|
1351
|
+
if (info.isOverride)
|
|
1352
|
+
isOverride = info.isOverride;
|
|
1353
|
+
if (info.isAsync)
|
|
1354
|
+
isAsync = info.isAsync;
|
|
1355
|
+
if (info.isPartial)
|
|
1356
|
+
isPartial = info.isPartial;
|
|
1357
|
+
if (info.annotations.length > 0)
|
|
1358
|
+
annotations = info.annotations;
|
|
1359
|
+
}
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
if (!enrichedByMethodExtractor) {
|
|
1363
|
+
const sig = extractMethodSignature(definitionNode);
|
|
1364
|
+
parameterCount = sig.parameterCount;
|
|
1365
|
+
requiredParameterCount = sig.requiredParameterCount;
|
|
1366
|
+
parameterTypes = sig.parameterTypes;
|
|
1367
|
+
returnType = sig.returnType;
|
|
1368
|
+
}
|
|
1148
1369
|
// Language-specific return type fallback (e.g. Ruby YARD @return [Type])
|
|
1149
1370
|
// Also upgrades uninformative AST types like PHP `array` with PHPDoc `@return User[]`
|
|
1150
|
-
if ((!returnType || returnType === 'array' || returnType === 'iterable') &&
|
|
1371
|
+
if ((!returnType || returnType === 'array' || returnType === 'iterable') &&
|
|
1372
|
+
definitionNode) {
|
|
1151
1373
|
const tc = provider.typeConfig;
|
|
1152
1374
|
if (tc?.extractReturnType) {
|
|
1153
1375
|
const docReturn = tc.extractReturnType(definitionNode);
|
|
@@ -1162,7 +1384,10 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
|
|
|
1162
1384
|
const classNode = findEnclosingClassNode(definitionNode);
|
|
1163
1385
|
if (classNode) {
|
|
1164
1386
|
const fieldMap = getFieldInfo(classNode, provider, {
|
|
1165
|
-
typeEnv,
|
|
1387
|
+
typeEnv,
|
|
1388
|
+
symbolTable: NOOP_SYMBOL_TABLE,
|
|
1389
|
+
filePath: file.path,
|
|
1390
|
+
language,
|
|
1166
1391
|
});
|
|
1167
1392
|
const info = fieldMap?.get(nodeName);
|
|
1168
1393
|
if (info) {
|
|
@@ -1184,10 +1409,12 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
|
|
|
1184
1409
|
endLine: definitionNode ? definitionNode.endPosition.row : startLine,
|
|
1185
1410
|
language: language,
|
|
1186
1411
|
isExported: cachedExportCheck(provider.exportChecker, nameNode || definitionNode, nodeName),
|
|
1187
|
-
...(frameworkHint
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1412
|
+
...(frameworkHint
|
|
1413
|
+
? {
|
|
1414
|
+
astFrameworkMultiplier: frameworkHint.entryPointMultiplier,
|
|
1415
|
+
astFrameworkReason: frameworkHint.reason,
|
|
1416
|
+
}
|
|
1417
|
+
: {}),
|
|
1191
1418
|
...(description !== undefined ? { description } : {}),
|
|
1192
1419
|
...(parameterCount !== undefined ? { parameterCount } : {}),
|
|
1193
1420
|
...(requiredParameterCount !== undefined ? { requiredParameterCount } : {}),
|
|
@@ -1197,12 +1424,24 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
|
|
|
1197
1424
|
...(visibility !== undefined ? { visibility } : {}),
|
|
1198
1425
|
...(isStatic !== undefined ? { isStatic } : {}),
|
|
1199
1426
|
...(isReadonly !== undefined ? { isReadonly } : {}),
|
|
1427
|
+
...(isAbstract !== undefined ? { isAbstract } : {}),
|
|
1428
|
+
...(isFinal !== undefined ? { isFinal } : {}),
|
|
1429
|
+
...(isVirtual !== undefined ? { isVirtual } : {}),
|
|
1430
|
+
...(isOverride !== undefined ? { isOverride } : {}),
|
|
1431
|
+
...(isAsync !== undefined ? { isAsync } : {}),
|
|
1432
|
+
...(isPartial !== undefined ? { isPartial } : {}),
|
|
1433
|
+
...(annotations !== undefined ? { annotations } : {}),
|
|
1200
1434
|
},
|
|
1201
1435
|
});
|
|
1202
1436
|
// Compute enclosing class for Method/Constructor/Property/Function — used for both ownerId and HAS_METHOD
|
|
1203
1437
|
// Function is included because Kotlin/Rust/Python capture class methods as Function nodes
|
|
1204
|
-
const needsOwner = nodeLabel === 'Method' ||
|
|
1205
|
-
|
|
1438
|
+
const needsOwner = nodeLabel === 'Method' ||
|
|
1439
|
+
nodeLabel === 'Constructor' ||
|
|
1440
|
+
nodeLabel === 'Property' ||
|
|
1441
|
+
nodeLabel === 'Function';
|
|
1442
|
+
const enclosingClassId = needsOwner
|
|
1443
|
+
? cachedFindEnclosingClassId(nameNode || definitionNode, file.path)
|
|
1444
|
+
: null;
|
|
1206
1445
|
result.symbols.push({
|
|
1207
1446
|
filePath: file.path,
|
|
1208
1447
|
name: nodeName,
|
|
@@ -1217,6 +1456,13 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
|
|
|
1217
1456
|
...(visibility !== undefined ? { visibility } : {}),
|
|
1218
1457
|
...(isStatic !== undefined ? { isStatic } : {}),
|
|
1219
1458
|
...(isReadonly !== undefined ? { isReadonly } : {}),
|
|
1459
|
+
...(isAbstract !== undefined ? { isAbstract } : {}),
|
|
1460
|
+
...(isFinal !== undefined ? { isFinal } : {}),
|
|
1461
|
+
...(isVirtual !== undefined ? { isVirtual } : {}),
|
|
1462
|
+
...(isOverride !== undefined ? { isOverride } : {}),
|
|
1463
|
+
...(isAsync !== undefined ? { isAsync } : {}),
|
|
1464
|
+
...(isPartial !== undefined ? { isPartial } : {}),
|
|
1465
|
+
...(annotations !== undefined ? { annotations } : {}),
|
|
1220
1466
|
});
|
|
1221
1467
|
const fileId = generateId('File', file.path);
|
|
1222
1468
|
const relId = generateId('DEFINES', `${fileId}->${nodeId}`);
|
|
@@ -1255,8 +1501,22 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
|
|
|
1255
1501
|
// ============================================================================
|
|
1256
1502
|
/** Accumulated result across sub-batches */
|
|
1257
1503
|
let accumulated = {
|
|
1258
|
-
nodes: [],
|
|
1259
|
-
|
|
1504
|
+
nodes: [],
|
|
1505
|
+
relationships: [],
|
|
1506
|
+
symbols: [],
|
|
1507
|
+
imports: [],
|
|
1508
|
+
calls: [],
|
|
1509
|
+
assignments: [],
|
|
1510
|
+
heritage: [],
|
|
1511
|
+
routes: [],
|
|
1512
|
+
fetchCalls: [],
|
|
1513
|
+
decoratorRoutes: [],
|
|
1514
|
+
toolDefs: [],
|
|
1515
|
+
ormQueries: [],
|
|
1516
|
+
constructorBindings: [],
|
|
1517
|
+
typeEnvBindings: [],
|
|
1518
|
+
skippedLanguages: {},
|
|
1519
|
+
fileCount: 0,
|
|
1260
1520
|
};
|
|
1261
1521
|
let cumulativeProcessed = 0;
|
|
1262
1522
|
const mergeResult = (target, src) => {
|
|
@@ -1281,10 +1541,21 @@ const mergeResult = (target, src) => {
|
|
|
1281
1541
|
};
|
|
1282
1542
|
parentPort.on('message', (msg) => {
|
|
1283
1543
|
try {
|
|
1544
|
+
// Legacy single-message mode (backward compat): array of files
|
|
1545
|
+
if (Array.isArray(msg)) {
|
|
1546
|
+
const result = processBatch(msg, (filesProcessed) => {
|
|
1547
|
+
parentPort.postMessage({ type: 'progress', filesProcessed });
|
|
1548
|
+
});
|
|
1549
|
+
parentPort.postMessage({ type: 'result', data: result });
|
|
1550
|
+
return;
|
|
1551
|
+
}
|
|
1284
1552
|
// Sub-batch mode: { type: 'sub-batch', files: [...] }
|
|
1285
|
-
if (msg
|
|
1553
|
+
if (msg.type === 'sub-batch') {
|
|
1286
1554
|
const result = processBatch(msg.files, (filesProcessed) => {
|
|
1287
|
-
parentPort.postMessage({
|
|
1555
|
+
parentPort.postMessage({
|
|
1556
|
+
type: 'progress',
|
|
1557
|
+
filesProcessed: cumulativeProcessed + filesProcessed,
|
|
1558
|
+
});
|
|
1288
1559
|
});
|
|
1289
1560
|
cumulativeProcessed += result.fileCount;
|
|
1290
1561
|
mergeResult(accumulated, result);
|
|
@@ -1293,21 +1564,30 @@ parentPort.on('message', (msg) => {
|
|
|
1293
1564
|
return;
|
|
1294
1565
|
}
|
|
1295
1566
|
// Flush: send accumulated results
|
|
1296
|
-
if (msg
|
|
1567
|
+
if (msg.type === 'flush') {
|
|
1297
1568
|
parentPort.postMessage({ type: 'result', data: accumulated });
|
|
1298
1569
|
// Reset for potential reuse
|
|
1299
|
-
accumulated = {
|
|
1570
|
+
accumulated = {
|
|
1571
|
+
nodes: [],
|
|
1572
|
+
relationships: [],
|
|
1573
|
+
symbols: [],
|
|
1574
|
+
imports: [],
|
|
1575
|
+
calls: [],
|
|
1576
|
+
assignments: [],
|
|
1577
|
+
heritage: [],
|
|
1578
|
+
routes: [],
|
|
1579
|
+
fetchCalls: [],
|
|
1580
|
+
decoratorRoutes: [],
|
|
1581
|
+
toolDefs: [],
|
|
1582
|
+
ormQueries: [],
|
|
1583
|
+
constructorBindings: [],
|
|
1584
|
+
typeEnvBindings: [],
|
|
1585
|
+
skippedLanguages: {},
|
|
1586
|
+
fileCount: 0,
|
|
1587
|
+
};
|
|
1300
1588
|
cumulativeProcessed = 0;
|
|
1301
1589
|
return;
|
|
1302
1590
|
}
|
|
1303
|
-
// Legacy single-message mode (backward compat): array of files
|
|
1304
|
-
if (Array.isArray(msg)) {
|
|
1305
|
-
const result = processBatch(msg, (filesProcessed) => {
|
|
1306
|
-
parentPort.postMessage({ type: 'progress', filesProcessed });
|
|
1307
|
-
});
|
|
1308
|
-
parentPort.postMessage({ type: 'result', data: result });
|
|
1309
|
-
return;
|
|
1310
|
-
}
|
|
1311
1591
|
}
|
|
1312
1592
|
catch (err) {
|
|
1313
1593
|
const message = err instanceof Error ? err.message : String(err);
|