gitnexus 1.4.7 → 1.4.9
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 +29 -1
- package/dist/cli/ai-context.d.ts +1 -1
- package/dist/cli/ai-context.js +1 -1
- package/dist/cli/analyze.d.ts +2 -0
- package/dist/cli/analyze.js +54 -21
- package/dist/cli/index-repo.d.ts +15 -0
- package/dist/cli/index-repo.js +115 -0
- package/dist/cli/index.js +13 -3
- package/dist/cli/setup.js +90 -10
- package/dist/cli/wiki.d.ts +4 -0
- package/dist/cli/wiki.js +174 -53
- package/dist/config/supported-languages.d.ts +33 -1
- package/dist/config/supported-languages.js +32 -0
- package/dist/core/embeddings/embedder.d.ts +6 -1
- package/dist/core/embeddings/embedder.js +65 -5
- package/dist/core/embeddings/embedding-pipeline.js +11 -9
- package/dist/core/embeddings/http-client.d.ts +31 -0
- package/dist/core/embeddings/http-client.js +179 -0
- package/dist/core/embeddings/index.d.ts +1 -0
- package/dist/core/embeddings/index.js +1 -0
- package/dist/core/embeddings/types.d.ts +1 -1
- package/dist/core/graph/graph.js +9 -1
- package/dist/core/graph/types.d.ts +11 -2
- package/dist/core/ingestion/call-processor.d.ts +66 -2
- package/dist/core/ingestion/call-processor.js +650 -30
- package/dist/core/ingestion/call-routing.d.ts +9 -18
- package/dist/core/ingestion/call-routing.js +0 -19
- package/dist/core/ingestion/cobol/cobol-copy-expander.d.ts +57 -0
- package/dist/core/ingestion/cobol/cobol-copy-expander.js +385 -0
- package/dist/core/ingestion/cobol/cobol-preprocessor.d.ts +210 -0
- package/dist/core/ingestion/cobol/cobol-preprocessor.js +1509 -0
- package/dist/core/ingestion/cobol/jcl-parser.d.ts +68 -0
- package/dist/core/ingestion/cobol/jcl-parser.js +217 -0
- package/dist/core/ingestion/cobol/jcl-processor.d.ts +33 -0
- package/dist/core/ingestion/cobol/jcl-processor.js +229 -0
- package/dist/core/ingestion/cobol-processor.d.ts +54 -0
- package/dist/core/ingestion/cobol-processor.js +1186 -0
- package/dist/core/ingestion/entry-point-scoring.d.ts +17 -0
- package/dist/core/ingestion/entry-point-scoring.js +52 -28
- package/dist/core/ingestion/export-detection.d.ts +47 -8
- package/dist/core/ingestion/export-detection.js +29 -50
- package/dist/core/ingestion/field-extractor.d.ts +29 -0
- package/dist/core/ingestion/field-extractor.js +25 -0
- package/dist/core/ingestion/field-extractors/configs/c-cpp.d.ts +3 -0
- package/dist/core/ingestion/field-extractors/configs/c-cpp.js +108 -0
- package/dist/core/ingestion/field-extractors/configs/csharp.d.ts +8 -0
- package/dist/core/ingestion/field-extractors/configs/csharp.js +73 -0
- package/dist/core/ingestion/field-extractors/configs/dart.d.ts +8 -0
- package/dist/core/ingestion/field-extractors/configs/dart.js +76 -0
- package/dist/core/ingestion/field-extractors/configs/go.d.ts +11 -0
- package/dist/core/ingestion/field-extractors/configs/go.js +64 -0
- package/dist/core/ingestion/field-extractors/configs/helpers.d.ts +44 -0
- package/dist/core/ingestion/field-extractors/configs/helpers.js +134 -0
- package/dist/core/ingestion/field-extractors/configs/jvm.d.ts +3 -0
- package/dist/core/ingestion/field-extractors/configs/jvm.js +118 -0
- package/dist/core/ingestion/field-extractors/configs/php.d.ts +8 -0
- package/dist/core/ingestion/field-extractors/configs/php.js +67 -0
- package/dist/core/ingestion/field-extractors/configs/python.d.ts +12 -0
- package/dist/core/ingestion/field-extractors/configs/python.js +91 -0
- package/dist/core/ingestion/field-extractors/configs/ruby.d.ts +16 -0
- package/dist/core/ingestion/field-extractors/configs/ruby.js +75 -0
- package/dist/core/ingestion/field-extractors/configs/rust.d.ts +9 -0
- package/dist/core/ingestion/field-extractors/configs/rust.js +55 -0
- package/dist/core/ingestion/field-extractors/configs/swift.d.ts +8 -0
- package/dist/core/ingestion/field-extractors/configs/swift.js +63 -0
- package/dist/core/ingestion/field-extractors/configs/typescript-javascript.d.ts +3 -0
- package/dist/core/ingestion/field-extractors/configs/typescript-javascript.js +60 -0
- package/dist/core/ingestion/field-extractors/generic.d.ts +46 -0
- package/dist/core/ingestion/field-extractors/generic.js +111 -0
- package/dist/core/ingestion/field-extractors/typescript.d.ts +77 -0
- package/dist/core/ingestion/field-extractors/typescript.js +291 -0
- package/dist/core/ingestion/field-types.d.ts +59 -0
- package/dist/core/ingestion/field-types.js +2 -0
- package/dist/core/ingestion/framework-detection.d.ts +97 -2
- package/dist/core/ingestion/framework-detection.js +114 -14
- package/dist/core/ingestion/heritage-processor.js +62 -66
- package/dist/core/ingestion/import-processor.d.ts +9 -10
- package/dist/core/ingestion/import-processor.js +150 -196
- package/dist/core/ingestion/{resolvers → import-resolvers}/csharp.d.ts +6 -9
- package/dist/core/ingestion/{resolvers → import-resolvers}/csharp.js +20 -2
- package/dist/core/ingestion/import-resolvers/dart.d.ts +7 -0
- package/dist/core/ingestion/import-resolvers/dart.js +44 -0
- package/dist/core/ingestion/{resolvers → import-resolvers}/go.d.ts +4 -5
- package/dist/core/ingestion/{resolvers → import-resolvers}/go.js +17 -0
- package/dist/core/ingestion/{resolvers → import-resolvers}/jvm.d.ts +10 -1
- package/dist/core/ingestion/import-resolvers/jvm.js +159 -0
- package/dist/core/ingestion/import-resolvers/php.d.ts +25 -0
- package/dist/core/ingestion/import-resolvers/php.js +80 -0
- package/dist/core/ingestion/{resolvers → import-resolvers}/python.d.ts +9 -3
- package/dist/core/ingestion/{resolvers → import-resolvers}/python.js +35 -3
- package/dist/core/ingestion/{resolvers → import-resolvers}/ruby.d.ts +5 -2
- package/dist/core/ingestion/{resolvers → import-resolvers}/ruby.js +7 -2
- package/dist/core/ingestion/{resolvers → import-resolvers}/rust.d.ts +5 -2
- package/dist/core/ingestion/{resolvers → import-resolvers}/rust.js +41 -2
- package/dist/core/ingestion/{resolvers → import-resolvers}/standard.d.ts +15 -7
- package/dist/core/ingestion/{resolvers → import-resolvers}/standard.js +22 -3
- package/dist/core/ingestion/import-resolvers/swift.d.ts +7 -0
- package/dist/core/ingestion/import-resolvers/swift.js +23 -0
- package/dist/core/ingestion/import-resolvers/types.d.ts +44 -0
- package/dist/core/ingestion/import-resolvers/types.js +6 -0
- package/dist/core/ingestion/{resolvers → import-resolvers}/utils.d.ts +2 -0
- package/dist/core/ingestion/{resolvers → import-resolvers}/utils.js +7 -0
- package/dist/core/ingestion/language-config.d.ts +6 -0
- package/dist/core/ingestion/language-config.js +13 -0
- package/dist/core/ingestion/language-provider.d.ts +121 -0
- package/dist/core/ingestion/language-provider.js +24 -0
- package/dist/core/ingestion/languages/c-cpp.d.ts +12 -0
- package/dist/core/ingestion/languages/c-cpp.js +71 -0
- package/dist/core/ingestion/languages/cobol.d.ts +1 -0
- package/dist/core/ingestion/languages/cobol.js +26 -0
- package/dist/core/ingestion/languages/csharp.d.ts +8 -0
- package/dist/core/ingestion/languages/csharp.js +49 -0
- package/dist/core/ingestion/languages/dart.d.ts +12 -0
- package/dist/core/ingestion/languages/dart.js +58 -0
- package/dist/core/ingestion/languages/go.d.ts +11 -0
- package/dist/core/ingestion/languages/go.js +28 -0
- package/dist/core/ingestion/languages/index.d.ts +38 -0
- package/dist/core/ingestion/languages/index.js +63 -0
- package/dist/core/ingestion/languages/java.d.ts +9 -0
- package/dist/core/ingestion/languages/java.js +29 -0
- package/dist/core/ingestion/languages/kotlin.d.ts +9 -0
- package/dist/core/ingestion/languages/kotlin.js +53 -0
- package/dist/core/ingestion/languages/php.d.ts +8 -0
- package/dist/core/ingestion/languages/php.js +145 -0
- package/dist/core/ingestion/languages/python.d.ts +12 -0
- package/dist/core/ingestion/languages/python.js +39 -0
- package/dist/core/ingestion/languages/ruby.d.ts +9 -0
- package/dist/core/ingestion/languages/ruby.js +44 -0
- package/dist/core/ingestion/languages/rust.d.ts +12 -0
- package/dist/core/ingestion/languages/rust.js +44 -0
- package/dist/core/ingestion/languages/swift.d.ts +12 -0
- package/dist/core/ingestion/languages/swift.js +133 -0
- package/dist/core/ingestion/languages/typescript.d.ts +10 -0
- package/dist/core/ingestion/languages/typescript.js +60 -0
- package/dist/core/ingestion/markdown-processor.d.ts +17 -0
- package/dist/core/ingestion/markdown-processor.js +124 -0
- package/dist/core/ingestion/mro-processor.js +22 -18
- package/dist/core/ingestion/named-binding-processor.d.ts +18 -0
- package/dist/core/ingestion/named-binding-processor.js +42 -0
- package/dist/core/ingestion/named-bindings/csharp.d.ts +3 -0
- package/dist/core/ingestion/named-bindings/csharp.js +37 -0
- package/dist/core/ingestion/named-bindings/java.d.ts +3 -0
- package/dist/core/ingestion/named-bindings/java.js +29 -0
- package/dist/core/ingestion/named-bindings/kotlin.d.ts +3 -0
- package/dist/core/ingestion/named-bindings/kotlin.js +36 -0
- package/dist/core/ingestion/named-bindings/php.d.ts +3 -0
- package/dist/core/ingestion/named-bindings/php.js +61 -0
- package/dist/core/ingestion/named-bindings/python.d.ts +3 -0
- package/dist/core/ingestion/named-bindings/python.js +49 -0
- package/dist/core/ingestion/named-bindings/rust.d.ts +3 -0
- package/dist/core/ingestion/named-bindings/rust.js +64 -0
- package/dist/core/ingestion/named-bindings/types.d.ts +16 -0
- package/dist/core/ingestion/named-bindings/types.js +6 -0
- package/dist/core/ingestion/named-bindings/typescript.d.ts +3 -0
- package/dist/core/ingestion/named-bindings/typescript.js +58 -0
- package/dist/core/ingestion/parsing-processor.d.ts +6 -2
- package/dist/core/ingestion/parsing-processor.js +125 -85
- package/dist/core/ingestion/pipeline.d.ts +10 -0
- package/dist/core/ingestion/pipeline.js +1235 -317
- package/dist/core/ingestion/resolution-context.d.ts +5 -0
- package/dist/core/ingestion/resolution-context.js +8 -5
- package/dist/core/ingestion/route-extractors/expo.d.ts +1 -0
- package/dist/core/ingestion/route-extractors/expo.js +36 -0
- package/dist/core/ingestion/route-extractors/middleware.d.ts +47 -0
- package/dist/core/ingestion/route-extractors/middleware.js +143 -0
- package/dist/core/ingestion/route-extractors/nextjs.d.ts +3 -0
- package/dist/core/ingestion/route-extractors/nextjs.js +76 -0
- package/dist/core/ingestion/route-extractors/php.d.ts +7 -0
- package/dist/core/ingestion/route-extractors/php.js +21 -0
- package/dist/core/ingestion/route-extractors/response-shapes.d.ts +20 -0
- package/dist/core/ingestion/route-extractors/response-shapes.js +290 -0
- package/dist/core/ingestion/symbol-table.d.ts +16 -0
- package/dist/core/ingestion/symbol-table.js +20 -6
- package/dist/core/ingestion/tree-sitter-queries.d.ts +10 -9
- package/dist/core/ingestion/tree-sitter-queries.js +274 -11
- package/dist/core/ingestion/type-env.d.ts +42 -18
- package/dist/core/ingestion/type-env.js +481 -106
- package/dist/core/ingestion/type-extractors/c-cpp.d.ts +5 -0
- package/dist/core/ingestion/type-extractors/c-cpp.js +119 -0
- package/dist/core/ingestion/type-extractors/csharp.js +149 -16
- package/dist/core/ingestion/type-extractors/dart.d.ts +15 -0
- package/dist/core/ingestion/type-extractors/dart.js +371 -0
- package/dist/core/ingestion/type-extractors/jvm.js +169 -66
- package/dist/core/ingestion/type-extractors/rust.js +35 -1
- package/dist/core/ingestion/type-extractors/shared.d.ts +1 -15
- package/dist/core/ingestion/type-extractors/shared.js +14 -112
- package/dist/core/ingestion/type-extractors/swift.js +338 -7
- package/dist/core/ingestion/type-extractors/types.d.ts +40 -8
- package/dist/core/ingestion/type-extractors/typescript.js +141 -9
- package/dist/core/ingestion/utils/ast-helpers.d.ts +83 -0
- package/dist/core/ingestion/utils/ast-helpers.js +817 -0
- package/dist/core/ingestion/utils/call-analysis.d.ts +73 -0
- package/dist/core/ingestion/utils/call-analysis.js +527 -0
- package/dist/core/ingestion/utils/event-loop.d.ts +5 -0
- package/dist/core/ingestion/utils/event-loop.js +5 -0
- package/dist/core/ingestion/utils/language-detection.d.ts +9 -0
- package/dist/core/ingestion/utils/language-detection.js +70 -0
- package/dist/core/ingestion/utils/verbose.d.ts +1 -0
- package/dist/core/ingestion/utils/verbose.js +7 -0
- package/dist/core/ingestion/workers/parse-worker.d.ts +55 -5
- package/dist/core/ingestion/workers/parse-worker.js +415 -225
- package/dist/core/lbug/csv-generator.js +51 -1
- package/dist/core/lbug/lbug-adapter.d.ts +10 -0
- package/dist/core/lbug/lbug-adapter.js +75 -4
- package/dist/core/lbug/schema.d.ts +8 -4
- package/dist/core/lbug/schema.js +65 -4
- package/dist/core/tree-sitter/parser-loader.js +7 -1
- package/dist/core/wiki/cursor-client.d.ts +31 -0
- package/dist/core/wiki/cursor-client.js +127 -0
- package/dist/core/wiki/generator.d.ts +28 -9
- package/dist/core/wiki/generator.js +115 -18
- package/dist/core/wiki/graph-queries.d.ts +4 -0
- package/dist/core/wiki/graph-queries.js +7 -1
- package/dist/core/wiki/llm-client.d.ts +2 -0
- package/dist/core/wiki/llm-client.js +8 -4
- package/dist/core/wiki/prompts.d.ts +3 -3
- package/dist/core/wiki/prompts.js +6 -0
- package/dist/mcp/core/embedder.js +11 -3
- package/dist/mcp/core/lbug-adapter.d.ts +5 -0
- package/dist/mcp/core/lbug-adapter.js +23 -2
- package/dist/mcp/local/local-backend.d.ts +38 -5
- package/dist/mcp/local/local-backend.js +804 -63
- package/dist/mcp/resources.js +2 -0
- package/dist/mcp/tools.js +73 -4
- package/dist/server/api.d.ts +19 -1
- package/dist/server/api.js +66 -6
- package/dist/storage/git.d.ts +12 -0
- package/dist/storage/git.js +21 -0
- package/dist/storage/repo-manager.d.ts +3 -0
- package/package.json +25 -16
- package/dist/core/ingestion/named-binding-extraction.d.ts +0 -61
- package/dist/core/ingestion/named-binding-extraction.js +0 -363
- package/dist/core/ingestion/resolvers/index.d.ts +0 -18
- package/dist/core/ingestion/resolvers/index.js +0 -13
- package/dist/core/ingestion/resolvers/jvm.js +0 -87
- package/dist/core/ingestion/resolvers/php.d.ts +0 -15
- package/dist/core/ingestion/resolvers/php.js +0 -35
- package/dist/core/ingestion/type-extractors/index.d.ts +0 -22
- package/dist/core/ingestion/type-extractors/index.js +0 -31
- package/dist/core/ingestion/utils.d.ts +0 -138
- package/dist/core/ingestion/utils.js +0 -1290
- package/scripts/patch-tree-sitter-swift.cjs +0 -74
|
@@ -13,7 +13,7 @@ import PHP from 'tree-sitter-php';
|
|
|
13
13
|
import Ruby from 'tree-sitter-ruby';
|
|
14
14
|
import { createRequire } from 'node:module';
|
|
15
15
|
import { SupportedLanguages } from '../../../config/supported-languages.js';
|
|
16
|
-
import {
|
|
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
|
|
19
19
|
const _require = createRequire(import.meta.url);
|
|
@@ -22,22 +22,26 @@ try {
|
|
|
22
22
|
Swift = _require('tree-sitter-swift');
|
|
23
23
|
}
|
|
24
24
|
catch { }
|
|
25
|
+
// tree-sitter-dart is an optionalDependency — may not be installed
|
|
26
|
+
let Dart = null;
|
|
27
|
+
try {
|
|
28
|
+
Dart = _require('tree-sitter-dart');
|
|
29
|
+
}
|
|
30
|
+
catch { }
|
|
25
31
|
// tree-sitter-kotlin is an optionalDependency — may not be installed
|
|
26
32
|
let Kotlin = null;
|
|
27
33
|
try {
|
|
28
34
|
Kotlin = _require('tree-sitter-kotlin');
|
|
29
35
|
}
|
|
30
36
|
catch { }
|
|
31
|
-
import { getLanguageFromFilename
|
|
37
|
+
import { getLanguageFromFilename } from '../utils/language-detection.js';
|
|
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';
|
|
32
40
|
import { buildTypeEnv } from '../type-env.js';
|
|
33
|
-
import { isNodeExported } from '../export-detection.js';
|
|
34
41
|
import { detectFrameworkFromAST } from '../framework-detection.js';
|
|
35
|
-
import { typeConfigs } from '../type-extractors/index.js';
|
|
36
42
|
import { generateId } from '../../../lib/utils.js';
|
|
37
|
-
import {
|
|
38
|
-
import {
|
|
39
|
-
import { callRouters } from '../call-routing.js';
|
|
40
|
-
import { extractPropertyDeclaredType } from '../type-extractors/shared.js';
|
|
43
|
+
import { preprocessImportPath } from '../import-processor.js';
|
|
44
|
+
import { CLASS_CONTAINER_TYPES } from '../utils/ast-helpers.js';
|
|
41
45
|
// ============================================================================
|
|
42
46
|
// Worker-local parser + language map
|
|
43
47
|
// ============================================================================
|
|
@@ -56,6 +60,7 @@ const languageMap = {
|
|
|
56
60
|
...(Kotlin ? { [SupportedLanguages.Kotlin]: Kotlin } : {}),
|
|
57
61
|
[SupportedLanguages.PHP]: PHP.php_only,
|
|
58
62
|
[SupportedLanguages.Ruby]: Ruby,
|
|
63
|
+
...(Dart ? { [SupportedLanguages.Dart]: Dart } : {}),
|
|
59
64
|
...(Swift ? { [SupportedLanguages.Swift]: Swift } : {}),
|
|
60
65
|
};
|
|
61
66
|
/**
|
|
@@ -79,79 +84,131 @@ const setLanguage = (language, filePath) => {
|
|
|
79
84
|
throw new Error(`Unsupported language: ${language}`);
|
|
80
85
|
parser.setLanguage(lang);
|
|
81
86
|
};
|
|
82
|
-
// isNodeExported imported from ../export-detection.js (shared module)
|
|
83
87
|
// ============================================================================
|
|
84
|
-
//
|
|
88
|
+
// Per-file O(1) memoization — avoids repeated parent-chain walks per symbol.
|
|
89
|
+
// Three bare Maps cleared at file boundaries. Map.get() returns undefined for
|
|
90
|
+
// missing keys, so `cached !== undefined` distinguishes "not computed" from
|
|
91
|
+
// a stored null (enclosing class/function not found = top-level).
|
|
92
|
+
// ============================================================================
|
|
93
|
+
const classIdCache = new Map();
|
|
94
|
+
const functionIdCache = new Map();
|
|
95
|
+
const exportCache = new Map();
|
|
96
|
+
const clearCaches = () => { classIdCache.clear(); functionIdCache.clear(); exportCache.clear(); fieldInfoCache.clear(); };
|
|
97
|
+
// ============================================================================
|
|
98
|
+
// FieldExtractor cache — extract field metadata once per class, reuse for each property.
|
|
99
|
+
// Keyed by class node startIndex (unique per AST node within a file).
|
|
85
100
|
// ============================================================================
|
|
86
|
-
|
|
87
|
-
|
|
101
|
+
const fieldInfoCache = new Map();
|
|
102
|
+
/**
|
|
103
|
+
* Walk up from a definition node to find the nearest enclosing class/struct/interface
|
|
104
|
+
* AST node. Returns the SyntaxNode itself (not an ID) for passing to FieldExtractor.
|
|
105
|
+
*/
|
|
106
|
+
function findEnclosingClassNode(node) {
|
|
107
|
+
let current = node.parent;
|
|
108
|
+
while (current) {
|
|
109
|
+
if (CLASS_CONTAINER_TYPES.has(current.type)) {
|
|
110
|
+
return current;
|
|
111
|
+
}
|
|
112
|
+
current = current.parent;
|
|
113
|
+
}
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Minimal no-op SymbolTable stub for FieldExtractorContext in the worker.
|
|
118
|
+
* Field extraction only uses symbolTable.lookupExactAll for optional type resolution —
|
|
119
|
+
* returning [] causes the extractor to use the raw type string, which is fine for us.
|
|
120
|
+
*/
|
|
121
|
+
const NOOP_SYMBOL_TABLE = {
|
|
122
|
+
lookupExactAll: () => [],
|
|
123
|
+
lookupExact: () => undefined,
|
|
124
|
+
lookupExactFull: () => undefined,
|
|
125
|
+
};
|
|
126
|
+
/**
|
|
127
|
+
* Get (or extract and cache) field info for a class node.
|
|
128
|
+
* Returns a name→FieldInfo map, or undefined if the provider has no field extractor
|
|
129
|
+
* or the class yielded no fields.
|
|
130
|
+
*/
|
|
131
|
+
function getFieldInfo(classNode, provider, context) {
|
|
132
|
+
if (!provider.fieldExtractor)
|
|
133
|
+
return undefined;
|
|
134
|
+
const cacheKey = classNode.startIndex;
|
|
135
|
+
let cached = fieldInfoCache.get(cacheKey);
|
|
136
|
+
if (cached)
|
|
137
|
+
return cached;
|
|
138
|
+
const result = provider.fieldExtractor.extract(classNode, context);
|
|
139
|
+
if (!result?.fields?.length)
|
|
140
|
+
return undefined;
|
|
141
|
+
cached = new Map();
|
|
142
|
+
for (const field of result.fields) {
|
|
143
|
+
cached.set(field.name, field);
|
|
144
|
+
}
|
|
145
|
+
fieldInfoCache.set(cacheKey, cached);
|
|
146
|
+
return cached;
|
|
147
|
+
}
|
|
148
|
+
/** Walk up AST to find enclosing function, return its generateId or null for top-level.
|
|
149
|
+
* Applies provider.labelOverride so the label matches the definition phase (single source of truth). */
|
|
150
|
+
const findEnclosingFunctionId = (node, filePath, provider) => {
|
|
151
|
+
const cached = functionIdCache.get(node);
|
|
152
|
+
if (cached !== undefined)
|
|
153
|
+
return cached;
|
|
88
154
|
let current = node.parent;
|
|
89
155
|
while (current) {
|
|
90
156
|
if (FUNCTION_NODE_TYPES.has(current.type)) {
|
|
91
157
|
const { funcName, label } = extractFunctionName(current);
|
|
92
158
|
if (funcName) {
|
|
93
|
-
|
|
159
|
+
// Apply labelOverride so label matches definition phase (e.g., Kotlin Function→Method).
|
|
160
|
+
// null means "skip as definition" — keep original label for scope identification.
|
|
161
|
+
let finalLabel = label;
|
|
162
|
+
if (provider.labelOverride) {
|
|
163
|
+
const override = provider.labelOverride(current, label);
|
|
164
|
+
if (override !== null)
|
|
165
|
+
finalLabel = override;
|
|
166
|
+
}
|
|
167
|
+
const result = generateId(finalLabel, `${filePath}:${funcName}`);
|
|
168
|
+
functionIdCache.set(node, result);
|
|
169
|
+
return result;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
// Language-specific enclosing function resolution (e.g., Dart where
|
|
173
|
+
// function_body is a sibling of function_signature, not a child).
|
|
174
|
+
if (provider.enclosingFunctionFinder) {
|
|
175
|
+
const customResult = provider.enclosingFunctionFinder(current);
|
|
176
|
+
if (customResult) {
|
|
177
|
+
let finalLabel = customResult.label;
|
|
178
|
+
if (provider.labelOverride) {
|
|
179
|
+
const override = provider.labelOverride(current.previousSibling, finalLabel);
|
|
180
|
+
if (override !== null)
|
|
181
|
+
finalLabel = override;
|
|
182
|
+
}
|
|
183
|
+
const result = generateId(finalLabel, `${filePath}:${customResult.funcName}`);
|
|
184
|
+
functionIdCache.set(node, result);
|
|
185
|
+
return result;
|
|
94
186
|
}
|
|
95
187
|
}
|
|
96
188
|
current = current.parent;
|
|
97
189
|
}
|
|
190
|
+
functionIdCache.set(node, null);
|
|
98
191
|
return null;
|
|
99
192
|
};
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
if (captureMap['definition.struct'])
|
|
118
|
-
return 'Struct';
|
|
119
|
-
if (captureMap['definition.enum'])
|
|
120
|
-
return 'Enum';
|
|
121
|
-
if (captureMap['definition.namespace'])
|
|
122
|
-
return 'Namespace';
|
|
123
|
-
if (captureMap['definition.module'])
|
|
124
|
-
return 'Module';
|
|
125
|
-
if (captureMap['definition.trait'])
|
|
126
|
-
return 'Trait';
|
|
127
|
-
if (captureMap['definition.impl'])
|
|
128
|
-
return 'Impl';
|
|
129
|
-
if (captureMap['definition.type'])
|
|
130
|
-
return 'TypeAlias';
|
|
131
|
-
if (captureMap['definition.const'])
|
|
132
|
-
return 'Const';
|
|
133
|
-
if (captureMap['definition.static'])
|
|
134
|
-
return 'Static';
|
|
135
|
-
if (captureMap['definition.typedef'])
|
|
136
|
-
return 'Typedef';
|
|
137
|
-
if (captureMap['definition.macro'])
|
|
138
|
-
return 'Macro';
|
|
139
|
-
if (captureMap['definition.union'])
|
|
140
|
-
return 'Union';
|
|
141
|
-
if (captureMap['definition.property'])
|
|
142
|
-
return 'Property';
|
|
143
|
-
if (captureMap['definition.record'])
|
|
144
|
-
return 'Record';
|
|
145
|
-
if (captureMap['definition.delegate'])
|
|
146
|
-
return 'Delegate';
|
|
147
|
-
if (captureMap['definition.annotation'])
|
|
148
|
-
return 'Annotation';
|
|
149
|
-
if (captureMap['definition.constructor'])
|
|
150
|
-
return 'Constructor';
|
|
151
|
-
if (captureMap['definition.template'])
|
|
152
|
-
return 'Template';
|
|
153
|
-
return 'CodeElement';
|
|
193
|
+
/** Cached wrapper for findEnclosingClassId — avoids repeated parent walks. */
|
|
194
|
+
const cachedFindEnclosingClassId = (node, filePath) => {
|
|
195
|
+
const cached = classIdCache.get(node);
|
|
196
|
+
if (cached !== undefined)
|
|
197
|
+
return cached;
|
|
198
|
+
const result = findEnclosingClassId(node, filePath);
|
|
199
|
+
classIdCache.set(node, result);
|
|
200
|
+
return result;
|
|
201
|
+
};
|
|
202
|
+
/** Cached wrapper for export checking — avoids repeated parent walks per symbol. */
|
|
203
|
+
const cachedExportCheck = (checker, node, name) => {
|
|
204
|
+
const cached = exportCache.get(node);
|
|
205
|
+
if (cached !== undefined)
|
|
206
|
+
return cached;
|
|
207
|
+
const result = checker(node, name);
|
|
208
|
+
exportCache.set(node, result);
|
|
209
|
+
return result;
|
|
154
210
|
};
|
|
211
|
+
// Label detection moved to shared getLabelFromCaptures in utils.ts
|
|
155
212
|
// DEFINITION_CAPTURE_KEYS and getDefinitionNodeFromCaptures imported from ../utils.js
|
|
156
213
|
// ============================================================================
|
|
157
214
|
// Process a batch of files
|
|
@@ -166,7 +223,12 @@ const processBatch = (files, onProgress) => {
|
|
|
166
223
|
assignments: [],
|
|
167
224
|
heritage: [],
|
|
168
225
|
routes: [],
|
|
226
|
+
fetchCalls: [],
|
|
227
|
+
decoratorRoutes: [],
|
|
228
|
+
toolDefs: [],
|
|
229
|
+
ormQueries: [],
|
|
169
230
|
constructorBindings: [],
|
|
231
|
+
typeEnvBindings: [],
|
|
170
232
|
skippedLanguages: {},
|
|
171
233
|
fileCount: 0,
|
|
172
234
|
};
|
|
@@ -194,7 +256,8 @@ const processBatch = (files, onProgress) => {
|
|
|
194
256
|
}
|
|
195
257
|
} : undefined;
|
|
196
258
|
for (const [language, langFiles] of byLanguage) {
|
|
197
|
-
const
|
|
259
|
+
const provider = getProvider(language);
|
|
260
|
+
const queryString = provider.treeSitterQueries;
|
|
198
261
|
if (!queryString)
|
|
199
262
|
continue;
|
|
200
263
|
// Track if we need to handle tsx separately
|
|
@@ -246,114 +309,23 @@ const processBatch = (files, onProgress) => {
|
|
|
246
309
|
}
|
|
247
310
|
return result;
|
|
248
311
|
};
|
|
249
|
-
// ============================================================================
|
|
250
|
-
// PHP Eloquent metadata extraction
|
|
251
|
-
// ============================================================================
|
|
252
|
-
/** Eloquent model properties whose array values are worth indexing */
|
|
253
|
-
const ELOQUENT_ARRAY_PROPS = new Set(['fillable', 'casts', 'hidden', 'guarded', 'with', 'appends']);
|
|
254
|
-
/** Eloquent relationship method names */
|
|
255
|
-
const ELOQUENT_RELATIONS = new Set([
|
|
256
|
-
'hasMany', 'hasOne', 'belongsTo', 'belongsToMany',
|
|
257
|
-
'morphTo', 'morphMany', 'morphOne', 'morphToMany', 'morphedByMany',
|
|
258
|
-
'hasManyThrough', 'hasOneThrough',
|
|
259
|
-
]);
|
|
260
|
-
function findDescendant(node, type) {
|
|
261
|
-
if (node.type === type)
|
|
262
|
-
return node;
|
|
263
|
-
for (const child of (node.children ?? [])) {
|
|
264
|
-
const found = findDescendant(child, type);
|
|
265
|
-
if (found)
|
|
266
|
-
return found;
|
|
267
|
-
}
|
|
268
|
-
return null;
|
|
269
|
-
}
|
|
270
|
-
function extractStringContent(node) {
|
|
271
|
-
if (!node)
|
|
272
|
-
return null;
|
|
273
|
-
const content = node.children?.find((c) => c.type === 'string_content');
|
|
274
|
-
if (content)
|
|
275
|
-
return content.text;
|
|
276
|
-
if (node.type === 'string_content')
|
|
277
|
-
return node.text;
|
|
278
|
-
return null;
|
|
279
|
-
}
|
|
280
|
-
/**
|
|
281
|
-
* For a PHP property_declaration node, extract array values as a description string.
|
|
282
|
-
* Returns null if not an Eloquent model property or no array values found.
|
|
283
|
-
*/
|
|
284
|
-
function extractPhpPropertyDescription(propName, propDeclNode) {
|
|
285
|
-
if (!ELOQUENT_ARRAY_PROPS.has(propName))
|
|
286
|
-
return null;
|
|
287
|
-
const arrayNode = findDescendant(propDeclNode, 'array_creation_expression');
|
|
288
|
-
if (!arrayNode)
|
|
289
|
-
return null;
|
|
290
|
-
const items = [];
|
|
291
|
-
for (const child of (arrayNode.children ?? [])) {
|
|
292
|
-
if (child.type !== 'array_element_initializer')
|
|
293
|
-
continue;
|
|
294
|
-
const children = child.children ?? [];
|
|
295
|
-
const arrowIdx = children.findIndex((c) => c.type === '=>');
|
|
296
|
-
if (arrowIdx !== -1) {
|
|
297
|
-
// key => value pair (used in $casts)
|
|
298
|
-
const key = extractStringContent(children[arrowIdx - 1]);
|
|
299
|
-
const val = extractStringContent(children[arrowIdx + 1]);
|
|
300
|
-
if (key && val)
|
|
301
|
-
items.push(`${key}:${val}`);
|
|
302
|
-
}
|
|
303
|
-
else {
|
|
304
|
-
// Simple value (used in $fillable, $hidden, etc.)
|
|
305
|
-
const val = extractStringContent(children[0]);
|
|
306
|
-
if (val)
|
|
307
|
-
items.push(val);
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
return items.length > 0 ? items.join(', ') : null;
|
|
311
|
-
}
|
|
312
|
-
/**
|
|
313
|
-
* For a PHP method_declaration node, detect if it defines an Eloquent relationship.
|
|
314
|
-
* Returns description like "hasMany(Post)" or null.
|
|
315
|
-
*/
|
|
316
|
-
function extractEloquentRelationDescription(methodNode) {
|
|
317
|
-
function findRelationCall(node) {
|
|
318
|
-
if (node.type === 'member_call_expression') {
|
|
319
|
-
const children = node.children ?? [];
|
|
320
|
-
const objectNode = children.find((c) => c.type === 'variable_name' && c.text === '$this');
|
|
321
|
-
const nameNode = children.find((c) => c.type === 'name');
|
|
322
|
-
if (objectNode && nameNode && ELOQUENT_RELATIONS.has(nameNode.text))
|
|
323
|
-
return node;
|
|
324
|
-
}
|
|
325
|
-
for (const child of (node.children ?? [])) {
|
|
326
|
-
const found = findRelationCall(child);
|
|
327
|
-
if (found)
|
|
328
|
-
return found;
|
|
329
|
-
}
|
|
330
|
-
return null;
|
|
331
|
-
}
|
|
332
|
-
const callNode = findRelationCall(methodNode);
|
|
333
|
-
if (!callNode)
|
|
334
|
-
return null;
|
|
335
|
-
const relType = callNode.children?.find((c) => c.type === 'name')?.text;
|
|
336
|
-
const argsNode = callNode.children?.find((c) => c.type === 'arguments');
|
|
337
|
-
let targetModel = null;
|
|
338
|
-
if (argsNode) {
|
|
339
|
-
const firstArg = argsNode.children?.find((c) => c.type === 'argument');
|
|
340
|
-
if (firstArg) {
|
|
341
|
-
const classConstant = firstArg.children?.find((c) => c.type === 'class_constant_access_expression');
|
|
342
|
-
if (classConstant) {
|
|
343
|
-
targetModel = classConstant.children?.find((c) => c.type === 'name')?.text ?? null;
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
if (relType && targetModel)
|
|
348
|
-
return `${relType}(${targetModel})`;
|
|
349
|
-
if (relType)
|
|
350
|
-
return relType;
|
|
351
|
-
return null;
|
|
352
|
-
}
|
|
353
312
|
const ROUTE_HTTP_METHODS = new Set([
|
|
354
313
|
'get', 'post', 'put', 'patch', 'delete', 'options', 'any', 'match',
|
|
355
314
|
]);
|
|
356
315
|
const ROUTE_RESOURCE_METHODS = new Set(['resource', 'apiResource']);
|
|
316
|
+
// Express/Hono method names that register routes
|
|
317
|
+
const EXPRESS_ROUTE_METHODS = new Set(['get', 'post', 'put', 'delete', 'patch', 'all', 'use', 'route']);
|
|
318
|
+
// HTTP client methods that are ONLY used by clients, not Express route registration.
|
|
319
|
+
// Methods like get/post/put/delete/patch overlap with Express — those are captured by
|
|
320
|
+
// the express_route handler as route definitions, not consumers. The fetch() global
|
|
321
|
+
// function is captured separately by the route.fetch query.
|
|
322
|
+
const HTTP_CLIENT_ONLY_METHODS = new Set(['head', 'options', 'request', 'ajax']);
|
|
323
|
+
// Decorator names that indicate HTTP route handlers (NestJS, Flask, FastAPI, Spring)
|
|
324
|
+
const ROUTE_DECORATOR_NAMES = new Set([
|
|
325
|
+
'Get', 'Post', 'Put', 'Delete', 'Patch', 'Route',
|
|
326
|
+
'get', 'post', 'put', 'delete', 'patch', 'route',
|
|
327
|
+
'RequestMapping', 'GetMapping', 'PostMapping', 'PutMapping', 'DeleteMapping',
|
|
328
|
+
]);
|
|
357
329
|
const RESOURCE_ACTIONS = ['index', 'create', 'store', 'show', 'edit', 'update', 'destroy'];
|
|
358
330
|
const API_RESOURCE_ACTIONS = ['index', 'store', 'show', 'update', 'destroy'];
|
|
359
331
|
/** Check if node is a scoped_call_expression with object 'Route' */
|
|
@@ -697,6 +669,50 @@ function extractLaravelRoutes(tree, filePath) {
|
|
|
697
669
|
walk(tree.rootNode, []);
|
|
698
670
|
return routes;
|
|
699
671
|
}
|
|
672
|
+
// ============================================================================
|
|
673
|
+
// ORM Query Detection (Prisma + Supabase)
|
|
674
|
+
// ============================================================================
|
|
675
|
+
const PRISMA_QUERY_RE = /\bprisma\.(\w+)\.(findMany|findFirst|findUnique|findUniqueOrThrow|findFirstOrThrow|create|createMany|update|updateMany|delete|deleteMany|upsert|count|aggregate|groupBy)\s*\(/g;
|
|
676
|
+
const SUPABASE_QUERY_RE = /\bsupabase\.from\s*\(\s*['"](\w+)['"]\s*\)\s*\.(select|insert|update|delete|upsert)\s*\(/g;
|
|
677
|
+
/**
|
|
678
|
+
* Extract ORM query calls from file content via regex.
|
|
679
|
+
* Appends results to the provided array (avoids allocation when no matches).
|
|
680
|
+
*/
|
|
681
|
+
export function extractORMQueries(filePath, content, out) {
|
|
682
|
+
const hasPrisma = content.includes('prisma.');
|
|
683
|
+
const hasSupabase = content.includes('supabase.from');
|
|
684
|
+
if (!hasPrisma && !hasSupabase)
|
|
685
|
+
return;
|
|
686
|
+
if (hasPrisma) {
|
|
687
|
+
PRISMA_QUERY_RE.lastIndex = 0;
|
|
688
|
+
let m;
|
|
689
|
+
while ((m = PRISMA_QUERY_RE.exec(content)) !== null) {
|
|
690
|
+
const model = m[1];
|
|
691
|
+
if (model.startsWith('$'))
|
|
692
|
+
continue;
|
|
693
|
+
out.push({
|
|
694
|
+
filePath,
|
|
695
|
+
orm: 'prisma',
|
|
696
|
+
model,
|
|
697
|
+
method: m[2],
|
|
698
|
+
lineNumber: content.substring(0, m.index).split('\n').length - 1,
|
|
699
|
+
});
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
if (hasSupabase) {
|
|
703
|
+
SUPABASE_QUERY_RE.lastIndex = 0;
|
|
704
|
+
let m;
|
|
705
|
+
while ((m = SUPABASE_QUERY_RE.exec(content)) !== null) {
|
|
706
|
+
out.push({
|
|
707
|
+
filePath,
|
|
708
|
+
orm: 'supabase',
|
|
709
|
+
model: m[1],
|
|
710
|
+
method: m[2],
|
|
711
|
+
lineNumber: content.substring(0, m.index).split('\n').length - 1,
|
|
712
|
+
});
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
}
|
|
700
716
|
const processFileGroup = (files, language, queryString, result, onFileProcessed) => {
|
|
701
717
|
let query;
|
|
702
718
|
try {
|
|
@@ -717,6 +733,7 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
|
|
|
717
733
|
// Skip files larger than the max tree-sitter buffer (32 MB)
|
|
718
734
|
if (file.content.length > TREE_SITTER_MAX_BUFFER)
|
|
719
735
|
continue;
|
|
736
|
+
clearCaches(); // Reset memoization before each new file
|
|
720
737
|
let tree;
|
|
721
738
|
try {
|
|
722
739
|
tree = parser.parse(file.content, undefined, { bufferSize: getTreeSitterBufferSize(file.content.length) });
|
|
@@ -727,13 +744,6 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
|
|
|
727
744
|
}
|
|
728
745
|
result.fileCount++;
|
|
729
746
|
onFileProcessed?.();
|
|
730
|
-
// Build per-file type environment + constructor bindings in a single AST walk.
|
|
731
|
-
// Constructor bindings are verified against the SymbolTable in processCallsFromExtracted.
|
|
732
|
-
const typeEnv = buildTypeEnv(tree, language);
|
|
733
|
-
const callRouter = callRouters[language];
|
|
734
|
-
if (typeEnv.constructorBindings.length > 0) {
|
|
735
|
-
result.constructorBindings.push({ filePath: file.path, bindings: [...typeEnv.constructorBindings] });
|
|
736
|
-
}
|
|
737
747
|
let matches;
|
|
738
748
|
try {
|
|
739
749
|
matches = query.matches(tree.rootNode);
|
|
@@ -742,6 +752,54 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
|
|
|
742
752
|
console.warn(`Query execution failed for ${file.path}: ${err instanceof Error ? err.message : String(err)}`);
|
|
743
753
|
continue;
|
|
744
754
|
}
|
|
755
|
+
// Pre-pass: extract heritage from query matches to build parentMap for buildTypeEnv.
|
|
756
|
+
// Heritage edges (EXTENDS/IMPLEMENTS) are created by heritage-processor which runs
|
|
757
|
+
// in PARALLEL with call-processor, so the graph edges don't exist when buildTypeEnv
|
|
758
|
+
// runs. This pre-pass makes parent class information available for type resolution.
|
|
759
|
+
const fileParentMap = new Map();
|
|
760
|
+
for (const match of matches) {
|
|
761
|
+
const captureMap = {};
|
|
762
|
+
for (const c of match.captures) {
|
|
763
|
+
captureMap[c.name] = c.node;
|
|
764
|
+
}
|
|
765
|
+
if (captureMap['heritage.class'] && captureMap['heritage.extends']) {
|
|
766
|
+
const className = captureMap['heritage.class'].text;
|
|
767
|
+
const parentName = captureMap['heritage.extends'].text;
|
|
768
|
+
// Skip Go named fields (only anonymous fields are struct embedding)
|
|
769
|
+
const extendsNode = captureMap['heritage.extends'];
|
|
770
|
+
const fieldDecl = extendsNode.parent;
|
|
771
|
+
if (fieldDecl?.type === 'field_declaration' && fieldDecl.childForFieldName('name'))
|
|
772
|
+
continue;
|
|
773
|
+
let parents = fileParentMap.get(className);
|
|
774
|
+
if (!parents) {
|
|
775
|
+
parents = [];
|
|
776
|
+
fileParentMap.set(className, parents);
|
|
777
|
+
}
|
|
778
|
+
if (!parents.includes(parentName))
|
|
779
|
+
parents.push(parentName);
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
// Build per-file type environment + constructor bindings in a single AST walk.
|
|
783
|
+
// Constructor bindings are verified against the SymbolTable in processCallsFromExtracted.
|
|
784
|
+
const parentMap = fileParentMap;
|
|
785
|
+
const provider = getProvider(language);
|
|
786
|
+
const typeEnv = buildTypeEnv(tree, language, { parentMap, enclosingFunctionFinder: provider?.enclosingFunctionFinder });
|
|
787
|
+
const callRouter = provider.callRouter;
|
|
788
|
+
if (typeEnv.constructorBindings.length > 0) {
|
|
789
|
+
result.constructorBindings.push({ filePath: file.path, bindings: [...typeEnv.constructorBindings] });
|
|
790
|
+
}
|
|
791
|
+
// Extract file-scope bindings for ExportedTypeMap (closes worker/sequential quality gap).
|
|
792
|
+
// Sequential path uses collectExportedBindings(typeEnv) directly; worker path serializes
|
|
793
|
+
// these bindings so the main thread can merge them into ExportedTypeMap.
|
|
794
|
+
const fileScope = typeEnv.fileScope();
|
|
795
|
+
if (fileScope.size > 0) {
|
|
796
|
+
const bindings = [];
|
|
797
|
+
for (const [name, type] of fileScope)
|
|
798
|
+
bindings.push([name, type]);
|
|
799
|
+
result.typeEnvBindings.push({ filePath: file.path, bindings });
|
|
800
|
+
}
|
|
801
|
+
// Per-file map: decorator end-line → decorator info, for associating with definitions
|
|
802
|
+
const fileDecorators = new Map();
|
|
745
803
|
for (const match of matches) {
|
|
746
804
|
const captureMap = {};
|
|
747
805
|
for (const c of match.captures) {
|
|
@@ -749,10 +807,11 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
|
|
|
749
807
|
}
|
|
750
808
|
// Extract import paths before skipping
|
|
751
809
|
if (captureMap['import'] && captureMap['import.source']) {
|
|
752
|
-
const rawImportPath =
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
const
|
|
810
|
+
const rawImportPath = preprocessImportPath(captureMap['import.source'].text, captureMap['import'], provider);
|
|
811
|
+
if (!rawImportPath)
|
|
812
|
+
continue;
|
|
813
|
+
const extractor = provider.namedBindingExtractor;
|
|
814
|
+
const namedBindings = extractor ? extractor(captureMap['import']) : undefined;
|
|
756
815
|
result.imports.push({
|
|
757
816
|
filePath: file.path,
|
|
758
817
|
rawImportPath,
|
|
@@ -766,7 +825,7 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
|
|
|
766
825
|
const receiverText = captureMap['assignment.receiver'].text;
|
|
767
826
|
const propertyName = captureMap['assignment.property'].text;
|
|
768
827
|
if (receiverText && propertyName) {
|
|
769
|
-
const srcId = findEnclosingFunctionId(captureMap['assignment'], file.path)
|
|
828
|
+
const srcId = findEnclosingFunctionId(captureMap['assignment'], file.path, provider)
|
|
770
829
|
|| generateId('File', file.path);
|
|
771
830
|
let receiverTypeName;
|
|
772
831
|
if (typeEnv) {
|
|
@@ -783,13 +842,82 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
|
|
|
783
842
|
if (!captureMap['call'])
|
|
784
843
|
continue;
|
|
785
844
|
}
|
|
845
|
+
// Store decorator metadata for later association with definitions
|
|
846
|
+
if (captureMap['decorator'] && captureMap['decorator.name']) {
|
|
847
|
+
const decoratorName = captureMap['decorator.name'].text;
|
|
848
|
+
const decoratorArg = captureMap['decorator.arg']?.text;
|
|
849
|
+
const decoratorNode = captureMap['decorator'];
|
|
850
|
+
// Store by the decorator's end line — the definition follows immediately after
|
|
851
|
+
fileDecorators.set(decoratorNode.endPosition.row, { name: decoratorName, arg: decoratorArg });
|
|
852
|
+
if (ROUTE_DECORATOR_NAMES.has(decoratorName)) {
|
|
853
|
+
const routePath = decoratorArg || '';
|
|
854
|
+
const method = decoratorName.replace('Mapping', '').toUpperCase();
|
|
855
|
+
const httpMethod = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'].includes(method) ? method : 'GET';
|
|
856
|
+
result.decoratorRoutes.push({
|
|
857
|
+
filePath: file.path,
|
|
858
|
+
routePath,
|
|
859
|
+
httpMethod,
|
|
860
|
+
decoratorName,
|
|
861
|
+
lineNumber: decoratorNode.startPosition.row,
|
|
862
|
+
});
|
|
863
|
+
}
|
|
864
|
+
// MCP/RPC tool detection: @mcp.tool(), @app.tool(), @server.tool()
|
|
865
|
+
if (decoratorName === 'tool') {
|
|
866
|
+
// Re-store with isTool flag for the definition handler
|
|
867
|
+
fileDecorators.set(decoratorNode.endPosition.row, { name: decoratorName, arg: decoratorArg, isTool: true });
|
|
868
|
+
}
|
|
869
|
+
continue;
|
|
870
|
+
}
|
|
871
|
+
// Extract HTTP consumer URLs: fetch(), axios.get(), $.get(), requests.get(), etc.
|
|
872
|
+
if (captureMap['route.fetch']) {
|
|
873
|
+
const urlNode = captureMap['route.url'] ?? captureMap['route.template_url'];
|
|
874
|
+
if (urlNode) {
|
|
875
|
+
result.fetchCalls.push({
|
|
876
|
+
filePath: file.path,
|
|
877
|
+
fetchURL: urlNode.text,
|
|
878
|
+
lineNumber: captureMap['route.fetch'].startPosition.row,
|
|
879
|
+
});
|
|
880
|
+
}
|
|
881
|
+
continue;
|
|
882
|
+
}
|
|
883
|
+
// HTTP client calls: axios.get('/path'), $.post('/path'), requests.get('/path')
|
|
884
|
+
// Skip methods also in EXPRESS_ROUTE_METHODS to avoid double-registering Express
|
|
885
|
+
// routes as both route definitions AND consumers (both queries match same AST node)
|
|
886
|
+
if (captureMap['http_client'] && captureMap['http_client.url']) {
|
|
887
|
+
const method = captureMap['http_client.method']?.text;
|
|
888
|
+
const url = captureMap['http_client.url'].text;
|
|
889
|
+
if (method && HTTP_CLIENT_ONLY_METHODS.has(method) && url.startsWith('/')) {
|
|
890
|
+
result.fetchCalls.push({
|
|
891
|
+
filePath: file.path,
|
|
892
|
+
fetchURL: url,
|
|
893
|
+
lineNumber: captureMap['http_client'].startPosition.row,
|
|
894
|
+
});
|
|
895
|
+
}
|
|
896
|
+
continue;
|
|
897
|
+
}
|
|
898
|
+
// Express/Hono route registration: app.get('/path', handler)
|
|
899
|
+
if (captureMap['express_route'] && captureMap['express_route.method'] && captureMap['express_route.path']) {
|
|
900
|
+
const method = captureMap['express_route.method'].text;
|
|
901
|
+
const routePath = captureMap['express_route.path'].text;
|
|
902
|
+
if (EXPRESS_ROUTE_METHODS.has(method) && routePath.startsWith('/')) {
|
|
903
|
+
const httpMethod = method === 'all' || method === 'use' || method === 'route' ? 'GET' : method.toUpperCase();
|
|
904
|
+
result.decoratorRoutes.push({
|
|
905
|
+
filePath: file.path,
|
|
906
|
+
routePath,
|
|
907
|
+
httpMethod,
|
|
908
|
+
decoratorName: `express.${method}`,
|
|
909
|
+
lineNumber: captureMap['express_route'].startPosition.row,
|
|
910
|
+
});
|
|
911
|
+
}
|
|
912
|
+
continue;
|
|
913
|
+
}
|
|
786
914
|
// Extract call sites
|
|
787
915
|
if (captureMap['call']) {
|
|
788
916
|
const callNameNode = captureMap['call.name'];
|
|
789
917
|
if (callNameNode) {
|
|
790
918
|
const calledName = callNameNode.text;
|
|
791
919
|
// Dispatch: route language-specific calls (heritage, properties, imports)
|
|
792
|
-
const routed = callRouter(calledName, captureMap['call']);
|
|
920
|
+
const routed = callRouter?.(calledName, captureMap['call']);
|
|
793
921
|
if (routed) {
|
|
794
922
|
if (routed.kind === 'skip')
|
|
795
923
|
continue;
|
|
@@ -813,8 +941,19 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
|
|
|
813
941
|
continue;
|
|
814
942
|
}
|
|
815
943
|
if (routed.kind === 'properties') {
|
|
816
|
-
const propEnclosingClassId =
|
|
944
|
+
const propEnclosingClassId = cachedFindEnclosingClassId(captureMap['call'], file.path);
|
|
945
|
+
// Enrich routed properties with FieldExtractor metadata
|
|
946
|
+
let routedFieldMap;
|
|
947
|
+
if (provider.fieldExtractor && typeEnv) {
|
|
948
|
+
const classNode = findEnclosingClassNode(captureMap['call']);
|
|
949
|
+
if (classNode) {
|
|
950
|
+
routedFieldMap = getFieldInfo(classNode, provider, {
|
|
951
|
+
typeEnv, symbolTable: NOOP_SYMBOL_TABLE, filePath: file.path, language,
|
|
952
|
+
});
|
|
953
|
+
}
|
|
954
|
+
}
|
|
817
955
|
for (const item of routed.items) {
|
|
956
|
+
const routedFieldInfo = routedFieldMap?.get(item.propName);
|
|
818
957
|
const nodeId = generateId('Property', `${file.path}:${item.propName}`);
|
|
819
958
|
result.nodes.push({
|
|
820
959
|
id: nodeId,
|
|
@@ -827,6 +966,10 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
|
|
|
827
966
|
language,
|
|
828
967
|
isExported: true,
|
|
829
968
|
description: item.accessorType,
|
|
969
|
+
...(item.declaredType ? { declaredType: item.declaredType } : routedFieldInfo?.type ? { declaredType: routedFieldInfo.type } : {}),
|
|
970
|
+
...(routedFieldInfo?.visibility !== undefined ? { visibility: routedFieldInfo.visibility } : {}),
|
|
971
|
+
...(routedFieldInfo?.isStatic !== undefined ? { isStatic: routedFieldInfo.isStatic } : {}),
|
|
972
|
+
...(routedFieldInfo?.isReadonly !== undefined ? { isReadonly: routedFieldInfo.isReadonly } : {}),
|
|
830
973
|
},
|
|
831
974
|
});
|
|
832
975
|
result.symbols.push({
|
|
@@ -835,7 +978,10 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
|
|
|
835
978
|
nodeId,
|
|
836
979
|
type: 'Property',
|
|
837
980
|
...(propEnclosingClassId ? { ownerId: propEnclosingClassId } : {}),
|
|
838
|
-
...(item.declaredType ? { declaredType: item.declaredType } : {}),
|
|
981
|
+
...(item.declaredType ? { declaredType: item.declaredType } : routedFieldInfo?.type ? { declaredType: routedFieldInfo.type } : {}),
|
|
982
|
+
...(routedFieldInfo?.visibility !== undefined ? { visibility: routedFieldInfo.visibility } : {}),
|
|
983
|
+
...(routedFieldInfo?.isStatic !== undefined ? { isStatic: routedFieldInfo.isStatic } : {}),
|
|
984
|
+
...(routedFieldInfo?.isReadonly !== undefined ? { isReadonly: routedFieldInfo.isReadonly } : {}),
|
|
839
985
|
});
|
|
840
986
|
const fileId = generateId('File', file.path);
|
|
841
987
|
const relId = generateId('DEFINES', `${fileId}->${nodeId}`);
|
|
@@ -862,9 +1008,9 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
|
|
|
862
1008
|
}
|
|
863
1009
|
// kind === 'call' — fall through to normal call processing below
|
|
864
1010
|
}
|
|
865
|
-
if (!
|
|
1011
|
+
if (!provider.isBuiltInName(calledName)) {
|
|
866
1012
|
const callNode = captureMap['call'];
|
|
867
|
-
const sourceId = findEnclosingFunctionId(callNode, file.path)
|
|
1013
|
+
const sourceId = findEnclosingFunctionId(callNode, file.path, provider)
|
|
868
1014
|
|| generateId('File', file.path);
|
|
869
1015
|
const callForm = inferCallForm(callNode, callNameNode);
|
|
870
1016
|
let receiverName = callForm === 'member' ? extractReceiverName(callNameNode) : undefined;
|
|
@@ -941,24 +1087,9 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
|
|
|
941
1087
|
continue;
|
|
942
1088
|
}
|
|
943
1089
|
}
|
|
944
|
-
const nodeLabel = getLabelFromCaptures(captureMap);
|
|
1090
|
+
const nodeLabel = getLabelFromCaptures(captureMap, provider);
|
|
945
1091
|
if (!nodeLabel)
|
|
946
1092
|
continue;
|
|
947
|
-
// C/C++: @definition.function is broad and also matches inline class methods (inside
|
|
948
|
-
// a class/struct body). Those are already captured by @definition.method, so skip
|
|
949
|
-
// the duplicate Function entry to prevent double-indexing in globalIndex.
|
|
950
|
-
if ((language === SupportedLanguages.CPlusPlus || language === SupportedLanguages.C) &&
|
|
951
|
-
nodeLabel === 'Function') {
|
|
952
|
-
let ancestor = captureMap['definition.function']?.parent;
|
|
953
|
-
while (ancestor) {
|
|
954
|
-
if (ancestor.type === 'class_specifier' || ancestor.type === 'struct_specifier') {
|
|
955
|
-
break; // inside a class body — duplicate of @definition.method
|
|
956
|
-
}
|
|
957
|
-
ancestor = ancestor.parent;
|
|
958
|
-
}
|
|
959
|
-
if (ancestor)
|
|
960
|
-
continue; // found a class/struct ancestor → skip
|
|
961
|
-
}
|
|
962
1093
|
const nameNode = captureMap['name'];
|
|
963
1094
|
// Synthesize name for constructors without explicit @name capture (e.g. Swift init)
|
|
964
1095
|
if (!nameNode && nodeLabel !== 'Constructor')
|
|
@@ -967,29 +1098,57 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
|
|
|
967
1098
|
const definitionNode = getDefinitionNodeFromCaptures(captureMap);
|
|
968
1099
|
const startLine = definitionNode ? definitionNode.startPosition.row : (nameNode ? nameNode.startPosition.row : 0);
|
|
969
1100
|
const nodeId = generateId(nodeLabel, `${file.path}:${nodeName}`);
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
if (nodeLabel === 'Property' && captureMap['definition.property']) {
|
|
973
|
-
description = extractPhpPropertyDescription(nodeName, captureMap['definition.property']) ?? undefined;
|
|
974
|
-
}
|
|
975
|
-
else if (nodeLabel === 'Method' && captureMap['definition.method']) {
|
|
976
|
-
description = extractEloquentRelationDescription(captureMap['definition.method']) ?? undefined;
|
|
977
|
-
}
|
|
978
|
-
}
|
|
979
|
-
const frameworkHint = definitionNode
|
|
1101
|
+
const description = provider.descriptionExtractor?.(nodeLabel, nodeName, captureMap);
|
|
1102
|
+
let frameworkHint = definitionNode
|
|
980
1103
|
? detectFrameworkFromAST(language, (definitionNode.text || '').slice(0, 300))
|
|
981
1104
|
: null;
|
|
1105
|
+
// Decorators appear on lines immediately before their definition; allow up to
|
|
1106
|
+
// MAX_DECORATOR_SCAN_LINES gap for blank lines / multi-line decorator stacks.
|
|
1107
|
+
const MAX_DECORATOR_SCAN_LINES = 5;
|
|
1108
|
+
if (definitionNode) {
|
|
1109
|
+
const defStartLine = definitionNode.startPosition.row;
|
|
1110
|
+
for (let checkLine = defStartLine - 1; checkLine >= Math.max(0, defStartLine - MAX_DECORATOR_SCAN_LINES); checkLine--) {
|
|
1111
|
+
const dec = fileDecorators.get(checkLine);
|
|
1112
|
+
if (dec) {
|
|
1113
|
+
// Use first (closest) decorator found for framework hint
|
|
1114
|
+
if (!frameworkHint) {
|
|
1115
|
+
frameworkHint = {
|
|
1116
|
+
framework: 'decorator',
|
|
1117
|
+
entryPointMultiplier: 1.2,
|
|
1118
|
+
reason: `@${dec.name}${dec.arg ? `("${dec.arg}")` : ''}`,
|
|
1119
|
+
};
|
|
1120
|
+
}
|
|
1121
|
+
// Emit tool definition if this is a @tool decorator
|
|
1122
|
+
if (dec.isTool) {
|
|
1123
|
+
result.toolDefs.push({
|
|
1124
|
+
filePath: file.path,
|
|
1125
|
+
toolName: nodeName,
|
|
1126
|
+
description: dec.arg || '',
|
|
1127
|
+
lineNumber: definitionNode.startPosition.row,
|
|
1128
|
+
});
|
|
1129
|
+
}
|
|
1130
|
+
fileDecorators.delete(checkLine);
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
982
1134
|
let parameterCount;
|
|
1135
|
+
let requiredParameterCount;
|
|
1136
|
+
let parameterTypes;
|
|
983
1137
|
let returnType;
|
|
984
1138
|
let declaredType;
|
|
1139
|
+
let visibility;
|
|
1140
|
+
let isStatic;
|
|
1141
|
+
let isReadonly;
|
|
985
1142
|
if (nodeLabel === 'Function' || nodeLabel === 'Method' || nodeLabel === 'Constructor') {
|
|
986
1143
|
const sig = extractMethodSignature(definitionNode);
|
|
987
1144
|
parameterCount = sig.parameterCount;
|
|
1145
|
+
requiredParameterCount = sig.requiredParameterCount;
|
|
1146
|
+
parameterTypes = sig.parameterTypes;
|
|
988
1147
|
returnType = sig.returnType;
|
|
989
1148
|
// Language-specific return type fallback (e.g. Ruby YARD @return [Type])
|
|
990
1149
|
// Also upgrades uninformative AST types like PHP `array` with PHPDoc `@return User[]`
|
|
991
1150
|
if ((!returnType || returnType === 'array' || returnType === 'iterable') && definitionNode) {
|
|
992
|
-
const tc =
|
|
1151
|
+
const tc = provider.typeConfig;
|
|
993
1152
|
if (tc?.extractReturnType) {
|
|
994
1153
|
const docReturn = tc.extractReturnType(definitionNode);
|
|
995
1154
|
if (docReturn)
|
|
@@ -998,9 +1157,22 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
|
|
|
998
1157
|
}
|
|
999
1158
|
}
|
|
1000
1159
|
else if (nodeLabel === 'Property' && definitionNode) {
|
|
1001
|
-
//
|
|
1002
|
-
|
|
1003
|
-
|
|
1160
|
+
// FieldExtractor is the single source of truth when available
|
|
1161
|
+
if (provider.fieldExtractor && typeEnv) {
|
|
1162
|
+
const classNode = findEnclosingClassNode(definitionNode);
|
|
1163
|
+
if (classNode) {
|
|
1164
|
+
const fieldMap = getFieldInfo(classNode, provider, {
|
|
1165
|
+
typeEnv, symbolTable: NOOP_SYMBOL_TABLE, filePath: file.path, language,
|
|
1166
|
+
});
|
|
1167
|
+
const info = fieldMap?.get(nodeName);
|
|
1168
|
+
if (info) {
|
|
1169
|
+
declaredType = info.type ?? undefined;
|
|
1170
|
+
visibility = info.visibility;
|
|
1171
|
+
isStatic = info.isStatic;
|
|
1172
|
+
isReadonly = info.isReadonly;
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
1175
|
+
}
|
|
1004
1176
|
}
|
|
1005
1177
|
result.nodes.push({
|
|
1006
1178
|
id: nodeId,
|
|
@@ -1011,29 +1183,40 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
|
|
|
1011
1183
|
startLine: definitionNode ? definitionNode.startPosition.row : startLine,
|
|
1012
1184
|
endLine: definitionNode ? definitionNode.endPosition.row : startLine,
|
|
1013
1185
|
language: language,
|
|
1014
|
-
isExported:
|
|
1186
|
+
isExported: cachedExportCheck(provider.exportChecker, nameNode || definitionNode, nodeName),
|
|
1015
1187
|
...(frameworkHint ? {
|
|
1016
1188
|
astFrameworkMultiplier: frameworkHint.entryPointMultiplier,
|
|
1017
1189
|
astFrameworkReason: frameworkHint.reason,
|
|
1018
1190
|
} : {}),
|
|
1019
1191
|
...(description !== undefined ? { description } : {}),
|
|
1020
1192
|
...(parameterCount !== undefined ? { parameterCount } : {}),
|
|
1193
|
+
...(requiredParameterCount !== undefined ? { requiredParameterCount } : {}),
|
|
1194
|
+
...(parameterTypes !== undefined ? { parameterTypes } : {}),
|
|
1021
1195
|
...(returnType !== undefined ? { returnType } : {}),
|
|
1196
|
+
...(declaredType !== undefined ? { declaredType } : {}),
|
|
1197
|
+
...(visibility !== undefined ? { visibility } : {}),
|
|
1198
|
+
...(isStatic !== undefined ? { isStatic } : {}),
|
|
1199
|
+
...(isReadonly !== undefined ? { isReadonly } : {}),
|
|
1022
1200
|
},
|
|
1023
1201
|
});
|
|
1024
1202
|
// Compute enclosing class for Method/Constructor/Property/Function — used for both ownerId and HAS_METHOD
|
|
1025
1203
|
// Function is included because Kotlin/Rust/Python capture class methods as Function nodes
|
|
1026
1204
|
const needsOwner = nodeLabel === 'Method' || nodeLabel === 'Constructor' || nodeLabel === 'Property' || nodeLabel === 'Function';
|
|
1027
|
-
const enclosingClassId = needsOwner ?
|
|
1205
|
+
const enclosingClassId = needsOwner ? cachedFindEnclosingClassId(nameNode || definitionNode, file.path) : null;
|
|
1028
1206
|
result.symbols.push({
|
|
1029
1207
|
filePath: file.path,
|
|
1030
1208
|
name: nodeName,
|
|
1031
1209
|
nodeId,
|
|
1032
1210
|
type: nodeLabel,
|
|
1033
1211
|
...(parameterCount !== undefined ? { parameterCount } : {}),
|
|
1212
|
+
...(requiredParameterCount !== undefined ? { requiredParameterCount } : {}),
|
|
1213
|
+
...(parameterTypes !== undefined ? { parameterTypes } : {}),
|
|
1034
1214
|
...(returnType !== undefined ? { returnType } : {}),
|
|
1035
1215
|
...(declaredType !== undefined ? { declaredType } : {}),
|
|
1036
1216
|
...(enclosingClassId ? { ownerId: enclosingClassId } : {}),
|
|
1217
|
+
...(visibility !== undefined ? { visibility } : {}),
|
|
1218
|
+
...(isStatic !== undefined ? { isStatic } : {}),
|
|
1219
|
+
...(isReadonly !== undefined ? { isReadonly } : {}),
|
|
1037
1220
|
});
|
|
1038
1221
|
const fileId = generateId('File', file.path);
|
|
1039
1222
|
const relId = generateId('DEFINES', `${fileId}->${nodeId}`);
|
|
@@ -1058,11 +1241,13 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
|
|
|
1058
1241
|
});
|
|
1059
1242
|
}
|
|
1060
1243
|
}
|
|
1061
|
-
// Extract
|
|
1062
|
-
if (
|
|
1244
|
+
// Extract framework routes via provider detection (e.g., Laravel routes.php)
|
|
1245
|
+
if (provider.isRouteFile?.(file.path)) {
|
|
1063
1246
|
const extractedRoutes = extractLaravelRoutes(tree, file.path);
|
|
1064
1247
|
result.routes.push(...extractedRoutes);
|
|
1065
1248
|
}
|
|
1249
|
+
// Extract ORM queries (Prisma, Supabase)
|
|
1250
|
+
extractORMQueries(file.path, file.content, result.ormQueries);
|
|
1066
1251
|
}
|
|
1067
1252
|
};
|
|
1068
1253
|
// ============================================================================
|
|
@@ -1071,7 +1256,7 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
|
|
|
1071
1256
|
/** Accumulated result across sub-batches */
|
|
1072
1257
|
let accumulated = {
|
|
1073
1258
|
nodes: [], relationships: [], symbols: [],
|
|
1074
|
-
imports: [], calls: [], assignments: [], heritage: [], routes: [], constructorBindings: [], skippedLanguages: {}, fileCount: 0,
|
|
1259
|
+
imports: [], calls: [], assignments: [], heritage: [], routes: [], fetchCalls: [], decoratorRoutes: [], toolDefs: [], ormQueries: [], constructorBindings: [], typeEnvBindings: [], skippedLanguages: {}, fileCount: 0,
|
|
1075
1260
|
};
|
|
1076
1261
|
let cumulativeProcessed = 0;
|
|
1077
1262
|
const mergeResult = (target, src) => {
|
|
@@ -1083,7 +1268,12 @@ const mergeResult = (target, src) => {
|
|
|
1083
1268
|
target.assignments.push(...src.assignments);
|
|
1084
1269
|
target.heritage.push(...src.heritage);
|
|
1085
1270
|
target.routes.push(...src.routes);
|
|
1271
|
+
target.fetchCalls.push(...src.fetchCalls);
|
|
1272
|
+
target.decoratorRoutes.push(...src.decoratorRoutes);
|
|
1273
|
+
target.toolDefs.push(...src.toolDefs);
|
|
1274
|
+
target.ormQueries.push(...src.ormQueries);
|
|
1086
1275
|
target.constructorBindings.push(...src.constructorBindings);
|
|
1276
|
+
target.typeEnvBindings.push(...src.typeEnvBindings);
|
|
1087
1277
|
for (const [lang, count] of Object.entries(src.skippedLanguages)) {
|
|
1088
1278
|
target.skippedLanguages[lang] = (target.skippedLanguages[lang] || 0) + count;
|
|
1089
1279
|
}
|
|
@@ -1106,7 +1296,7 @@ parentPort.on('message', (msg) => {
|
|
|
1106
1296
|
if (msg && msg.type === 'flush') {
|
|
1107
1297
|
parentPort.postMessage({ type: 'result', data: accumulated });
|
|
1108
1298
|
// Reset for potential reuse
|
|
1109
|
-
accumulated = { nodes: [], relationships: [], symbols: [], imports: [], calls: [], assignments: [], heritage: [], routes: [], constructorBindings: [], skippedLanguages: {}, fileCount: 0 };
|
|
1299
|
+
accumulated = { nodes: [], relationships: [], symbols: [], imports: [], calls: [], assignments: [], heritage: [], routes: [], fetchCalls: [], decoratorRoutes: [], toolDefs: [], ormQueries: [], constructorBindings: [], typeEnvBindings: [], skippedLanguages: {}, fileCount: 0 };
|
|
1110
1300
|
cumulativeProcessed = 0;
|
|
1111
1301
|
return;
|
|
1112
1302
|
}
|