gitnexus 1.6.11-rc.2 → 1.6.11-rc.21
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 +43 -1
- package/dist/_shared/impact-risk.d.ts +37 -0
- package/dist/_shared/impact-risk.d.ts.map +1 -0
- package/dist/_shared/impact-risk.js +92 -0
- package/dist/_shared/impact-risk.js.map +1 -0
- package/dist/_shared/index.d.ts +2 -0
- package/dist/_shared/index.d.ts.map +1 -1
- package/dist/_shared/index.js +2 -0
- package/dist/_shared/index.js.map +1 -1
- package/dist/cli/ai-context.js +4 -4
- package/dist/cli/analyze-config.d.ts +2 -0
- package/dist/cli/analyze-config.js +16 -0
- package/dist/cli/analyze-options.d.ts +4 -0
- package/dist/cli/analyze.d.ts +17 -0
- package/dist/cli/analyze.js +43 -7
- package/dist/cli/eval-server.js +22 -5
- package/dist/cli/group.js +7 -1
- package/dist/cli/help-i18n.js +2 -0
- package/dist/cli/i18n/en.d.ts +12 -2
- package/dist/cli/i18n/en.js +12 -2
- package/dist/cli/i18n/resources.d.ts +22 -2
- package/dist/cli/i18n/zh-CN.d.ts +10 -0
- package/dist/cli/i18n/zh-CN.js +12 -2
- package/dist/cli/index.js +11 -4
- package/dist/cli/status.js +91 -6
- package/dist/cli/watch-queue.d.ts +41 -0
- package/dist/cli/watch-queue.js +184 -0
- package/dist/cli/watch.d.ts +20 -0
- package/dist/cli/watch.js +372 -0
- package/dist/cli/wiki.js +15 -2
- package/dist/config/ignore-service.d.ts +11 -0
- package/dist/config/ignore-service.js +41 -4
- package/dist/config/repo-control-file.d.ts +3 -0
- package/dist/config/repo-control-file.js +115 -0
- package/dist/core/group/config-parser.js +20 -2
- package/dist/core/group/cross-impact.d.ts +2 -1
- package/dist/core/group/cross-impact.js +33 -2
- package/dist/core/group/extractors/fs-utils.d.ts +2 -0
- package/dist/core/group/extractors/fs-utils.js +86 -0
- package/dist/core/group/extractors/graphql-extractor.d.ts +17 -0
- package/dist/core/group/extractors/graphql-extractor.js +652 -0
- package/dist/core/group/extractors/java-workspace-extractor.js +244 -31
- package/dist/core/group/extractors/manifest-extractor.d.ts +1 -1
- package/dist/core/group/extractors/manifest-extractor.js +1 -1
- package/dist/core/group/matching.js +8 -1
- package/dist/core/group/service.js +5 -1
- package/dist/core/group/storage.js +1 -0
- package/dist/core/group/sync.d.ts +3 -1
- package/dist/core/group/sync.js +43 -5
- package/dist/core/group/types.d.ts +19 -5
- package/dist/core/incremental/derived-writeback.d.ts +36 -0
- package/dist/core/incremental/derived-writeback.js +68 -0
- package/dist/core/incremental/subgraph-extract.d.ts +6 -4
- package/dist/core/incremental/subgraph-extract.js +7 -5
- package/dist/core/index-content-drift.d.ts +54 -0
- package/dist/core/index-content-drift.js +127 -0
- package/dist/core/ingestion/filesystem-walker.d.ts +15 -5
- package/dist/core/ingestion/filesystem-walker.js +20 -3
- package/dist/core/ingestion/frameworks/spring/dynamic-lookups.d.ts +22 -0
- package/dist/core/ingestion/frameworks/spring/dynamic-lookups.js +124 -0
- package/dist/core/ingestion/language-provider.d.ts +43 -0
- package/dist/core/ingestion/languages/csharp/razor-view-components.d.ts +62 -0
- package/dist/core/ingestion/languages/csharp/razor-view-components.js +954 -0
- package/dist/core/ingestion/languages/csharp/resolution-config.d.ts +3 -0
- package/dist/core/ingestion/languages/csharp/resolution-config.js +6 -1
- package/dist/core/ingestion/languages/csharp/scope-resolver.js +8 -0
- package/dist/core/ingestion/languages/java/capture-side-channel.d.ts +4 -0
- package/dist/core/ingestion/languages/java/capture-side-channel.js +16 -0
- package/dist/core/ingestion/languages/java/captures.js +14 -1
- package/dist/core/ingestion/languages/java/lombok-synthesizer.d.ts +42 -0
- package/dist/core/ingestion/languages/java/lombok-synthesizer.js +439 -0
- package/dist/core/ingestion/languages/java/scope-resolver.js +2 -0
- package/dist/core/ingestion/languages/java/spring-dynamic-lookup.d.ts +8 -0
- package/dist/core/ingestion/languages/java/spring-dynamic-lookup.js +62 -0
- package/dist/core/ingestion/languages/java.js +2 -0
- package/dist/core/ingestion/languages/jvm/accessor-synthesis.d.ts +99 -0
- package/dist/core/ingestion/languages/jvm/accessor-synthesis.js +173 -0
- package/dist/core/ingestion/languages/jvm/beanspec.d.ts +17 -0
- package/dist/core/ingestion/languages/jvm/beanspec.js +42 -0
- package/dist/core/ingestion/languages/kotlin/capture-side-channel.d.ts +5 -0
- package/dist/core/ingestion/languages/kotlin/capture-side-channel.js +16 -0
- package/dist/core/ingestion/languages/kotlin/captures.js +14 -1
- package/dist/core/ingestion/languages/kotlin/lombok-synthesizer.d.ts +26 -0
- package/dist/core/ingestion/languages/kotlin/lombok-synthesizer.js +417 -0
- package/dist/core/ingestion/languages/kotlin/scope-resolver.js +2 -0
- package/dist/core/ingestion/languages/kotlin/spring-dynamic-lookup.d.ts +8 -0
- package/dist/core/ingestion/languages/kotlin/spring-dynamic-lookup.js +77 -0
- package/dist/core/ingestion/languages/kotlin.js +2 -0
- package/dist/core/ingestion/pipeline-phases/di.js +47 -14
- package/dist/core/ingestion/pipeline-phases/parse-impl.d.ts +3 -1
- package/dist/core/ingestion/pipeline-phases/parse-impl.js +57 -86
- package/dist/core/ingestion/pipeline-phases/parse.d.ts +2 -0
- package/dist/core/ingestion/pipeline-phases/runner.d.ts +4 -1
- package/dist/core/ingestion/pipeline-phases/runner.js +34 -14
- package/dist/core/ingestion/pipeline-phases/scan.js +25 -13
- package/dist/core/ingestion/pipeline.d.ts +6 -0
- package/dist/core/ingestion/pipeline.js +44 -12
- package/dist/core/ingestion/scope-extractor.js +1 -0
- package/dist/core/ingestion/utils/symbol-labels.d.ts +2 -2
- package/dist/core/ingestion/utils/symbol-labels.js +2 -2
- package/dist/core/ingestion/workers/parse-worker.js +28 -2
- package/dist/core/lbug/lbug-adapter.d.ts +59 -0
- package/dist/core/lbug/lbug-adapter.js +154 -1
- package/dist/core/run-analyze.d.ts +17 -0
- package/dist/core/run-analyze.js +231 -23
- package/dist/core/search/fts-indexes.d.ts +19 -1
- package/dist/core/search/fts-indexes.js +28 -1
- package/dist/core/wiki/generator.js +8 -0
- package/dist/core/wiki/grok-client.d.ts +21 -0
- package/dist/core/wiki/grok-client.js +287 -0
- package/dist/core/wiki/llm-client.d.ts +1 -1
- package/dist/core/wiki/llm-client.js +5 -2
- package/dist/core/wiki/local-cli-client.d.ts +11 -0
- package/dist/core/wiki/local-cli-client.js +22 -9
- package/dist/mcp/local/local-backend.d.ts +24 -7
- package/dist/mcp/local/local-backend.js +190 -81
- package/dist/mcp/local/pdg-impact.d.ts +8 -4
- package/dist/mcp/local/pdg-impact.js +7 -2
- package/dist/mcp/repository-policy.d.ts +5 -1
- package/dist/mcp/repository-policy.js +48 -4
- package/dist/mcp/resources.js +2 -1
- package/dist/mcp/server.js +6 -5
- package/dist/mcp/tools.js +31 -19
- package/dist/server/api.js +23 -64
- package/dist/server/grep-params.d.ts +18 -0
- package/dist/server/grep-params.js +83 -0
- package/dist/server/grep-scan.d.ts +23 -0
- package/dist/server/grep-scan.js +107 -0
- package/dist/server/grep-worker.d.ts +1 -0
- package/dist/server/grep-worker.js +12 -0
- package/dist/server/mcp-http.d.ts +8 -0
- package/dist/server/mcp-http.js +16 -1
- package/dist/storage/file-hash.d.ts +5 -0
- package/dist/storage/file-hash.js +16 -6
- package/dist/storage/fs-atomic.d.ts +24 -0
- package/dist/storage/fs-atomic.js +86 -2
- package/dist/storage/git.d.ts +19 -8
- package/dist/storage/git.js +83 -24
- package/dist/storage/gitnexus-managed-paths.d.ts +36 -0
- package/dist/storage/gitnexus-managed-paths.js +46 -0
- package/dist/storage/parse-cache.d.ts +22 -4
- package/dist/storage/parse-cache.js +106 -29
- package/dist/storage/parsedfile-store.d.ts +34 -60
- package/dist/storage/parsedfile-store.js +177 -171
- package/dist/storage/repo-manager.d.ts +11 -1
- package/dist/storage/repo-manager.js +23 -2
- package/dist/storage/repo-meta.d.ts +12 -0
- package/dist/storage/v8-sidecar.d.ts +48 -0
- package/dist/storage/v8-sidecar.js +347 -0
- package/dist/types/pipeline.d.ts +14 -0
- package/package.json +4 -1
- package/scripts/cross-platform-shard.ts +4 -2
- package/scripts/cross-platform-tests.ts +7 -1
- package/skills/gitnexus-cli.md +11 -3
- package/skills/gitnexus-impact-analysis.md +9 -0
- package/web/assets/{agent-Dr4l5EOp.js → agent-CFqT4hjR.js} +108 -104
- package/web/assets/{index-2zdvEdzg.js → index-BMIniRtX.js} +3 -3
- package/web/index.html +1 -1
|
@@ -0,0 +1,417 @@
|
|
|
1
|
+
import { booleanIsPrefixBase, jvmGetterName, jvmSetterName } from '../jvm/beanspec.js';
|
|
2
|
+
import { createExistingMethodIndex, createJvmAccessorSynthesis, hasExistingMethod, jvmTypeSimpleName, rememberExistingMethod, } from '../jvm/accessor-synthesis.js';
|
|
3
|
+
const KOTLIN_TYPE_DECLS = new Set(['class_declaration', 'object_declaration', 'companion_object']);
|
|
4
|
+
function capitalizeAscii(name) {
|
|
5
|
+
const first = name.charAt(0);
|
|
6
|
+
return first >= 'a' && first <= 'z'
|
|
7
|
+
? String.fromCharCode(first.charCodeAt(0) - 32) + name.slice(1)
|
|
8
|
+
: name;
|
|
9
|
+
}
|
|
10
|
+
export function kotlinGetterName(propertyName) {
|
|
11
|
+
return jvmGetterName(propertyName, booleanIsPrefixBase(propertyName, true) !== null, capitalizeAscii);
|
|
12
|
+
}
|
|
13
|
+
export function kotlinSetterName(propertyName) {
|
|
14
|
+
return jvmSetterName(propertyName, true, capitalizeAscii);
|
|
15
|
+
}
|
|
16
|
+
function collectKotlinImports(root) {
|
|
17
|
+
const byLocalName = new Map();
|
|
18
|
+
const shadowedSimpleNames = new Set();
|
|
19
|
+
for (const child of root.children) {
|
|
20
|
+
if (child.type !== 'class_declaration')
|
|
21
|
+
continue;
|
|
22
|
+
const name = jvmTypeSimpleName(child);
|
|
23
|
+
if (name)
|
|
24
|
+
shadowedSimpleNames.add(name);
|
|
25
|
+
}
|
|
26
|
+
const importList = root.children.find((child) => child.type === 'import_list');
|
|
27
|
+
for (const child of importList?.children ?? []) {
|
|
28
|
+
if (child.type !== 'import_header')
|
|
29
|
+
continue;
|
|
30
|
+
const text = child.text
|
|
31
|
+
.replace(/^import\s+/, '')
|
|
32
|
+
.replace(/\/\*[\s\S]*?\*\//g, '')
|
|
33
|
+
.trim();
|
|
34
|
+
const [pathText, aliasText] = text.split(/\s+as\s+/, 2);
|
|
35
|
+
const importPath = pathText?.replace(/\s+/g, '');
|
|
36
|
+
if (!importPath || importPath.endsWith('.*'))
|
|
37
|
+
continue;
|
|
38
|
+
const localName = aliasText?.trim() || importPath.split('.').pop();
|
|
39
|
+
if (localName)
|
|
40
|
+
byLocalName.set(localName, importPath);
|
|
41
|
+
}
|
|
42
|
+
return { byLocalName, shadowedSimpleNames };
|
|
43
|
+
}
|
|
44
|
+
function annotationUserTypeText(annotation) {
|
|
45
|
+
const constructor = annotation.namedChildren.find((c) => c.type === 'constructor_invocation');
|
|
46
|
+
const userType = constructor?.namedChildren.find((c) => c.type === 'user_type') ??
|
|
47
|
+
annotation.namedChildren.find((c) => c.type === 'user_type');
|
|
48
|
+
return userType?.text ?? '';
|
|
49
|
+
}
|
|
50
|
+
function isKotlinJvmAnnotation(annotation, name, imports) {
|
|
51
|
+
const typeText = annotationUserTypeText(annotation);
|
|
52
|
+
const canonical = `kotlin.jvm.${name}`;
|
|
53
|
+
if (typeText.includes('.'))
|
|
54
|
+
return typeText === canonical;
|
|
55
|
+
const imported = imports.byLocalName.get(typeText);
|
|
56
|
+
if (imported !== undefined)
|
|
57
|
+
return imported === canonical;
|
|
58
|
+
if (imports.shadowedSimpleNames.has(typeText))
|
|
59
|
+
return false;
|
|
60
|
+
return typeText === name;
|
|
61
|
+
}
|
|
62
|
+
function kotlinVisibility(modifiers) {
|
|
63
|
+
if (!modifiers)
|
|
64
|
+
return 'public';
|
|
65
|
+
for (const child of modifiers.namedChildren) {
|
|
66
|
+
if (child.type !== 'visibility_modifier')
|
|
67
|
+
continue;
|
|
68
|
+
if (child.text === 'private')
|
|
69
|
+
return 'private';
|
|
70
|
+
if (child.text === 'protected')
|
|
71
|
+
return 'protected';
|
|
72
|
+
if (child.text === 'internal')
|
|
73
|
+
return 'package';
|
|
74
|
+
}
|
|
75
|
+
return 'public';
|
|
76
|
+
}
|
|
77
|
+
function hasJvmField(node, imports) {
|
|
78
|
+
const mods = node.children.find((c) => c.type === 'modifiers');
|
|
79
|
+
return (mods?.namedChildren.some((child) => child.type === 'annotation' && isKotlinJvmAnnotation(child, 'JvmField', imports)) === true);
|
|
80
|
+
}
|
|
81
|
+
function hasConst(node) {
|
|
82
|
+
const mods = node.children.find((c) => c.type === 'modifiers');
|
|
83
|
+
if (mods?.namedChildren.some((child) => child.type === 'property_modifier' && child.text === 'const')) {
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
return node.namedChildren.some((child) => child.type === 'const');
|
|
87
|
+
}
|
|
88
|
+
function isVarBinding(node) {
|
|
89
|
+
const kind = node.children.find((c) => c.type === 'binding_pattern_kind');
|
|
90
|
+
const text = kind?.text;
|
|
91
|
+
if (text === 'var')
|
|
92
|
+
return true;
|
|
93
|
+
if (text === 'val')
|
|
94
|
+
return false;
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
function inferredInitializerType(node) {
|
|
98
|
+
switch (node.type) {
|
|
99
|
+
case 'string_literal':
|
|
100
|
+
case 'line_string_literal':
|
|
101
|
+
case 'multi_line_string_literal':
|
|
102
|
+
return 'String';
|
|
103
|
+
case 'character_literal':
|
|
104
|
+
return 'Char';
|
|
105
|
+
case 'boolean_literal':
|
|
106
|
+
case 'true':
|
|
107
|
+
case 'false':
|
|
108
|
+
return 'Boolean';
|
|
109
|
+
case 'long_literal':
|
|
110
|
+
return 'Long';
|
|
111
|
+
case 'unsigned_literal':
|
|
112
|
+
return /l$/i.test(node.text) ? 'ULong' : 'UInt';
|
|
113
|
+
case 'integer_literal':
|
|
114
|
+
case 'decimal_integer_literal':
|
|
115
|
+
case 'hex_integer_literal':
|
|
116
|
+
case 'octal_integer_literal':
|
|
117
|
+
case 'binary_integer_literal':
|
|
118
|
+
return 'Int';
|
|
119
|
+
case 'real_literal':
|
|
120
|
+
case 'decimal_floating_point_literal':
|
|
121
|
+
return /f$/i.test(node.text) ? 'Float' : 'Double';
|
|
122
|
+
case 'prefix_expression': {
|
|
123
|
+
const operand = node.namedChildren.at(-1);
|
|
124
|
+
return operand ? inferredInitializerType(operand) : undefined;
|
|
125
|
+
}
|
|
126
|
+
case 'call_expression': {
|
|
127
|
+
const callee = node.namedChildren.find((child) => child.type === 'simple_identifier');
|
|
128
|
+
if (!callee)
|
|
129
|
+
return undefined;
|
|
130
|
+
const first = callee.text.charAt(0);
|
|
131
|
+
return first !== '' && first === first.toUpperCase() ? callee.text : undefined;
|
|
132
|
+
}
|
|
133
|
+
default:
|
|
134
|
+
return undefined;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
function propertyTypeText(node) {
|
|
138
|
+
const declarator = node.type === 'class_parameter'
|
|
139
|
+
? node
|
|
140
|
+
: (node.children.find((c) => c.type === 'variable_declaration') ?? node);
|
|
141
|
+
const colon = declarator.children.find((c) => c.type === ':');
|
|
142
|
+
let typeNode = colon?.nextNamedSibling ?? null;
|
|
143
|
+
while (typeNode?.type === 'type_modifiers')
|
|
144
|
+
typeNode = typeNode.nextNamedSibling;
|
|
145
|
+
if (typeNode)
|
|
146
|
+
return typeNode.text;
|
|
147
|
+
const initializer = node.namedChildren.find((child) => child.id !== declarator.id &&
|
|
148
|
+
child.type !== 'binding_pattern_kind' &&
|
|
149
|
+
child.type !== 'modifiers');
|
|
150
|
+
return initializer ? (inferredInitializerType(initializer) ?? 'unknown') : 'unknown';
|
|
151
|
+
}
|
|
152
|
+
function propertyNameNode(node) {
|
|
153
|
+
if (node.type === 'class_parameter') {
|
|
154
|
+
return node.children.find((c) => c.type === 'simple_identifier') ?? null;
|
|
155
|
+
}
|
|
156
|
+
const decl = node.children.find((c) => c.type === 'variable_declaration');
|
|
157
|
+
if (decl) {
|
|
158
|
+
return decl.children.find((c) => c.type === 'simple_identifier') ?? null;
|
|
159
|
+
}
|
|
160
|
+
return node.children.find((c) => c.type === 'simple_identifier') ?? null;
|
|
161
|
+
}
|
|
162
|
+
function accessorMetadata(prop, propertyVisibility, imports) {
|
|
163
|
+
let getter = propertyVisibility;
|
|
164
|
+
let setter = propertyVisibility;
|
|
165
|
+
let skipGetter = false;
|
|
166
|
+
let skipSetter = false;
|
|
167
|
+
const propertyModifiers = prop.children.find((c) => c.type === 'modifiers');
|
|
168
|
+
for (const annotation of propertyModifiers?.namedChildren ?? []) {
|
|
169
|
+
if (annotation.type !== 'annotation' ||
|
|
170
|
+
!isKotlinJvmAnnotation(annotation, 'JvmName', imports)) {
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
const target = annotation.children.find((c) => c.type === 'use_site_target')?.text;
|
|
174
|
+
if (target === 'get:')
|
|
175
|
+
skipGetter = true;
|
|
176
|
+
if (target === 'set:')
|
|
177
|
+
skipSetter = true;
|
|
178
|
+
}
|
|
179
|
+
const apply = (node) => {
|
|
180
|
+
const modifiers = node.children.find((c) => c.type === 'modifiers');
|
|
181
|
+
if (!modifiers)
|
|
182
|
+
return;
|
|
183
|
+
if (node.type === 'getter')
|
|
184
|
+
getter = kotlinVisibility(modifiers);
|
|
185
|
+
if (node.type === 'setter')
|
|
186
|
+
setter = kotlinVisibility(modifiers);
|
|
187
|
+
if (modifiers.namedChildren.some((annotation) => isKotlinJvmAnnotation(annotation, 'JvmName', imports))) {
|
|
188
|
+
if (node.type === 'getter')
|
|
189
|
+
skipGetter = true;
|
|
190
|
+
if (node.type === 'setter')
|
|
191
|
+
skipSetter = true;
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
for (const child of prop.children) {
|
|
195
|
+
if (child.type === 'getter' || child.type === 'setter')
|
|
196
|
+
apply(child);
|
|
197
|
+
}
|
|
198
|
+
let sib = prop.nextNamedSibling;
|
|
199
|
+
while (sib && (sib.type === 'getter' || sib.type === 'setter')) {
|
|
200
|
+
apply(sib);
|
|
201
|
+
sib = sib.nextNamedSibling;
|
|
202
|
+
}
|
|
203
|
+
return {
|
|
204
|
+
getterVisibility: getter,
|
|
205
|
+
setterVisibility: setter,
|
|
206
|
+
skipGetter,
|
|
207
|
+
skipSetter,
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
function hasKotlinAccessorBody(prop, kind) {
|
|
211
|
+
const hasBody = (node) => node.type === kind && node.children.some((child) => child.type === 'function_body');
|
|
212
|
+
if (prop.children.some(hasBody))
|
|
213
|
+
return true;
|
|
214
|
+
let sib = prop.nextNamedSibling;
|
|
215
|
+
while (sib && (sib.type === 'getter' || sib.type === 'setter')) {
|
|
216
|
+
if (hasBody(sib))
|
|
217
|
+
return true;
|
|
218
|
+
sib = sib.nextNamedSibling;
|
|
219
|
+
}
|
|
220
|
+
return false;
|
|
221
|
+
}
|
|
222
|
+
function functionName(node) {
|
|
223
|
+
return node.children.find((c) => c.type === 'simple_identifier')?.text;
|
|
224
|
+
}
|
|
225
|
+
function functionArity(node) {
|
|
226
|
+
const params = node.children.find((c) => c.type === 'function_value_parameters');
|
|
227
|
+
let arity = node.childForFieldName('receiver') !== null ||
|
|
228
|
+
node.namedChildren.some((child) => child.type === 'receiver_type')
|
|
229
|
+
? 1
|
|
230
|
+
: 0;
|
|
231
|
+
const modifiers = node.children.find((child) => child.type === 'modifiers');
|
|
232
|
+
if (modifiers?.namedChildren.some((child) => child.type === 'function_modifier' && child.text === 'suspend')) {
|
|
233
|
+
arity += 1;
|
|
234
|
+
}
|
|
235
|
+
for (const child of params?.namedChildren ?? []) {
|
|
236
|
+
if (child.type === 'parameter' || child.type === 'parameter_with_optional_type')
|
|
237
|
+
arity += 1;
|
|
238
|
+
}
|
|
239
|
+
return arity;
|
|
240
|
+
}
|
|
241
|
+
function collectExistingMethods(...bodies) {
|
|
242
|
+
const index = createExistingMethodIndex('exact');
|
|
243
|
+
for (const body of bodies) {
|
|
244
|
+
if (!body)
|
|
245
|
+
continue;
|
|
246
|
+
for (const child of body.children) {
|
|
247
|
+
if (child.type !== 'function_declaration')
|
|
248
|
+
continue;
|
|
249
|
+
const name = functionName(child);
|
|
250
|
+
if (!name)
|
|
251
|
+
continue;
|
|
252
|
+
rememberExistingMethod(index, name, functionArity(child));
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
return index;
|
|
256
|
+
}
|
|
257
|
+
function toKtProperty(child, imports) {
|
|
258
|
+
const isVar = isVarBinding(child);
|
|
259
|
+
if (isVar === null)
|
|
260
|
+
return null;
|
|
261
|
+
if (hasJvmField(child, imports) || hasConst(child))
|
|
262
|
+
return null;
|
|
263
|
+
const nameNode = propertyNameNode(child);
|
|
264
|
+
if (!nameNode)
|
|
265
|
+
return null;
|
|
266
|
+
const mods = child.children.find((c) => c.type === 'modifiers');
|
|
267
|
+
const visibility = kotlinVisibility(mods);
|
|
268
|
+
const accessor = accessorMetadata(child, visibility, imports);
|
|
269
|
+
return {
|
|
270
|
+
name: nameNode.text,
|
|
271
|
+
type: propertyTypeText(child),
|
|
272
|
+
isVar,
|
|
273
|
+
skipGetter: accessor.skipGetter,
|
|
274
|
+
skipSetter: accessor.skipSetter,
|
|
275
|
+
getterVisibility: accessor.getterVisibility,
|
|
276
|
+
setterVisibility: accessor.setterVisibility,
|
|
277
|
+
startLine: child.startPosition.row + 1,
|
|
278
|
+
endLine: child.endPosition.row + 1,
|
|
279
|
+
propertyNode: child,
|
|
280
|
+
declaratorNode: nameNode,
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
function collectTypedProperties(parent, type, imports) {
|
|
284
|
+
if (!parent)
|
|
285
|
+
return [];
|
|
286
|
+
const out = [];
|
|
287
|
+
for (const child of parent.namedChildren) {
|
|
288
|
+
if (child.type !== type)
|
|
289
|
+
continue;
|
|
290
|
+
const prop = toKtProperty(child, imports);
|
|
291
|
+
if (prop)
|
|
292
|
+
out.push(prop);
|
|
293
|
+
}
|
|
294
|
+
return out;
|
|
295
|
+
}
|
|
296
|
+
function findKtClasses(root, imports) {
|
|
297
|
+
const classes = [];
|
|
298
|
+
const graphOwnerNode = (node) => {
|
|
299
|
+
if (node.type !== 'companion_object')
|
|
300
|
+
return node;
|
|
301
|
+
if (jvmTypeSimpleName(node))
|
|
302
|
+
return node;
|
|
303
|
+
let current = node.parent;
|
|
304
|
+
while (current && !KOTLIN_TYPE_DECLS.has(current.type))
|
|
305
|
+
current = current.parent;
|
|
306
|
+
return current ?? node;
|
|
307
|
+
};
|
|
308
|
+
const walk = (node) => {
|
|
309
|
+
if (KOTLIN_TYPE_DECLS.has(node.type)) {
|
|
310
|
+
const ownerNode = graphOwnerNode(node);
|
|
311
|
+
const name = jvmTypeSimpleName(ownerNode) ?? '';
|
|
312
|
+
const ctor = node.children.find((c) => c.type === 'primary_constructor') ?? null;
|
|
313
|
+
const body = node.children.find((c) => c.type === 'class_body') ?? null;
|
|
314
|
+
if (name) {
|
|
315
|
+
const properties = [
|
|
316
|
+
...collectTypedProperties(ctor, 'class_parameter', imports),
|
|
317
|
+
...collectTypedProperties(body, 'property_declaration', imports),
|
|
318
|
+
];
|
|
319
|
+
if (properties.length > 0) {
|
|
320
|
+
const ownerBody = ownerNode.id === node.id
|
|
321
|
+
? null
|
|
322
|
+
: (ownerNode.children.find((child) => child.type === 'class_body') ?? null);
|
|
323
|
+
classes.push({
|
|
324
|
+
node: ownerNode,
|
|
325
|
+
name,
|
|
326
|
+
isStatic: node.type === 'companion_object',
|
|
327
|
+
isInterface: node.children.some((child) => child.type === 'interface'),
|
|
328
|
+
wasHoisted: ownerNode.id !== node.id,
|
|
329
|
+
properties,
|
|
330
|
+
existingMethods: collectExistingMethods(body, ownerBody),
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
if (body) {
|
|
335
|
+
for (const child of body.namedChildren) {
|
|
336
|
+
if (KOTLIN_TYPE_DECLS.has(child.type))
|
|
337
|
+
walk(child);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
for (const child of node.namedChildren)
|
|
343
|
+
walk(child);
|
|
344
|
+
};
|
|
345
|
+
walk(root);
|
|
346
|
+
return classes;
|
|
347
|
+
}
|
|
348
|
+
function planAccessors(cls) {
|
|
349
|
+
const planned = [];
|
|
350
|
+
for (const prop of cls.properties) {
|
|
351
|
+
const gName = kotlinGetterName(prop.name);
|
|
352
|
+
if (!prop.skipGetter && !hasExistingMethod(cls.existingMethods, gName, 0)) {
|
|
353
|
+
planned.push({
|
|
354
|
+
kind: 'getter',
|
|
355
|
+
name: gName,
|
|
356
|
+
returnType: prop.type,
|
|
357
|
+
parameterTypes: [],
|
|
358
|
+
visibility: prop.getterVisibility,
|
|
359
|
+
isStatic: cls.isStatic,
|
|
360
|
+
isAbstract: cls.isInterface && !hasKotlinAccessorBody(prop.propertyNode, 'getter'),
|
|
361
|
+
startLine: prop.startLine,
|
|
362
|
+
endLine: prop.endLine,
|
|
363
|
+
declaratorNode: prop.declaratorNode,
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
if (prop.isVar && !prop.skipSetter) {
|
|
367
|
+
const sName = kotlinSetterName(prop.name);
|
|
368
|
+
if (!hasExistingMethod(cls.existingMethods, sName, 1)) {
|
|
369
|
+
planned.push({
|
|
370
|
+
kind: 'setter',
|
|
371
|
+
name: sName,
|
|
372
|
+
returnType: 'void',
|
|
373
|
+
parameterTypes: [prop.type],
|
|
374
|
+
visibility: prop.setterVisibility,
|
|
375
|
+
isStatic: cls.isStatic,
|
|
376
|
+
isAbstract: cls.isInterface && !hasKotlinAccessorBody(prop.propertyNode, 'setter'),
|
|
377
|
+
startLine: prop.startLine,
|
|
378
|
+
endLine: prop.endLine,
|
|
379
|
+
declaratorNode: prop.declaratorNode,
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
return planned;
|
|
385
|
+
}
|
|
386
|
+
function planKotlinAccessorOwners(rootNode) {
|
|
387
|
+
const owners = [];
|
|
388
|
+
const imports = collectKotlinImports(rootNode);
|
|
389
|
+
for (const cls of findKtClasses(rootNode, imports)) {
|
|
390
|
+
const accessors = planAccessors(cls);
|
|
391
|
+
const existingIndex = cls.wasHoisted
|
|
392
|
+
? owners.findIndex((owner) => owner.node.id === cls.node.id)
|
|
393
|
+
: -1;
|
|
394
|
+
const existing = existingIndex >= 0 ? owners[existingIndex] : undefined;
|
|
395
|
+
if (existing) {
|
|
396
|
+
owners[existingIndex] = {
|
|
397
|
+
...existing,
|
|
398
|
+
accessors: [...existing.accessors, ...accessors],
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
else {
|
|
402
|
+
owners.push({ node: cls.node, name: cls.name, accessors });
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
return owners;
|
|
406
|
+
}
|
|
407
|
+
const lombokAccessorSynthesis = createJvmAccessorSynthesis({
|
|
408
|
+
language: 'kotlin',
|
|
409
|
+
synthetic: 'kotlin-jvm',
|
|
410
|
+
planOwners: planKotlinAccessorOwners,
|
|
411
|
+
});
|
|
412
|
+
export function synthesizeLombokAccessors(tree, filePath, classOwnersById) {
|
|
413
|
+
return lombokAccessorSynthesis.synthesize(tree, filePath, classOwnersById);
|
|
414
|
+
}
|
|
415
|
+
export function synthesizeLombokAccessorCaptures(rootNode) {
|
|
416
|
+
return lombokAccessorSynthesis.captures(rootNode);
|
|
417
|
+
}
|
|
@@ -15,6 +15,7 @@ import { clearKotlinPackageFacts } from './package-facts.js';
|
|
|
15
15
|
import { attachKotlinSpringDiMetadata } from './spring-di.js';
|
|
16
16
|
import { attachKotlinSpringConditionalMetadata } from './spring-conditionals.js';
|
|
17
17
|
import { attachKotlinSpringNonHttpHandlerMetadata } from './spring-non-http-handlers.js';
|
|
18
|
+
import { attachKotlinSpringDynamicLookup } from './spring-dynamic-lookup.js';
|
|
18
19
|
/**
|
|
19
20
|
* Kotlin scope resolver for RFC #909 Ring 3.
|
|
20
21
|
*
|
|
@@ -121,6 +122,7 @@ export const kotlinScopeResolver = {
|
|
|
121
122
|
attachKotlinSpringConditionalMetadata(graph, parsedFiles, nodeLookup, indexes);
|
|
122
123
|
attachKotlinSpringDiMetadata(graph, parsedFiles, nodeLookup, indexes);
|
|
123
124
|
attachKotlinSpringNonHttpHandlerMetadata(graph, parsedFiles, nodeLookup, indexes);
|
|
125
|
+
attachKotlinSpringDynamicLookup(graph, parsedFiles, nodeLookup, indexes);
|
|
124
126
|
},
|
|
125
127
|
};
|
|
126
128
|
/**
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type SpringDynamicLookupFact } from '../../frameworks/spring/dynamic-lookups.js';
|
|
2
|
+
import { type SyntaxNode } from '../../utils/ast-helpers.js';
|
|
3
|
+
/** Capture real Kotlin calls using `Type::class` or `Type::class.java`. */
|
|
4
|
+
export declare function captureKotlinSpringDynamicLookupFact(node: SyntaxNode, filePath: string): SpringDynamicLookupFact | null;
|
|
5
|
+
/** Standalone extractor for focused tests; production reuses scope-query call nodes. */
|
|
6
|
+
export declare function captureKotlinSpringDynamicLookupFacts(rootNode: SyntaxNode, filePath: string): SpringDynamicLookupFact[];
|
|
7
|
+
/** Attach Kotlin lookup facts for later resolution by the shared DI phase. */
|
|
8
|
+
export declare const attachKotlinSpringDynamicLookup: (graph: import("../../../graph/types.js").KnowledgeGraph, parsedFiles: readonly import('../../../../_shared/index.js').ParsedFile[], nodeLookup: import("../../scope-resolution/graph-bridge/node-lookup.js").GraphNodeLookup, indexes: import("../../model/scope-resolution-indexes.js").ScopeResolutionIndexes) => void;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { makeScopeId } from '../../../../_shared/index.js';
|
|
2
|
+
import { createSpringDynamicLookupMetadataAttacher, springDynamicLookupCardinality, } from '../../frameworks/spring/dynamic-lookups.js';
|
|
3
|
+
import { findAncestorBeforeBoundary, nodeToCapture, } from '../../utils/ast-helpers.js';
|
|
4
|
+
import { getKotlinSpringDynamicLookupFacts } from './capture-side-channel.js';
|
|
5
|
+
// Kotlin emits graph callables for functions and secondary constructors.
|
|
6
|
+
// `init {}` / primary-constructor bodies have no independent callable node, so
|
|
7
|
+
// attributing their lookups to the enclosing Class would violate graph semantics.
|
|
8
|
+
const CALLABLE_NODE_TYPES = new Set(['function_declaration', 'secondary_constructor']);
|
|
9
|
+
const NO_CALLABLE_BOUNDARIES = new Set();
|
|
10
|
+
const KOTLIN_CLASS_LITERAL = /^([A-Za-z_$][A-Za-z0-9_$]*(?:\.[A-Za-z_$][A-Za-z0-9_$]*)*)::class(?:\.java)?$/;
|
|
11
|
+
function navigationParts(node) {
|
|
12
|
+
if (node.type !== 'navigation_expression')
|
|
13
|
+
return null;
|
|
14
|
+
const text = node.text.trim();
|
|
15
|
+
const separator = text.lastIndexOf('.');
|
|
16
|
+
if (separator <= 0 || separator === text.length - 1)
|
|
17
|
+
return null;
|
|
18
|
+
return {
|
|
19
|
+
receiverName: text.slice(0, separator),
|
|
20
|
+
methodName: text.slice(separator + 1),
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
function singleClassLiteralArgument(node) {
|
|
24
|
+
const suffix = node.namedChildren.find((child) => child.type === 'call_suffix');
|
|
25
|
+
const argumentsNode = suffix?.namedChildren.find((child) => child.type === 'value_arguments');
|
|
26
|
+
if (argumentsNode === undefined)
|
|
27
|
+
return null;
|
|
28
|
+
const argumentsWithoutComments = argumentsNode.namedChildren.filter((child) => child.type !== 'line_comment' && child.type !== 'multiline_comment');
|
|
29
|
+
if (argumentsWithoutComments.length !== 1)
|
|
30
|
+
return null;
|
|
31
|
+
const value = argumentsWithoutComments[0];
|
|
32
|
+
if (value?.type !== 'value_argument' || value.namedChildCount !== 1)
|
|
33
|
+
return null;
|
|
34
|
+
return value.namedChild(0)?.text.trim().match(KOTLIN_CLASS_LITERAL)?.[1] ?? null;
|
|
35
|
+
}
|
|
36
|
+
/** Capture real Kotlin calls using `Type::class` or `Type::class.java`. */
|
|
37
|
+
export function captureKotlinSpringDynamicLookupFact(node, filePath) {
|
|
38
|
+
if (node.type !== 'call_expression')
|
|
39
|
+
return null;
|
|
40
|
+
const callee = node.namedChildren.find((child) => child.type === 'navigation_expression');
|
|
41
|
+
if (callee === undefined)
|
|
42
|
+
return null;
|
|
43
|
+
const parts = navigationParts(callee);
|
|
44
|
+
if (parts === null)
|
|
45
|
+
return null;
|
|
46
|
+
if (springDynamicLookupCardinality(parts.receiverName, parts.methodName) === null)
|
|
47
|
+
return null;
|
|
48
|
+
const targetTypeName = singleClassLiteralArgument(node);
|
|
49
|
+
if (targetTypeName === null)
|
|
50
|
+
return null;
|
|
51
|
+
const owner = findAncestorBeforeBoundary(node, CALLABLE_NODE_TYPES, NO_CALLABLE_BOUNDARIES);
|
|
52
|
+
if (owner === null)
|
|
53
|
+
return null;
|
|
54
|
+
const ownerCapture = nodeToCapture('@spring-dynamic-lookup.owner', owner);
|
|
55
|
+
return {
|
|
56
|
+
ownerScopeId: makeScopeId({
|
|
57
|
+
filePath,
|
|
58
|
+
range: ownerCapture.range,
|
|
59
|
+
kind: 'Function',
|
|
60
|
+
}),
|
|
61
|
+
ownerRange: ownerCapture.range,
|
|
62
|
+
receiverName: parts.receiverName,
|
|
63
|
+
methodName: parts.methodName,
|
|
64
|
+
targetTypeName,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
/** Standalone extractor for focused tests; production reuses scope-query call nodes. */
|
|
68
|
+
export function captureKotlinSpringDynamicLookupFacts(rootNode, filePath) {
|
|
69
|
+
return rootNode
|
|
70
|
+
.descendantsOfType('call_expression')
|
|
71
|
+
.map((node) => captureKotlinSpringDynamicLookupFact(node, filePath))
|
|
72
|
+
.filter((fact) => fact !== null);
|
|
73
|
+
}
|
|
74
|
+
/** Attach Kotlin lookup facts for later resolution by the shared DI phase. */
|
|
75
|
+
export const attachKotlinSpringDynamicLookup = createSpringDynamicLookupMetadataAttacher({
|
|
76
|
+
getFacts: getKotlinSpringDynamicLookupFacts,
|
|
77
|
+
});
|
|
@@ -28,6 +28,7 @@ import { kotlinMethodConfig } from '../method-extractors/configs/jvm.js';
|
|
|
28
28
|
import { createVariableExtractor } from '../variable-extractors/generic.js';
|
|
29
29
|
import { kotlinVariableConfig } from '../variable-extractors/configs/jvm.js';
|
|
30
30
|
import { collectKotlinCaptureSideChannel, emitKotlinScopeCaptures, interpretKotlinImport, interpretKotlinTypeBinding, kotlinArityCompatibility, kotlinBindingScopeFor, kotlinImportOwningScope, kotlinMergeBindings, kotlinReceiverBinding, } from './kotlin/index.js';
|
|
31
|
+
import { synthesizeLombokAccessors } from './kotlin/lombok-synthesizer.js';
|
|
31
32
|
/** Check if a Kotlin function_declaration capture is inside a class_body (i.e., a method).
|
|
32
33
|
* Kotlin grammar uses function_declaration for both top-level functions and class methods.
|
|
33
34
|
* Returns true when the captured definition node has a class_body ancestor. */
|
|
@@ -183,4 +184,5 @@ export const kotlinProvider = defineLanguage({
|
|
|
183
184
|
mergeBindings: (_scope, bindings) => kotlinMergeBindings(bindings),
|
|
184
185
|
receiverBinding: kotlinReceiverBinding,
|
|
185
186
|
arityCompatibility: kotlinArityCompatibility,
|
|
187
|
+
synthesizeStructureMembers: synthesizeLombokAccessors,
|
|
186
188
|
});
|
|
@@ -48,6 +48,9 @@ function providerCandidates(ids, providers) {
|
|
|
48
48
|
// candidates when none are known instead of dropping the injection entirely.
|
|
49
49
|
return recognized.length > 0 ? recognized : all;
|
|
50
50
|
}
|
|
51
|
+
function isConcreteTypeNode(node) {
|
|
52
|
+
return node?.label === 'Class' || node?.label === 'Record' || node?.label === 'Enum';
|
|
53
|
+
}
|
|
51
54
|
export const diPhase = {
|
|
52
55
|
name: 'di',
|
|
53
56
|
deps: ['mro'],
|
|
@@ -103,21 +106,25 @@ export const diPhase = {
|
|
|
103
106
|
ambiguousInjections: 0,
|
|
104
107
|
};
|
|
105
108
|
}
|
|
106
|
-
const
|
|
109
|
+
const directSubtypes = new Map();
|
|
107
110
|
const directSupertypes = new Map();
|
|
108
111
|
for (const rel of ctx.graph.iterRelationshipsByType('IMPLEMENTS')) {
|
|
109
|
-
const
|
|
110
|
-
|
|
111
|
-
|
|
112
|
+
const subtypes = directSubtypes.get(rel.targetId) ?? new Set();
|
|
113
|
+
subtypes.add(rel.sourceId);
|
|
114
|
+
directSubtypes.set(rel.targetId, subtypes);
|
|
112
115
|
const supertypes = directSupertypes.get(rel.sourceId) ?? new Set();
|
|
113
116
|
supertypes.add(rel.targetId);
|
|
114
117
|
directSupertypes.set(rel.sourceId, supertypes);
|
|
115
118
|
}
|
|
116
119
|
for (const rel of ctx.graph.iterRelationshipsByType('EXTENDS')) {
|
|
120
|
+
const subtypes = directSubtypes.get(rel.targetId) ?? new Set();
|
|
121
|
+
subtypes.add(rel.sourceId);
|
|
122
|
+
directSubtypes.set(rel.targetId, subtypes);
|
|
117
123
|
const supertypes = directSupertypes.get(rel.sourceId) ?? new Set();
|
|
118
124
|
supertypes.add(rel.targetId);
|
|
119
125
|
directSupertypes.set(rel.sourceId, supertypes);
|
|
120
126
|
}
|
|
127
|
+
const orderedDirectSubtypes = new Map([...directSubtypes].map(([typeId, subtypes]) => [typeId, [...subtypes].sort().reverse()]));
|
|
121
128
|
const memberToClass = new Map();
|
|
122
129
|
for (const relationType of ['HAS_PROPERTY', 'HAS_METHOD']) {
|
|
123
130
|
for (const rel of ctx.graph.iterRelationshipsByType(relationType)) {
|
|
@@ -128,18 +135,45 @@ export const diPhase = {
|
|
|
128
135
|
const interfacesByLanguage = new Map();
|
|
129
136
|
const classesByLanguage = new Map();
|
|
130
137
|
ctx.graph.forEachNode((node) => {
|
|
131
|
-
|
|
138
|
+
const concreteType = isConcreteTypeNode(node);
|
|
139
|
+
if (!concreteType && node.label !== 'Interface')
|
|
132
140
|
return;
|
|
133
141
|
const language = node.properties.language;
|
|
134
142
|
if (typeof language !== 'string' || !candidateLanguages.has(language))
|
|
135
143
|
return;
|
|
136
|
-
const indexes =
|
|
144
|
+
const indexes = concreteType ? classesByLanguage : interfacesByLanguage;
|
|
137
145
|
const index = indexes.get(language) ?? emptyNameIndex();
|
|
138
146
|
addIndexedName(index, node);
|
|
139
147
|
indexes.set(language, index);
|
|
140
|
-
if (
|
|
148
|
+
if (concreteType)
|
|
141
149
|
providerNodes.set(node.id, node);
|
|
142
150
|
});
|
|
151
|
+
const concreteSubtypesByRoot = new Map();
|
|
152
|
+
const concreteSubtypes = (rootTypeId, language) => {
|
|
153
|
+
const cacheKey = `${language}\0${rootTypeId}`;
|
|
154
|
+
const cached = concreteSubtypesByRoot.get(cacheKey);
|
|
155
|
+
if (cached !== undefined)
|
|
156
|
+
return cached;
|
|
157
|
+
const concrete = new Set();
|
|
158
|
+
const queue = [rootTypeId];
|
|
159
|
+
const visited = new Set();
|
|
160
|
+
while (queue.length > 0) {
|
|
161
|
+
const typeId = queue.pop();
|
|
162
|
+
if (typeId === undefined || visited.has(typeId))
|
|
163
|
+
continue;
|
|
164
|
+
visited.add(typeId);
|
|
165
|
+
const typeNode = ctx.graph.getNode(typeId);
|
|
166
|
+
if (typeNode !== undefined &&
|
|
167
|
+
isConcreteTypeNode(typeNode) &&
|
|
168
|
+
typeNode.properties.language === language) {
|
|
169
|
+
concrete.add(typeId);
|
|
170
|
+
}
|
|
171
|
+
const children = orderedDirectSubtypes.get(typeId) ?? [];
|
|
172
|
+
queue.push(...children);
|
|
173
|
+
}
|
|
174
|
+
concreteSubtypesByRoot.set(cacheKey, concrete);
|
|
175
|
+
return concrete;
|
|
176
|
+
};
|
|
143
177
|
// A declaration returning a concrete class is assignable to every class or
|
|
144
178
|
// interface that type extends/implements. Expand once per language+type and
|
|
145
179
|
// register the declaration under those ancestor names. This keeps named
|
|
@@ -227,13 +261,12 @@ export const diPhase = {
|
|
|
227
261
|
ambiguousTypeNames.add(candidate.targetTypeName);
|
|
228
262
|
continue;
|
|
229
263
|
}
|
|
230
|
-
const
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
}
|
|
264
|
+
const rootTypeId = typeof classEntry === 'string'
|
|
265
|
+
? classEntry
|
|
266
|
+
: typeof interfaceEntry === 'string'
|
|
267
|
+
? interfaceEntry
|
|
268
|
+
: undefined;
|
|
269
|
+
const structural = new Set(rootTypeId === undefined ? [] : concreteSubtypes(rootTypeId, candidate.language));
|
|
237
270
|
for (const id of providedTypes.get(candidate.language)?.get(candidate.targetTypeName) ?? []) {
|
|
238
271
|
structural.add(id);
|
|
239
272
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Parse implementation — chunked parse + resolve loop.
|
|
3
3
|
*
|
|
4
4
|
* This is the core parsing engine of the ingestion pipeline. It reads
|
|
5
|
-
* source files in
|
|
5
|
+
* source files in stable hash-bucket packs (~2MB each by default), parses via the worker
|
|
6
6
|
* pool (the sole parse path — there is no sequential fallback), and emits
|
|
7
7
|
* route CALLS edges. Import,
|
|
8
8
|
* call, and inheritance resolution are owned by the scope-resolution
|
|
@@ -94,6 +94,8 @@ export declare function runChunkedParseAndResolve(graph: KnowledgeGraph, scanned
|
|
|
94
94
|
* files. There is no sequential parser — the pool is the sole parse path
|
|
95
95
|
* whenever a chunk misses the cache. */
|
|
96
96
|
usedWorkerPool: boolean;
|
|
97
|
+
/** Files dispatched to parser workers after parse-cache lookup. */
|
|
98
|
+
reparsedFileCount: number;
|
|
97
99
|
/** Worker-produced ParsedFile artifacts aggregated across chunks.
|
|
98
100
|
* Threaded into scope-resolution as a re-extract cache so the warm-
|
|
99
101
|
* cache analyze run can skip the dominant `extractParsedFile` cost
|