@adhisang/minecraft-modding-mcp 1.2.1 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +29 -0
- package/README.md +86 -46
- package/dist/index.js +150 -174
- package/dist/mapping-pipeline-service.d.ts +1 -1
- package/dist/mapping-pipeline-service.js +5 -5
- package/dist/mapping-service.d.ts +3 -4
- package/dist/mapping-service.js +40 -46
- package/dist/mcp-helpers.d.ts +10 -2
- package/dist/mcp-helpers.js +59 -5
- package/dist/minecraft-explorer-service.d.ts +1 -1
- package/dist/minecraft-explorer-service.js +1 -1
- package/dist/mixin-validator.js +5 -5
- package/dist/mojang-tiny-mapping-service.js +26 -26
- package/dist/resources.js +7 -7
- package/dist/search-hit-accumulator.d.ts +0 -3
- package/dist/search-hit-accumulator.js +4 -4
- package/dist/source-resolver.d.ts +1 -0
- package/dist/source-resolver.js +94 -2
- package/dist/source-service.d.ts +34 -43
- package/dist/source-service.js +597 -670
- package/dist/storage/files-repo.d.ts +9 -0
- package/dist/storage/files-repo.js +42 -0
- package/dist/storage/migrations.d.ts +1 -1
- package/dist/storage/migrations.js +6 -2
- package/dist/storage/schema.d.ts +1 -0
- package/dist/storage/schema.js +7 -0
- package/dist/tool-input.d.ts +6 -0
- package/dist/tool-input.js +64 -0
- package/dist/types.d.ts +1 -1
- package/package.json +4 -1
package/dist/source-service.js
CHANGED
|
@@ -80,6 +80,23 @@ function hasExactVersionToken(path, version) {
|
|
|
80
80
|
const pattern = new RegExp(`(^|[^0-9a-z])${escapeRegexLiteral(normalizedVersion)}([^0-9a-z]|$)`, "i");
|
|
81
81
|
return pattern.test(normalizedPath);
|
|
82
82
|
}
|
|
83
|
+
function looksLikeDeobfuscatedClassName(value) {
|
|
84
|
+
const trimmed = value.trim();
|
|
85
|
+
if (!trimmed) {
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
if (trimmed.startsWith("net.minecraft.") || trimmed.startsWith("com.mojang.")) {
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
91
|
+
const simpleName = trimmed.split(/[.$]/).at(-1) ?? trimmed;
|
|
92
|
+
return /^[A-Z][A-Za-z0-9_$]{2,}$/.test(simpleName);
|
|
93
|
+
}
|
|
94
|
+
function obfuscatedNamespaceHint(className) {
|
|
95
|
+
return `Artifact is indexed in obfuscated runtime names. Deobfuscated names like "${className}" usually require mapping="mojang" or a find-mapping lookup to obfuscated names.`;
|
|
96
|
+
}
|
|
97
|
+
function hasPartialNetMinecraftCoverage(qualityFlags) {
|
|
98
|
+
return qualityFlags.includes("partial-source-no-net-minecraft");
|
|
99
|
+
}
|
|
83
100
|
function normalizeOptionalProjectPath(projectPath) {
|
|
84
101
|
if (!projectPath) {
|
|
85
102
|
return undefined;
|
|
@@ -168,9 +185,9 @@ const COMMON_SOURCE_ROOTS = [
|
|
|
168
185
|
];
|
|
169
186
|
function normalizeMapping(mapping) {
|
|
170
187
|
if (mapping == null) {
|
|
171
|
-
return "
|
|
188
|
+
return "obfuscated";
|
|
172
189
|
}
|
|
173
|
-
if (mapping === "
|
|
190
|
+
if (mapping === "obfuscated" ||
|
|
174
191
|
mapping === "mojang" ||
|
|
175
192
|
mapping === "intermediary" ||
|
|
176
193
|
mapping === "yarn") {
|
|
@@ -181,8 +198,8 @@ function normalizeMapping(mapping) {
|
|
|
181
198
|
message: `Unsupported mapping "${mapping}".`,
|
|
182
199
|
details: {
|
|
183
200
|
mapping,
|
|
184
|
-
nextAction: "Try mapping=
|
|
185
|
-
suggestedCall: { tool: "resolve-artifact", params: { mapping: "
|
|
201
|
+
nextAction: "Try mapping=obfuscated which is always available.",
|
|
202
|
+
suggestedCall: { tool: "resolve-artifact", params: { mapping: "obfuscated" } }
|
|
186
203
|
}
|
|
187
204
|
});
|
|
188
205
|
}
|
|
@@ -194,7 +211,7 @@ function normalizeAccessWidenerNamespace(namespace) {
|
|
|
194
211
|
if (normalized === "named") {
|
|
195
212
|
return "yarn";
|
|
196
213
|
}
|
|
197
|
-
if (normalized === "
|
|
214
|
+
if (normalized === "obfuscated" ||
|
|
198
215
|
normalized === "mojang" ||
|
|
199
216
|
normalized === "intermediary" ||
|
|
200
217
|
normalized === "yarn") {
|
|
@@ -333,7 +350,7 @@ function canUseIndexedSearchPath(indexedSearchEnabled, intent, match, _scope) {
|
|
|
333
350
|
if (match === "regex") {
|
|
334
351
|
return false;
|
|
335
352
|
}
|
|
336
|
-
//
|
|
353
|
+
// packagePrefix and fileGlob are applied as post-filters on indexed candidates.
|
|
337
354
|
return true;
|
|
338
355
|
}
|
|
339
356
|
function buildGlobRegex(pattern) {
|
|
@@ -412,14 +429,6 @@ function checkPackagePrefix(filePath, packagePrefix) {
|
|
|
412
429
|
const normalizedPrefix = packagePrefix.replace(/\.+/g, "/").replace(/\/+$/, "");
|
|
413
430
|
return normalizePathStyle(filePath).startsWith(`${normalizedPrefix}/`);
|
|
414
431
|
}
|
|
415
|
-
function buildSnippetWindow(lines) {
|
|
416
|
-
const totalLines = clampLimit(lines, 8, 80);
|
|
417
|
-
const before = Math.floor((totalLines - 1) / 2);
|
|
418
|
-
return {
|
|
419
|
-
before,
|
|
420
|
-
after: Math.max(0, totalLines - 1 - before)
|
|
421
|
-
};
|
|
422
|
-
}
|
|
423
432
|
function buildSearchCursorContext(input) {
|
|
424
433
|
return JSON.stringify({
|
|
425
434
|
artifactId: input.artifactId,
|
|
@@ -427,7 +436,6 @@ function buildSearchCursorContext(input) {
|
|
|
427
436
|
intent: input.intent,
|
|
428
437
|
match: input.match,
|
|
429
438
|
queryMode: input.queryMode,
|
|
430
|
-
includeDefinition: input.includeDefinition,
|
|
431
439
|
packagePrefix: input.scope?.packagePrefix ?? "",
|
|
432
440
|
fileGlob: input.scope?.fileGlob ?? "",
|
|
433
441
|
symbolKind: input.scope?.symbolKind ?? ""
|
|
@@ -521,41 +529,6 @@ function matchRegexIndex(target, regex) {
|
|
|
521
529
|
const result = regex.exec(target);
|
|
522
530
|
return result?.index ?? -1;
|
|
523
531
|
}
|
|
524
|
-
function indexToLine(content, index) {
|
|
525
|
-
if (index <= 0) {
|
|
526
|
-
return 1;
|
|
527
|
-
}
|
|
528
|
-
return content.slice(0, index).split(/\r?\n/).length;
|
|
529
|
-
}
|
|
530
|
-
function lineToSymbol(symbol) {
|
|
531
|
-
if (!isSymbolKind(symbol.symbolKind)) {
|
|
532
|
-
return undefined;
|
|
533
|
-
}
|
|
534
|
-
return {
|
|
535
|
-
symbolKind: symbol.symbolKind,
|
|
536
|
-
symbolName: symbol.symbolName,
|
|
537
|
-
qualifiedName: symbol.qualifiedName,
|
|
538
|
-
line: symbol.line
|
|
539
|
-
};
|
|
540
|
-
}
|
|
541
|
-
function toContextSnippet(content, centerLineInput, beforeLines, afterLines, withLineNumbers) {
|
|
542
|
-
const lines = content.split(/\r?\n/);
|
|
543
|
-
const centerLine = Math.min(Math.max(1, centerLineInput), Math.max(lines.length, 1));
|
|
544
|
-
const requestedStart = Math.max(1, centerLine - beforeLines);
|
|
545
|
-
const requestedEnd = centerLine + afterLines;
|
|
546
|
-
const startLine = Math.min(requestedStart, Math.max(lines.length, 1));
|
|
547
|
-
const endLine = Math.min(requestedEnd, Math.max(lines.length, 1));
|
|
548
|
-
const snippetLines = lines.slice(startLine - 1, endLine);
|
|
549
|
-
const snippet = withLineNumbers
|
|
550
|
-
? snippetLines.map((line, index) => `${startLine + index}: ${line}`).join("\n")
|
|
551
|
-
: snippetLines.join("\n");
|
|
552
|
-
return {
|
|
553
|
-
startLine,
|
|
554
|
-
endLine,
|
|
555
|
-
snippet,
|
|
556
|
-
truncated: requestedStart !== startLine || requestedEnd !== endLine
|
|
557
|
-
};
|
|
558
|
-
}
|
|
559
532
|
function chunkArray(items, chunkSize) {
|
|
560
533
|
const size = Math.max(1, Math.trunc(chunkSize));
|
|
561
534
|
if (items.length === 0) {
|
|
@@ -663,11 +636,12 @@ export class SourceService {
|
|
|
663
636
|
const selected = candidates.find((candidate) => candidate.hasMinecraftNamespace) ?? candidates[0];
|
|
664
637
|
const candidateArtifacts = candidates
|
|
665
638
|
.slice(0, 20)
|
|
666
|
-
.map((candidate) => `${candidate.jarPath}#java=${candidate.javaEntryCount}`);
|
|
639
|
+
.map((candidate) => `${candidate.jarPath}#java=${candidate.javaEntryCount}#net_minecraft=${candidate.hasMinecraftNamespace ? 1 : 0}`);
|
|
667
640
|
return {
|
|
668
641
|
searchedPaths,
|
|
669
642
|
candidateArtifacts,
|
|
670
|
-
selectedSourceJarPath: selected?.jarPath
|
|
643
|
+
selectedSourceJarPath: selected?.jarPath,
|
|
644
|
+
selectedHasMinecraftNamespace: selected?.hasMinecraftNamespace
|
|
671
645
|
};
|
|
672
646
|
}
|
|
673
647
|
buildVersionSourceRecoveryCommand(projectPath) {
|
|
@@ -734,13 +708,13 @@ export class SourceService {
|
|
|
734
708
|
// coordinate validity is validated by resolver, keep version undefined on parse failure.
|
|
735
709
|
}
|
|
736
710
|
}
|
|
737
|
-
// Unobfuscated versions (MC 26.1+) ship with
|
|
711
|
+
// Unobfuscated versions (MC 26.1+) ship with deobfuscated runtime names; intermediary/yarn are not applicable.
|
|
738
712
|
let effectiveMapping = mapping;
|
|
739
713
|
if ((mapping === "intermediary" || mapping === "yarn") &&
|
|
740
714
|
resolvedVersion &&
|
|
741
715
|
isUnobfuscatedVersion(resolvedVersion)) {
|
|
742
|
-
warnings.push(`Version ${resolvedVersion} is unobfuscated; ${mapping} mappings are not applicable. Using
|
|
743
|
-
effectiveMapping = "
|
|
716
|
+
warnings.push(`Version ${resolvedVersion} is unobfuscated; ${mapping} mappings are not applicable. Using the obfuscated namespace label for the deobfuscated runtime names.`);
|
|
717
|
+
effectiveMapping = "obfuscated";
|
|
744
718
|
}
|
|
745
719
|
if (kind === "version" && resolvedVersion && effectiveMapping === "mojang" && scope !== "vanilla") {
|
|
746
720
|
versionSourceDiscovery = await this.discoverVersionSourceJar({
|
|
@@ -797,18 +771,18 @@ export class SourceService {
|
|
|
797
771
|
else if (isVanillaMojang) {
|
|
798
772
|
suggestedCall = {
|
|
799
773
|
tool: "resolve-artifact",
|
|
800
|
-
params: { targetKind: kind, targetValue: value, mapping: "
|
|
774
|
+
params: { targetKind: kind, targetValue: value, mapping: "obfuscated", scope: "vanilla" }
|
|
801
775
|
};
|
|
802
776
|
nextAction =
|
|
803
777
|
"scope=vanilla blocks Loom cache discovery needed for mojang mapping. " +
|
|
804
|
-
"Without a projectPath, use mapping=
|
|
778
|
+
"Without a projectPath, use mapping=obfuscated to read vanilla runtime names directly.";
|
|
805
779
|
}
|
|
806
780
|
else {
|
|
807
781
|
suggestedCall = {
|
|
808
782
|
tool: "resolve-artifact",
|
|
809
|
-
params: { targetKind: kind, targetValue: value, mapping: "
|
|
783
|
+
params: { targetKind: kind, targetValue: value, mapping: "obfuscated", ...(scope ? { scope } : {}) }
|
|
810
784
|
};
|
|
811
|
-
nextAction = "Retry with mapping=
|
|
785
|
+
nextAction = "Retry with mapping=obfuscated to use the runtime obfuscated namespace.";
|
|
812
786
|
}
|
|
813
787
|
throw createError({
|
|
814
788
|
code: ERROR_CODES.MAPPING_NOT_APPLIED,
|
|
@@ -841,7 +815,7 @@ export class SourceService {
|
|
|
841
815
|
}
|
|
842
816
|
const mappingAvailability = await this.mappingService.ensureMappingAvailable({
|
|
843
817
|
version: resolved.version,
|
|
844
|
-
sourceMapping: "
|
|
818
|
+
sourceMapping: "obfuscated",
|
|
845
819
|
targetMapping: effectiveMapping,
|
|
846
820
|
sourcePriority: input.sourcePriority
|
|
847
821
|
});
|
|
@@ -864,6 +838,10 @@ export class SourceService {
|
|
|
864
838
|
}
|
|
865
839
|
if (versionSourceDiscovery?.selectedSourceJarPath) {
|
|
866
840
|
resolved.qualityFlags.push("source-jar-validated");
|
|
841
|
+
if (versionSourceDiscovery.selectedHasMinecraftNamespace === false) {
|
|
842
|
+
resolved.qualityFlags.push("partial-source-no-net-minecraft");
|
|
843
|
+
warnings.push(`Source coverage does not include net.minecraft for ${versionSourceDiscovery.selectedSourceJarPath}; class lookups may fall back to the binary artifact.`);
|
|
844
|
+
}
|
|
867
845
|
if (kind === "version" && !hasExactVersionToken(versionSourceDiscovery.selectedSourceJarPath, value)) {
|
|
868
846
|
if (input.strictVersion) {
|
|
869
847
|
throw createError({
|
|
@@ -942,8 +920,7 @@ export class SourceService {
|
|
|
942
920
|
if (!query) {
|
|
943
921
|
return {
|
|
944
922
|
hits: [],
|
|
945
|
-
|
|
946
|
-
mappingApplied: artifact.mappingApplied ?? "official"
|
|
923
|
+
mappingApplied: artifact.mappingApplied ?? "obfuscated"
|
|
947
924
|
};
|
|
948
925
|
}
|
|
949
926
|
const intent = normalizeIntent(input.intent);
|
|
@@ -961,12 +938,15 @@ export class SourceService {
|
|
|
961
938
|
const searchLimitCap = match === "regex"
|
|
962
939
|
? Math.max(1, Math.min(this.config.maxSearchHits, MAX_REGEX_RESULT_LIMIT))
|
|
963
940
|
: this.config.maxSearchHits;
|
|
941
|
+
const scope = input.scope;
|
|
942
|
+
if (scope?.symbolKind && intent !== "symbol") {
|
|
943
|
+
throw createError({
|
|
944
|
+
code: ERROR_CODES.INVALID_INPUT,
|
|
945
|
+
message: 'symbolKind filter is only supported when intent="symbol".'
|
|
946
|
+
});
|
|
947
|
+
}
|
|
964
948
|
const limit = clampLimit(input.limit, 20, searchLimitCap);
|
|
965
|
-
const includeDefinition = input.include?.includeDefinition ?? false;
|
|
966
|
-
const includeOneHop = input.include?.includeOneHop ?? false;
|
|
967
|
-
const snippetWindow = buildSnippetWindow(input.include?.snippetLines);
|
|
968
949
|
const regexPattern = match === "regex" ? compileRegex(query) : undefined;
|
|
969
|
-
const scope = input.scope;
|
|
970
950
|
const queryMode = input.queryMode ?? "auto";
|
|
971
951
|
const cursorContext = buildSearchCursorContext({
|
|
972
952
|
artifactId: artifact.artifactId,
|
|
@@ -974,8 +954,7 @@ export class SourceService {
|
|
|
974
954
|
intent,
|
|
975
955
|
match,
|
|
976
956
|
queryMode,
|
|
977
|
-
scope
|
|
978
|
-
includeDefinition
|
|
957
|
+
scope
|
|
979
958
|
});
|
|
980
959
|
const decodedCursor = decodeSearchCursor(input.cursor);
|
|
981
960
|
const cursor = decodedCursor?.contextKey === cursorContext ? decodedCursor : undefined;
|
|
@@ -991,52 +970,35 @@ export class SourceService {
|
|
|
991
970
|
const hasSeparators = /[._$]/.test(query);
|
|
992
971
|
const tokenOnlyTextIntent = intent === "text" && queryMode === "token";
|
|
993
972
|
if (intent === "symbol") {
|
|
994
|
-
this.searchSymbolIntent(artifact.artifactId, query, match, scope,
|
|
995
|
-
// WS4: Use repo-level COUNT for symbol totalApprox when not regex
|
|
996
|
-
if (match !== "regex") {
|
|
997
|
-
const approxCount = this.symbolsRepo.countScopedSymbols({
|
|
998
|
-
artifactId: artifact.artifactId,
|
|
999
|
-
query,
|
|
1000
|
-
match,
|
|
1001
|
-
symbolKind: scope?.symbolKind,
|
|
1002
|
-
packagePrefix: scope?.packagePrefix
|
|
1003
|
-
});
|
|
1004
|
-
accumulator.setTotalApproxOverride(approxCount);
|
|
1005
|
-
}
|
|
973
|
+
this.searchSymbolIntent(artifact.artifactId, query, match, scope, regexPattern, recordHit);
|
|
1006
974
|
}
|
|
1007
975
|
else if (queryMode === "literal" && intent === "text") {
|
|
1008
976
|
// F-03: queryMode=literal forces substring scan for text intent
|
|
1009
977
|
this.metrics.recordSearchFallback();
|
|
1010
|
-
this.searchTextIntent(artifact.artifactId, query, match, scope,
|
|
978
|
+
this.searchTextIntent(artifact.artifactId, query, match, scope, regexPattern, recordHit);
|
|
1011
979
|
}
|
|
1012
980
|
else if (!indexedSearchEnabled) {
|
|
1013
981
|
this.metrics.recordIndexedDisabled();
|
|
1014
982
|
if (!tokenOnlyTextIntent) {
|
|
1015
983
|
this.metrics.recordSearchFallback();
|
|
1016
984
|
if (intent === "path") {
|
|
1017
|
-
this.searchPathIntent(artifact.artifactId, query, match, scope,
|
|
985
|
+
this.searchPathIntent(artifact.artifactId, query, match, scope, regexPattern, recordHit);
|
|
1018
986
|
}
|
|
1019
987
|
else {
|
|
1020
|
-
this.searchTextIntent(artifact.artifactId, query, match, scope,
|
|
988
|
+
this.searchTextIntent(artifact.artifactId, query, match, scope, regexPattern, recordHit);
|
|
1021
989
|
}
|
|
1022
990
|
}
|
|
1023
991
|
}
|
|
1024
992
|
else if (canUseIndexedSearchPath(indexedSearchEnabled, intent, match, scope)) {
|
|
1025
993
|
try {
|
|
1026
994
|
if (intent === "path") {
|
|
1027
|
-
this.searchPathIntentIndexed(artifact.artifactId, query, match, scope,
|
|
1028
|
-
// WS4: Use repo-level COUNT for totalApprox instead of accumulator count
|
|
1029
|
-
const approxCount = this.filesRepo.countPathCandidates(artifact.artifactId, query);
|
|
1030
|
-
accumulator.setTotalApproxOverride(approxCount);
|
|
995
|
+
this.searchPathIntentIndexed(artifact.artifactId, query, match, scope, recordHit);
|
|
1031
996
|
}
|
|
1032
997
|
else {
|
|
1033
|
-
this.searchTextIntentIndexed(artifact.artifactId, query, match, scope,
|
|
1034
|
-
// WS4: Use repo-level COUNT for totalApprox instead of accumulator count
|
|
1035
|
-
const approxCount = this.filesRepo.countTextCandidates(artifact.artifactId, query);
|
|
1036
|
-
accumulator.setTotalApproxOverride(approxCount);
|
|
998
|
+
this.searchTextIntentIndexed(artifact.artifactId, query, match, scope, recordHit);
|
|
1037
999
|
// F-03: queryMode=auto fallback — when indexed returns 0 hits and query has separators, retry with literal scan
|
|
1038
1000
|
if (queryMode === "auto" && hasSeparators && accumulator.currentCount() === 0) {
|
|
1039
|
-
this.searchTextIntent(artifact.artifactId, query, match, scope,
|
|
1001
|
+
this.searchTextIntent(artifact.artifactId, query, match, scope, regexPattern, recordHit);
|
|
1040
1002
|
}
|
|
1041
1003
|
}
|
|
1042
1004
|
this.metrics.recordSearchIndexedHit();
|
|
@@ -1052,10 +1014,10 @@ export class SourceService {
|
|
|
1052
1014
|
// F-03: queryMode=token suppresses error-path fallback to brute-force scan
|
|
1053
1015
|
if (!tokenOnlyTextIntent) {
|
|
1054
1016
|
if (intent === "path") {
|
|
1055
|
-
this.searchPathIntent(artifact.artifactId, query, match, scope,
|
|
1017
|
+
this.searchPathIntent(artifact.artifactId, query, match, scope, regexPattern, recordHit);
|
|
1056
1018
|
}
|
|
1057
1019
|
else {
|
|
1058
|
-
this.searchTextIntent(artifact.artifactId, query, match, scope,
|
|
1020
|
+
this.searchTextIntent(artifact.artifactId, query, match, scope, regexPattern, recordHit);
|
|
1059
1021
|
}
|
|
1060
1022
|
}
|
|
1061
1023
|
}
|
|
@@ -1064,10 +1026,10 @@ export class SourceService {
|
|
|
1064
1026
|
if (!tokenOnlyTextIntent) {
|
|
1065
1027
|
this.metrics.recordSearchFallback();
|
|
1066
1028
|
if (intent === "path") {
|
|
1067
|
-
this.searchPathIntent(artifact.artifactId, query, match, scope,
|
|
1029
|
+
this.searchPathIntent(artifact.artifactId, query, match, scope, regexPattern, recordHit);
|
|
1068
1030
|
}
|
|
1069
1031
|
else {
|
|
1070
|
-
this.searchTextIntent(artifact.artifactId, query, match, scope,
|
|
1032
|
+
this.searchTextIntent(artifact.artifactId, query, match, scope, regexPattern, recordHit);
|
|
1071
1033
|
}
|
|
1072
1034
|
}
|
|
1073
1035
|
}
|
|
@@ -1078,31 +1040,11 @@ export class SourceService {
|
|
|
1078
1040
|
const nextCursor = finalizedHits.nextCursorHit
|
|
1079
1041
|
? encodeSearchCursor(finalizedHits.nextCursorHit, cursorContext)
|
|
1080
1042
|
: undefined;
|
|
1081
|
-
|
|
1082
|
-
? this.buildOneHopRelations(artifact.artifactId, page.flatMap((hit) => hit.symbol && isSymbolKind(hit.symbol.symbolKind)
|
|
1083
|
-
? [
|
|
1084
|
-
{
|
|
1085
|
-
symbolKind: hit.symbol.symbolKind,
|
|
1086
|
-
symbolName: hit.symbol.symbolName,
|
|
1087
|
-
filePath: hit.filePath,
|
|
1088
|
-
line: hit.symbol.line
|
|
1089
|
-
}
|
|
1090
|
-
]
|
|
1091
|
-
: []), 10)
|
|
1092
|
-
: undefined;
|
|
1093
|
-
if (relations?.length) {
|
|
1094
|
-
this.metrics.recordOneHopExpansion(relations.length);
|
|
1095
|
-
}
|
|
1096
|
-
this.metrics.recordSearchTokenBytesReturned(Buffer.byteLength(JSON.stringify({ hits: page, relations }), "utf8"));
|
|
1097
|
-
// B5: If post-filtering eliminated all hits on the first page, the SQL-based
|
|
1098
|
-
// totalApprox is misleading — correct it to 0.
|
|
1099
|
-
const totalApprox = page.length === 0 && !cursor ? 0 : finalizedHits.totalApprox;
|
|
1043
|
+
this.metrics.recordSearchTokenBytesReturned(Buffer.byteLength(JSON.stringify({ hits: page }), "utf8"));
|
|
1100
1044
|
return {
|
|
1101
1045
|
hits: page,
|
|
1102
|
-
relations: relations && relations.length > 0 ? relations : undefined,
|
|
1103
1046
|
nextCursor,
|
|
1104
|
-
|
|
1105
|
-
mappingApplied: artifact.mappingApplied ?? "official"
|
|
1047
|
+
mappingApplied: artifact.mappingApplied ?? "obfuscated"
|
|
1106
1048
|
};
|
|
1107
1049
|
}
|
|
1108
1050
|
finally {
|
|
@@ -1139,7 +1081,7 @@ export class SourceService {
|
|
|
1139
1081
|
content,
|
|
1140
1082
|
contentBytes: fullBytes,
|
|
1141
1083
|
truncated,
|
|
1142
|
-
mappingApplied: artifact.mappingApplied ?? "
|
|
1084
|
+
mappingApplied: artifact.mappingApplied ?? "obfuscated"
|
|
1143
1085
|
};
|
|
1144
1086
|
}
|
|
1145
1087
|
finally {
|
|
@@ -1159,7 +1101,7 @@ export class SourceService {
|
|
|
1159
1101
|
return {
|
|
1160
1102
|
items: page.items,
|
|
1161
1103
|
nextCursor: page.nextCursor,
|
|
1162
|
-
mappingApplied: artifact.mappingApplied ?? "
|
|
1104
|
+
mappingApplied: artifact.mappingApplied ?? "obfuscated"
|
|
1163
1105
|
};
|
|
1164
1106
|
}
|
|
1165
1107
|
finally {
|
|
@@ -1304,12 +1246,13 @@ export class SourceService {
|
|
|
1304
1246
|
}
|
|
1305
1247
|
const mappingApplied = workspaceDetection.mappingApplied;
|
|
1306
1248
|
if (kind === "method") {
|
|
1249
|
+
const methodOwner = owner;
|
|
1250
|
+
const methodDescriptor = descriptor;
|
|
1307
1251
|
const exact = await this.mappingService.resolveMethodMappingExact({
|
|
1308
1252
|
version,
|
|
1309
|
-
|
|
1310
|
-
owner,
|
|
1253
|
+
owner: methodOwner,
|
|
1311
1254
|
name,
|
|
1312
|
-
descriptor:
|
|
1255
|
+
descriptor: methodDescriptor,
|
|
1313
1256
|
sourceMapping: input.sourceMapping,
|
|
1314
1257
|
targetMapping: mappingApplied,
|
|
1315
1258
|
sourcePriority: input.sourcePriority
|
|
@@ -1473,14 +1416,14 @@ export class SourceService {
|
|
|
1473
1416
|
try {
|
|
1474
1417
|
let resolvedSymbols = resolvedSymbolsByVersion.get(version);
|
|
1475
1418
|
if (!resolvedSymbols) {
|
|
1476
|
-
const [
|
|
1477
|
-
this.
|
|
1478
|
-
this.
|
|
1419
|
+
const [obfuscatedClassName, obfuscatedMethod] = await Promise.all([
|
|
1420
|
+
this.resolveToObfuscatedClassName(userClassName, version, mapping, input.sourcePriority, warnings),
|
|
1421
|
+
this.resolveToObfuscatedMemberName(userMethodName, userClassName, descriptor, "method", version, mapping, input.sourcePriority, warnings)
|
|
1479
1422
|
]);
|
|
1480
1423
|
resolvedSymbols = {
|
|
1481
|
-
className:
|
|
1482
|
-
methodName:
|
|
1483
|
-
methodDescriptor:
|
|
1424
|
+
className: obfuscatedClassName,
|
|
1425
|
+
methodName: obfuscatedMethod.name,
|
|
1426
|
+
methodDescriptor: obfuscatedMethod.descriptor
|
|
1484
1427
|
};
|
|
1485
1428
|
resolvedSymbolsByVersion.set(version, resolvedSymbols);
|
|
1486
1429
|
}
|
|
@@ -1630,18 +1573,18 @@ export class SourceService {
|
|
|
1630
1573
|
});
|
|
1631
1574
|
}
|
|
1632
1575
|
const mappingWarnings = [];
|
|
1633
|
-
const
|
|
1634
|
-
const
|
|
1635
|
-
?
|
|
1636
|
-
: await this.
|
|
1576
|
+
const obfuscatedFromClassName = await this.resolveToObfuscatedClassName(className, fromVersion, mapping, input.sourcePriority, mappingWarnings);
|
|
1577
|
+
const obfuscatedToClassName = fromVersion === toVersion
|
|
1578
|
+
? obfuscatedFromClassName
|
|
1579
|
+
: await this.resolveToObfuscatedClassName(className, toVersion, mapping, input.sourcePriority, mappingWarnings);
|
|
1637
1580
|
const [fromResolved, toResolved] = await Promise.all([
|
|
1638
1581
|
this.versionService.resolveVersionJar(fromVersion),
|
|
1639
1582
|
this.versionService.resolveVersionJar(toVersion)
|
|
1640
1583
|
]);
|
|
1641
|
-
const loadSignature = async (version, jarPath,
|
|
1584
|
+
const loadSignature = async (version, jarPath, obfuscatedClassName) => {
|
|
1642
1585
|
try {
|
|
1643
1586
|
const signature = await this.explorerService.getSignature({
|
|
1644
|
-
fqn:
|
|
1587
|
+
fqn: obfuscatedClassName,
|
|
1645
1588
|
jarPath,
|
|
1646
1589
|
access: "all",
|
|
1647
1590
|
includeSynthetic: false,
|
|
@@ -1662,8 +1605,8 @@ export class SourceService {
|
|
|
1662
1605
|
}
|
|
1663
1606
|
};
|
|
1664
1607
|
const [fromSignature, toSignature] = await Promise.all([
|
|
1665
|
-
loadSignature(fromVersion, fromResolved.jarPath,
|
|
1666
|
-
loadSignature(toVersion, toResolved.jarPath,
|
|
1608
|
+
loadSignature(fromVersion, fromResolved.jarPath, obfuscatedFromClassName),
|
|
1609
|
+
loadSignature(toVersion, toResolved.jarPath, obfuscatedToClassName)
|
|
1667
1610
|
]);
|
|
1668
1611
|
const warnings = [...mappingWarnings];
|
|
1669
1612
|
if (fromSignature) {
|
|
@@ -1740,16 +1683,16 @@ export class SourceService {
|
|
|
1740
1683
|
: classChange === "absent_in_both"
|
|
1741
1684
|
? emptyDiffDelta()
|
|
1742
1685
|
: diffMembersByKey(fromMembers.fields, toMembers.fields, (member) => member.name, true);
|
|
1743
|
-
// Remap diff delta members for non-
|
|
1686
|
+
// Remap diff delta members for non-obfuscated mappings
|
|
1744
1687
|
const remapDelta = async (delta, kind) => {
|
|
1745
1688
|
const [addedResult, removedResult] = await Promise.all([
|
|
1746
|
-
this.remapSignatureMembers(delta.added, kind, toVersion, mapping, input.sourcePriority, warnings),
|
|
1747
|
-
this.remapSignatureMembers(delta.removed, kind, fromVersion, mapping, input.sourcePriority, warnings)
|
|
1689
|
+
this.remapSignatureMembers(delta.added, kind, toVersion, "obfuscated", mapping, input.sourcePriority, warnings),
|
|
1690
|
+
this.remapSignatureMembers(delta.removed, kind, fromVersion, "obfuscated", mapping, input.sourcePriority, warnings)
|
|
1748
1691
|
]);
|
|
1749
1692
|
const remappedModified = await Promise.all(delta.modified.map(async (change) => {
|
|
1750
1693
|
const [fromResult, toResult] = await Promise.all([
|
|
1751
|
-
this.remapSignatureMembers([change.from], kind, fromVersion, mapping, input.sourcePriority, warnings),
|
|
1752
|
-
this.remapSignatureMembers([change.to], kind, toVersion, mapping, input.sourcePriority, warnings)
|
|
1694
|
+
this.remapSignatureMembers([change.from], kind, fromVersion, "obfuscated", mapping, input.sourcePriority, warnings),
|
|
1695
|
+
this.remapSignatureMembers([change.to], kind, toVersion, "obfuscated", mapping, input.sourcePriority, warnings)
|
|
1753
1696
|
]);
|
|
1754
1697
|
return { ...change, from: fromResult.members[0], to: toResult.members[0] };
|
|
1755
1698
|
}));
|
|
@@ -1817,7 +1760,7 @@ export class SourceService {
|
|
|
1817
1760
|
});
|
|
1818
1761
|
}
|
|
1819
1762
|
// Verify artifact exists
|
|
1820
|
-
this.getArtifact(artifactId);
|
|
1763
|
+
const artifact = this.getArtifact(artifactId);
|
|
1821
1764
|
const limit = Math.max(1, Math.min(input.limit ?? 20, 200));
|
|
1822
1765
|
const warnings = [];
|
|
1823
1766
|
const isQualified = className.includes(".");
|
|
@@ -1847,7 +1790,17 @@ export class SourceService {
|
|
|
1847
1790
|
symbolKind: row.symbolKind
|
|
1848
1791
|
}))
|
|
1849
1792
|
.slice(0, limit);
|
|
1850
|
-
|
|
1793
|
+
const partialVanillaLookup = hasPartialNetMinecraftCoverage(artifact.qualityFlags) && looksLikeDeobfuscatedClassName(className);
|
|
1794
|
+
const filteredMatches = partialVanillaLookup && matches.every((match) => !match.qualifiedName.startsWith("net.minecraft.") && !match.qualifiedName.startsWith("com.mojang."))
|
|
1795
|
+
? []
|
|
1796
|
+
: matches;
|
|
1797
|
+
if (filteredMatches.length === 0 && partialVanillaLookup) {
|
|
1798
|
+
warnings.push(`Artifact source coverage is partial and excludes net.minecraft; returning non-vanilla matches for "${className}" would be misleading. Use get-class-source/get-class-members for binary fallback or get-class-api-matrix for mapped API inspection.`);
|
|
1799
|
+
}
|
|
1800
|
+
if (filteredMatches.length === 0 && artifact.mappingApplied === "obfuscated" && looksLikeDeobfuscatedClassName(className)) {
|
|
1801
|
+
warnings.push(`No exact class symbol matched "${className}". ${obfuscatedNamespaceHint(className)}`);
|
|
1802
|
+
}
|
|
1803
|
+
return { matches: filteredMatches, total: filteredMatches.length, warnings };
|
|
1851
1804
|
}
|
|
1852
1805
|
// Simple name: search for exact symbol name match among type symbols
|
|
1853
1806
|
const result = this.symbolsRepo.findScopedSymbols({
|
|
@@ -1871,7 +1824,17 @@ export class SourceService {
|
|
|
1871
1824
|
symbolKind: row.symbolKind
|
|
1872
1825
|
});
|
|
1873
1826
|
}
|
|
1874
|
-
|
|
1827
|
+
const partialVanillaLookup = hasPartialNetMinecraftCoverage(artifact.qualityFlags) && looksLikeDeobfuscatedClassName(className);
|
|
1828
|
+
const filteredMatches = partialVanillaLookup && matches.every((match) => !match.qualifiedName.startsWith("net.minecraft.") && !match.qualifiedName.startsWith("com.mojang."))
|
|
1829
|
+
? []
|
|
1830
|
+
: matches;
|
|
1831
|
+
if (filteredMatches.length === 0 && partialVanillaLookup) {
|
|
1832
|
+
warnings.push(`Artifact source coverage is partial and excludes net.minecraft; returning non-vanilla matches for "${className}" would be misleading. Use get-class-source/get-class-members for binary fallback or get-class-api-matrix for mapped API inspection.`);
|
|
1833
|
+
}
|
|
1834
|
+
if (filteredMatches.length === 0 && artifact.mappingApplied === "obfuscated" && looksLikeDeobfuscatedClassName(className)) {
|
|
1835
|
+
warnings.push(`No exact class symbol matched "${className}". ${obfuscatedNamespaceHint(className)}`);
|
|
1836
|
+
}
|
|
1837
|
+
return { matches: filteredMatches, total: filteredMatches.length, warnings };
|
|
1875
1838
|
}
|
|
1876
1839
|
async getClassSource(input) {
|
|
1877
1840
|
const className = input.className.trim();
|
|
@@ -1919,6 +1882,10 @@ export class SourceService {
|
|
|
1919
1882
|
let mappingApplied = requestedMapping;
|
|
1920
1883
|
let provenance;
|
|
1921
1884
|
let qualityFlags = [];
|
|
1885
|
+
let sourceJarPath;
|
|
1886
|
+
let binaryJarPath;
|
|
1887
|
+
let version;
|
|
1888
|
+
let coordinate;
|
|
1922
1889
|
if (!artifactId) {
|
|
1923
1890
|
if (!input.target) {
|
|
1924
1891
|
throw createError({
|
|
@@ -1943,6 +1910,10 @@ export class SourceService {
|
|
|
1943
1910
|
mappingApplied = resolved.mappingApplied;
|
|
1944
1911
|
provenance = resolved.provenance;
|
|
1945
1912
|
qualityFlags = [...resolved.qualityFlags];
|
|
1913
|
+
sourceJarPath = resolved.resolvedSourceJarPath;
|
|
1914
|
+
binaryJarPath = resolved.binaryJarPath;
|
|
1915
|
+
version = resolved.version;
|
|
1916
|
+
coordinate = resolved.coordinate;
|
|
1946
1917
|
}
|
|
1947
1918
|
else {
|
|
1948
1919
|
const artifact = this.getArtifact(artifactId);
|
|
@@ -1951,53 +1922,122 @@ export class SourceService {
|
|
|
1951
1922
|
mappingApplied = artifact.mappingApplied ?? requestedMapping;
|
|
1952
1923
|
provenance = artifact.provenance;
|
|
1953
1924
|
qualityFlags = artifact.qualityFlags;
|
|
1925
|
+
sourceJarPath = artifact.sourceJarPath;
|
|
1926
|
+
binaryJarPath = artifact.binaryJarPath;
|
|
1927
|
+
version = artifact.version;
|
|
1928
|
+
coordinate = artifact.coordinate;
|
|
1929
|
+
}
|
|
1930
|
+
let activeArtifactId = artifactId;
|
|
1931
|
+
let activeOrigin = origin;
|
|
1932
|
+
let activeProvenance = provenance;
|
|
1933
|
+
let activeQualityFlags = [...qualityFlags];
|
|
1934
|
+
let activeMappingApplied = mappingApplied;
|
|
1935
|
+
let activeSourceJarPath = sourceJarPath;
|
|
1936
|
+
let attemptedBinaryFallback = false;
|
|
1937
|
+
const tryBinaryFallback = async () => {
|
|
1938
|
+
if (attemptedBinaryFallback) {
|
|
1939
|
+
return false;
|
|
1940
|
+
}
|
|
1941
|
+
attemptedBinaryFallback = true;
|
|
1942
|
+
const normalizedBinaryJarPath = normalizeOptionalString(binaryJarPath);
|
|
1943
|
+
if (!normalizedBinaryJarPath) {
|
|
1944
|
+
return false;
|
|
1945
|
+
}
|
|
1946
|
+
if (activeSourceJarPath &&
|
|
1947
|
+
normalizePathStyle(activeSourceJarPath) === normalizePathStyle(normalizedBinaryJarPath)) {
|
|
1948
|
+
return false;
|
|
1949
|
+
}
|
|
1950
|
+
const fallbackResolved = await this.resolveBinaryFallbackArtifact({
|
|
1951
|
+
binaryJarPath: normalizedBinaryJarPath,
|
|
1952
|
+
version,
|
|
1953
|
+
coordinate,
|
|
1954
|
+
requestedMapping,
|
|
1955
|
+
mappingApplied,
|
|
1956
|
+
provenance: activeProvenance,
|
|
1957
|
+
qualityFlags: activeQualityFlags
|
|
1958
|
+
});
|
|
1959
|
+
if (!fallbackResolved || fallbackResolved.artifactId === activeArtifactId) {
|
|
1960
|
+
return false;
|
|
1961
|
+
}
|
|
1962
|
+
activeArtifactId = fallbackResolved.artifactId;
|
|
1963
|
+
activeOrigin = fallbackResolved.origin;
|
|
1964
|
+
activeMappingApplied = fallbackResolved.mappingApplied ?? activeMappingApplied;
|
|
1965
|
+
activeProvenance = fallbackResolved.provenance ?? activeProvenance;
|
|
1966
|
+
activeQualityFlags = [...new Set([...(fallbackResolved.qualityFlags ?? []), "binary-fallback"])];
|
|
1967
|
+
activeSourceJarPath = fallbackResolved.sourceJarPath;
|
|
1968
|
+
warnings.push(`Falling back to binary artifact "${normalizedBinaryJarPath}" because source coverage for "${className}" was incomplete.`);
|
|
1969
|
+
if (activeMappingApplied !== requestedMapping) {
|
|
1970
|
+
warnings.push(`Fallback source text is indexed in ${activeMappingApplied} names; returned source is not remapped to ${requestedMapping}.`);
|
|
1971
|
+
}
|
|
1972
|
+
return true;
|
|
1973
|
+
};
|
|
1974
|
+
let activeLookupClassName = await this.resolveClassNameForLookup({
|
|
1975
|
+
className,
|
|
1976
|
+
version,
|
|
1977
|
+
sourceMapping: requestedMapping,
|
|
1978
|
+
targetMapping: activeMappingApplied,
|
|
1979
|
+
sourcePriority: input.sourcePriority,
|
|
1980
|
+
warnings,
|
|
1981
|
+
context: "source lookup"
|
|
1982
|
+
});
|
|
1983
|
+
let filePath = this.resolveClassFilePath(activeArtifactId, activeLookupClassName);
|
|
1984
|
+
if (!filePath && (await tryBinaryFallback())) {
|
|
1985
|
+
activeLookupClassName = await this.resolveClassNameForLookup({
|
|
1986
|
+
className,
|
|
1987
|
+
version,
|
|
1988
|
+
sourceMapping: requestedMapping,
|
|
1989
|
+
targetMapping: activeMappingApplied,
|
|
1990
|
+
sourcePriority: input.sourcePriority,
|
|
1991
|
+
warnings,
|
|
1992
|
+
context: "source lookup"
|
|
1993
|
+
});
|
|
1994
|
+
filePath = this.resolveClassFilePath(activeArtifactId, activeLookupClassName);
|
|
1954
1995
|
}
|
|
1955
|
-
const filePath = this.resolveClassFilePath(artifactId, className);
|
|
1956
1996
|
if (!filePath) {
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
throw createError({
|
|
1971
|
-
code: ERROR_CODES.CLASS_NOT_FOUND,
|
|
1972
|
-
message: `Source for class "${className}" was not found.`,
|
|
1973
|
-
details: {
|
|
1974
|
-
artifactId,
|
|
1975
|
-
className,
|
|
1976
|
-
...(scope ? { scope } : {}),
|
|
1977
|
-
...(targetKind ? { targetKind } : {}),
|
|
1978
|
-
...(targetValue ? { targetValue } : {}),
|
|
1979
|
-
mapping: mappingApplied,
|
|
1980
|
-
nextAction,
|
|
1981
|
-
suggestedCall: { tool: "find-class", params: { className: simpleName, artifactId } }
|
|
1982
|
-
}
|
|
1997
|
+
throw this.buildClassSourceNotFoundError({
|
|
1998
|
+
artifactId: activeArtifactId,
|
|
1999
|
+
className,
|
|
2000
|
+
lookupClassName: activeLookupClassName,
|
|
2001
|
+
mappingApplied: activeMappingApplied,
|
|
2002
|
+
requestedMapping,
|
|
2003
|
+
qualityFlags: activeQualityFlags,
|
|
2004
|
+
attemptedBinaryFallback,
|
|
2005
|
+
targetKind: input.target?.kind,
|
|
2006
|
+
targetValue: input.target?.value,
|
|
2007
|
+
scope: input.scope,
|
|
2008
|
+
projectPath: input.projectPath,
|
|
2009
|
+
version
|
|
1983
2010
|
});
|
|
1984
2011
|
}
|
|
1985
|
-
|
|
2012
|
+
let row = this.filesRepo.getFileContent(activeArtifactId, filePath);
|
|
2013
|
+
if (!row && (await tryBinaryFallback())) {
|
|
2014
|
+
activeLookupClassName = await this.resolveClassNameForLookup({
|
|
2015
|
+
className,
|
|
2016
|
+
version,
|
|
2017
|
+
sourceMapping: requestedMapping,
|
|
2018
|
+
targetMapping: activeMappingApplied,
|
|
2019
|
+
sourcePriority: input.sourcePriority,
|
|
2020
|
+
warnings,
|
|
2021
|
+
context: "source lookup"
|
|
2022
|
+
});
|
|
2023
|
+
filePath = this.resolveClassFilePath(activeArtifactId, activeLookupClassName) ?? filePath;
|
|
2024
|
+
row = this.filesRepo.getFileContent(activeArtifactId, filePath);
|
|
2025
|
+
}
|
|
1986
2026
|
if (!row) {
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2027
|
+
throw this.buildClassSourceNotFoundError({
|
|
2028
|
+
artifactId: activeArtifactId,
|
|
2029
|
+
className,
|
|
2030
|
+
lookupClassName: activeLookupClassName,
|
|
2031
|
+
mappingApplied: activeMappingApplied,
|
|
2032
|
+
requestedMapping,
|
|
2033
|
+
qualityFlags: activeQualityFlags,
|
|
2034
|
+
attemptedBinaryFallback,
|
|
2035
|
+
filePath,
|
|
2036
|
+
targetKind: input.target?.kind,
|
|
2037
|
+
targetValue: input.target?.value,
|
|
2038
|
+
scope: input.scope,
|
|
2039
|
+
projectPath: input.projectPath,
|
|
2040
|
+
version
|
|
2001
2041
|
});
|
|
2002
2042
|
}
|
|
2003
2043
|
const lines = row.content.split(/\r?\n/);
|
|
@@ -2048,12 +2088,12 @@ export class SourceService {
|
|
|
2048
2088
|
resolvedOutputFile = outputPath;
|
|
2049
2089
|
sourceText = `[Written to ${outputPath}]`;
|
|
2050
2090
|
}
|
|
2051
|
-
const normalizedProvenance =
|
|
2091
|
+
const normalizedProvenance = activeProvenance ??
|
|
2052
2092
|
this.buildFallbackProvenance({
|
|
2053
|
-
artifactId,
|
|
2054
|
-
origin,
|
|
2093
|
+
artifactId: activeArtifactId,
|
|
2094
|
+
origin: activeOrigin,
|
|
2055
2095
|
requestedMapping,
|
|
2056
|
-
mappingApplied
|
|
2096
|
+
mappingApplied: activeMappingApplied
|
|
2057
2097
|
});
|
|
2058
2098
|
return {
|
|
2059
2099
|
className,
|
|
@@ -2066,12 +2106,12 @@ export class SourceService {
|
|
|
2066
2106
|
},
|
|
2067
2107
|
truncated,
|
|
2068
2108
|
...(charsTruncated ? { charsTruncated } : {}),
|
|
2069
|
-
origin,
|
|
2070
|
-
artifactId,
|
|
2109
|
+
origin: activeOrigin,
|
|
2110
|
+
artifactId: activeArtifactId,
|
|
2071
2111
|
requestedMapping,
|
|
2072
|
-
mappingApplied,
|
|
2112
|
+
mappingApplied: activeMappingApplied,
|
|
2073
2113
|
provenance: normalizedProvenance,
|
|
2074
|
-
qualityFlags,
|
|
2114
|
+
qualityFlags: activeQualityFlags,
|
|
2075
2115
|
...(resolvedOutputFile ? { outputFile: resolvedOutputFile } : {}),
|
|
2076
2116
|
warnings
|
|
2077
2117
|
};
|
|
@@ -2148,10 +2188,10 @@ export class SourceService {
|
|
|
2148
2188
|
binaryJarPath = artifact.binaryJarPath;
|
|
2149
2189
|
version = artifact.version;
|
|
2150
2190
|
}
|
|
2151
|
-
if (requestedMapping !== "
|
|
2191
|
+
if (requestedMapping !== "obfuscated" && !version) {
|
|
2152
2192
|
throw createError({
|
|
2153
2193
|
code: ERROR_CODES.MAPPING_NOT_APPLIED,
|
|
2154
|
-
message: `Non-
|
|
2194
|
+
message: `Non-obfuscated mapping "${requestedMapping}" requires a version, but none was resolved.`,
|
|
2155
2195
|
details: {
|
|
2156
2196
|
mapping: requestedMapping,
|
|
2157
2197
|
nextAction: "Resolve with targetKind=version or specify a versioned coordinate.",
|
|
@@ -2170,29 +2210,35 @@ export class SourceService {
|
|
|
2170
2210
|
}
|
|
2171
2211
|
});
|
|
2172
2212
|
}
|
|
2173
|
-
const
|
|
2174
|
-
|
|
2175
|
-
|
|
2213
|
+
const lookupClassName = await this.resolveClassNameForLookup({
|
|
2214
|
+
className,
|
|
2215
|
+
version,
|
|
2216
|
+
sourceMapping: requestedMapping,
|
|
2217
|
+
targetMapping: mappingApplied,
|
|
2218
|
+
sourcePriority: input.sourcePriority,
|
|
2219
|
+
warnings,
|
|
2220
|
+
context: "binary lookup"
|
|
2221
|
+
});
|
|
2176
2222
|
const signature = await this.explorerService.getSignature({
|
|
2177
|
-
fqn:
|
|
2223
|
+
fqn: lookupClassName,
|
|
2178
2224
|
jarPath: binaryJarPath,
|
|
2179
2225
|
access,
|
|
2180
2226
|
includeSynthetic,
|
|
2181
2227
|
includeInherited,
|
|
2182
|
-
memberPattern: requestedMapping ===
|
|
2228
|
+
memberPattern: requestedMapping === mappingApplied ? memberPattern : undefined
|
|
2183
2229
|
});
|
|
2184
2230
|
warnings.push(...signature.warnings);
|
|
2185
2231
|
let remappedConstructors = version != null
|
|
2186
|
-
? (await this.remapSignatureMembers(signature.constructors, "method", version, requestedMapping, input.sourcePriority, warnings)).members
|
|
2232
|
+
? (await this.remapSignatureMembers(signature.constructors, "method", version, mappingApplied, requestedMapping, input.sourcePriority, warnings)).members
|
|
2187
2233
|
: signature.constructors;
|
|
2188
2234
|
let remappedFields = version != null
|
|
2189
|
-
? (await this.remapSignatureMembers(signature.fields, "field", version, requestedMapping, input.sourcePriority, warnings)).members
|
|
2235
|
+
? (await this.remapSignatureMembers(signature.fields, "field", version, mappingApplied, requestedMapping, input.sourcePriority, warnings)).members
|
|
2190
2236
|
: signature.fields;
|
|
2191
2237
|
let remappedMethods = version != null
|
|
2192
|
-
? (await this.remapSignatureMembers(signature.methods, "method", version, requestedMapping, input.sourcePriority, warnings)).members
|
|
2238
|
+
? (await this.remapSignatureMembers(signature.methods, "method", version, mappingApplied, requestedMapping, input.sourcePriority, warnings)).members
|
|
2193
2239
|
: signature.methods;
|
|
2194
|
-
// Apply memberPattern
|
|
2195
|
-
if (requestedMapping !==
|
|
2240
|
+
// Apply memberPattern after remap when the lookup namespace differs from the requested namespace.
|
|
2241
|
+
if (requestedMapping !== mappingApplied && memberPattern) {
|
|
2196
2242
|
const lowerPattern = memberPattern.toLowerCase();
|
|
2197
2243
|
remappedConstructors = remappedConstructors.filter((m) => m.name.toLowerCase().includes(lowerPattern));
|
|
2198
2244
|
remappedFields = remappedFields.filter((m) => m.name.toLowerCase().includes(lowerPattern));
|
|
@@ -2248,91 +2294,68 @@ export class SourceService {
|
|
|
2248
2294
|
};
|
|
2249
2295
|
}
|
|
2250
2296
|
async validateMixin(input) {
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
const
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
catch (err) {
|
|
2266
|
-
throw createError({
|
|
2267
|
-
code: ERROR_CODES.INVALID_INPUT,
|
|
2268
|
-
message: `Could not read/parse mixinConfigPath "${rawConfigPath}": ${err instanceof Error ? err.message : String(err)}`
|
|
2269
|
-
});
|
|
2270
|
-
}
|
|
2271
|
-
const pkg = configJson.package ?? "";
|
|
2272
|
-
const classNames = [
|
|
2273
|
-
...(configJson.mixins ?? []),
|
|
2274
|
-
...(configJson.client ?? []),
|
|
2275
|
-
...(configJson.server ?? [])
|
|
2276
|
-
];
|
|
2277
|
-
if (classNames.length === 0) {
|
|
2278
|
-
continue; // Skip empty configs in array mode
|
|
2279
|
-
}
|
|
2280
|
-
// Determine source root(s)
|
|
2281
|
-
const projectBase = input.projectPath
|
|
2282
|
-
? (isAbsolute(input.projectPath) ? input.projectPath : resolvePath(process.cwd(), input.projectPath))
|
|
2283
|
-
: dirname(resolvedConfigPath);
|
|
2284
|
-
let sourceRootCandidates;
|
|
2285
|
-
if (input.sourceRoots && input.sourceRoots.length > 0) {
|
|
2286
|
-
sourceRootCandidates = input.sourceRoots;
|
|
2287
|
-
}
|
|
2288
|
-
else if (input.sourceRoot) {
|
|
2289
|
-
sourceRootCandidates = [input.sourceRoot];
|
|
2290
|
-
}
|
|
2291
|
-
else {
|
|
2292
|
-
// Auto-detect: include any root that contains at least one configured mixin class.
|
|
2293
|
-
const detected = COMMON_SOURCE_ROOTS.filter((candidateRoot) => classNames.some((className) => {
|
|
2294
|
-
const fqcn = pkg ? `${pkg}.${className}` : className;
|
|
2295
|
-
const relative = fqcn.replace(/\./g, "/") + ".java";
|
|
2296
|
-
return existsSync(resolvePath(projectBase, candidateRoot, relative));
|
|
2297
|
-
}));
|
|
2298
|
-
if (detected.length > 0) {
|
|
2299
|
-
sourceRootCandidates = detected;
|
|
2300
|
-
}
|
|
2301
|
-
else {
|
|
2302
|
-
sourceRootCandidates = ["src/main/java"];
|
|
2303
|
-
}
|
|
2297
|
+
const { input: sourceInput, ...sharedInput } = input;
|
|
2298
|
+
const mode = sourceInput.mode;
|
|
2299
|
+
if (mode === "inline") {
|
|
2300
|
+
const singleResult = await this.validateMixinSingle({
|
|
2301
|
+
...sharedInput,
|
|
2302
|
+
source: sourceInput.source
|
|
2303
|
+
});
|
|
2304
|
+
return this.buildValidateMixinOutput(mode, [
|
|
2305
|
+
{
|
|
2306
|
+
source: {
|
|
2307
|
+
kind: "inline",
|
|
2308
|
+
label: "<inline>"
|
|
2309
|
+
},
|
|
2310
|
+
result: singleResult
|
|
2304
2311
|
}
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
}
|
|
2312
|
+
]);
|
|
2313
|
+
}
|
|
2314
|
+
if (mode === "path") {
|
|
2315
|
+
const resolvedPath = this.resolveMixinInputPath(sourceInput.path, "path");
|
|
2316
|
+
const singleResult = await this.validateMixinSingle({
|
|
2317
|
+
...sharedInput,
|
|
2318
|
+
sourcePath: sourceInput.path
|
|
2319
|
+
});
|
|
2320
|
+
return this.buildValidateMixinOutput(mode, [
|
|
2321
|
+
{
|
|
2322
|
+
source: {
|
|
2323
|
+
kind: "path",
|
|
2324
|
+
label: resolvedPath,
|
|
2325
|
+
path: resolvedPath
|
|
2326
|
+
},
|
|
2327
|
+
result: singleResult
|
|
2322
2328
|
}
|
|
2323
|
-
|
|
2324
|
-
if (allSourcePaths.length === 0) {
|
|
2325
|
-
throw createError({
|
|
2326
|
-
code: ERROR_CODES.INVALID_INPUT,
|
|
2327
|
-
message: `Mixin config(s) contain no mixin class entries.`
|
|
2328
|
-
});
|
|
2329
|
-
}
|
|
2330
|
-
return this.validateMixinBatch({ ...input, mixinConfigPath: undefined, sourcePaths: allSourcePaths });
|
|
2329
|
+
]);
|
|
2331
2330
|
}
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2331
|
+
if (mode === "paths") {
|
|
2332
|
+
return this.validateMixinMany(mode, sourceInput.paths.map((path) => ({
|
|
2333
|
+
source: {
|
|
2334
|
+
kind: "path",
|
|
2335
|
+
label: this.resolveMixinInputPath(path, "path"),
|
|
2336
|
+
path: this.resolveMixinInputPath(path, "path")
|
|
2337
|
+
},
|
|
2338
|
+
sourcePath: path
|
|
2339
|
+
})), input);
|
|
2340
|
+
}
|
|
2341
|
+
const configSources = await this.resolveMixinConfigSources(input);
|
|
2342
|
+
if (configSources.length === 0) {
|
|
2343
|
+
throw createError({
|
|
2344
|
+
code: ERROR_CODES.INVALID_INPUT,
|
|
2345
|
+
message: "Mixin config(s) contain no mixin class entries."
|
|
2346
|
+
});
|
|
2335
2347
|
}
|
|
2348
|
+
return this.validateMixinMany(mode, configSources.map((entry) => ({
|
|
2349
|
+
source: {
|
|
2350
|
+
kind: "config",
|
|
2351
|
+
label: entry.sourcePath,
|
|
2352
|
+
path: entry.sourcePath,
|
|
2353
|
+
configPath: entry.configPath
|
|
2354
|
+
},
|
|
2355
|
+
sourcePath: entry.sourcePath
|
|
2356
|
+
})), input);
|
|
2357
|
+
}
|
|
2358
|
+
async validateMixinSingle(input) {
|
|
2336
2359
|
let version = input.version.trim();
|
|
2337
2360
|
if (!version) {
|
|
2338
2361
|
throw createError({ code: ERROR_CODES.INVALID_INPUT, message: "version must be non-empty." });
|
|
@@ -2489,51 +2512,51 @@ export class SourceService {
|
|
|
2489
2512
|
}
|
|
2490
2513
|
}
|
|
2491
2514
|
}
|
|
2492
|
-
let
|
|
2493
|
-
if (requestedMapping !== "
|
|
2515
|
+
let obfuscatedName = resolvedClassName;
|
|
2516
|
+
if (requestedMapping !== "obfuscated") {
|
|
2494
2517
|
try {
|
|
2495
2518
|
const mapped = await this.mappingService.findMapping({
|
|
2496
2519
|
version,
|
|
2497
2520
|
kind: "class",
|
|
2498
2521
|
name: resolvedClassName,
|
|
2499
2522
|
sourceMapping: requestedMapping,
|
|
2500
|
-
targetMapping: "
|
|
2523
|
+
targetMapping: "obfuscated",
|
|
2501
2524
|
sourcePriority: input.sourcePriority
|
|
2502
2525
|
});
|
|
2503
2526
|
if (mapped.resolved && mapped.resolvedSymbol) {
|
|
2504
|
-
|
|
2505
|
-
resolutionTrace?.push({ target: target.className, step: "mapping", input: resolvedClassName, output:
|
|
2527
|
+
obfuscatedName = mapped.resolvedSymbol.name;
|
|
2528
|
+
resolutionTrace?.push({ target: target.className, step: "mapping", input: resolvedClassName, output: obfuscatedName, success: true });
|
|
2506
2529
|
}
|
|
2507
2530
|
else {
|
|
2508
|
-
warnings.push(`Could not map class "${resolvedClassName}" from ${requestedMapping} to
|
|
2531
|
+
warnings.push(`Could not map class "${resolvedClassName}" from ${requestedMapping} to obfuscated; using "${obfuscatedName}" for lookup.`);
|
|
2509
2532
|
mappingFailedTargets.add(target.className);
|
|
2510
|
-
resolutionTrace?.push({ target: target.className, step: "mapping", input: resolvedClassName, output:
|
|
2533
|
+
resolutionTrace?.push({ target: target.className, step: "mapping", input: resolvedClassName, output: obfuscatedName, success: false, detail: "No mapping found" });
|
|
2511
2534
|
}
|
|
2512
2535
|
}
|
|
2513
2536
|
catch (mapErr) {
|
|
2514
|
-
warnings.push(`Mapping lookup failed for class "${resolvedClassName}"; using "${
|
|
2537
|
+
warnings.push(`Mapping lookup failed for class "${resolvedClassName}"; using "${obfuscatedName}" for lookup.`);
|
|
2515
2538
|
mappingFailedTargets.add(target.className);
|
|
2516
|
-
resolutionTrace?.push({ target: target.className, step: "mapping", input: resolvedClassName, output:
|
|
2539
|
+
resolutionTrace?.push({ target: target.className, step: "mapping", input: resolvedClassName, output: obfuscatedName, success: false, detail: mapErr instanceof Error ? mapErr.message : String(mapErr) });
|
|
2517
2540
|
}
|
|
2518
2541
|
}
|
|
2519
2542
|
try {
|
|
2520
2543
|
const sig = await this.explorerService.getSignature({
|
|
2521
|
-
fqn:
|
|
2544
|
+
fqn: obfuscatedName,
|
|
2522
2545
|
jarPath,
|
|
2523
2546
|
access: "all"
|
|
2524
2547
|
});
|
|
2525
2548
|
warnings.push(...sig.warnings);
|
|
2526
|
-
resolutionTrace?.push({ target: target.className, step: "signature", input:
|
|
2549
|
+
resolutionTrace?.push({ target: target.className, step: "signature", input: obfuscatedName, output: `${sig.methods.length} methods, ${sig.fields.length} fields`, success: true });
|
|
2527
2550
|
// Bug 2 fix: remap signature members to requested mapping
|
|
2528
2551
|
let constructors = sig.constructors;
|
|
2529
2552
|
let methods = sig.methods;
|
|
2530
2553
|
let fields = sig.fields;
|
|
2531
|
-
if (requestedMapping !== "
|
|
2554
|
+
if (requestedMapping !== "obfuscated") {
|
|
2532
2555
|
try {
|
|
2533
2556
|
const [ctorResult, methodResult, fieldResult] = await Promise.all([
|
|
2534
|
-
this.remapSignatureMembers(sig.constructors, "method", version, requestedMapping, input.sourcePriority, warnings),
|
|
2535
|
-
this.remapSignatureMembers(sig.methods, "method", version, requestedMapping, input.sourcePriority, warnings),
|
|
2536
|
-
this.remapSignatureMembers(sig.fields, "field", version, requestedMapping, input.sourcePriority, warnings)
|
|
2557
|
+
this.remapSignatureMembers(sig.constructors, "method", version, "obfuscated", requestedMapping, input.sourcePriority, warnings),
|
|
2558
|
+
this.remapSignatureMembers(sig.methods, "method", version, "obfuscated", requestedMapping, input.sourcePriority, warnings),
|
|
2559
|
+
this.remapSignatureMembers(sig.fields, "field", version, "obfuscated", requestedMapping, input.sourcePriority, warnings)
|
|
2537
2560
|
]);
|
|
2538
2561
|
constructors = ctorResult.members;
|
|
2539
2562
|
methods = methodResult.members;
|
|
@@ -2555,9 +2578,9 @@ export class SourceService {
|
|
|
2555
2578
|
}
|
|
2556
2579
|
}
|
|
2557
2580
|
catch (remapErr) {
|
|
2558
|
-
warnings.push(`Member remapping failed for "${resolvedClassName}"; falling back to
|
|
2559
|
-
mappingApplied = "
|
|
2560
|
-
resolutionTrace?.push({ target: target.className, step: "remap", input: resolvedClassName, output: "
|
|
2581
|
+
warnings.push(`Member remapping failed for "${resolvedClassName}"; falling back to obfuscated names. Member names shown may be in the obfuscated runtime namespace.`);
|
|
2582
|
+
mappingApplied = "obfuscated";
|
|
2583
|
+
resolutionTrace?.push({ target: target.className, step: "remap", input: resolvedClassName, output: "obfuscated fallback", success: false, detail: remapErr instanceof Error ? remapErr.message : String(remapErr) });
|
|
2561
2584
|
}
|
|
2562
2585
|
}
|
|
2563
2586
|
targetMembers.set(target.className, {
|
|
@@ -2568,9 +2591,9 @@ export class SourceService {
|
|
|
2568
2591
|
});
|
|
2569
2592
|
}
|
|
2570
2593
|
catch (sigErr) {
|
|
2571
|
-
warnings.push(`Could not load signature for class "${resolvedClassName}" (
|
|
2594
|
+
warnings.push(`Could not load signature for class "${resolvedClassName}" (obfuscated: "${obfuscatedName}").`);
|
|
2572
2595
|
signatureFailedTargets.add(target.className);
|
|
2573
|
-
resolutionTrace?.push({ target: target.className, step: "signature", input:
|
|
2596
|
+
resolutionTrace?.push({ target: target.className, step: "signature", input: obfuscatedName, output: "CLASS_NOT_FOUND", success: false, detail: sigErr instanceof Error ? sigErr.message : String(sigErr) });
|
|
2574
2597
|
// Fallback: check if the symbol exists in the mapping graph even though getSignature failed
|
|
2575
2598
|
try {
|
|
2576
2599
|
const existenceCheck = await this.mappingService.checkSymbolExists({
|
|
@@ -2617,8 +2640,8 @@ export class SourceService {
|
|
|
2617
2640
|
}
|
|
2618
2641
|
// Build mapping chain description
|
|
2619
2642
|
const mappingChain = [];
|
|
2620
|
-
if (requestedMapping !== "
|
|
2621
|
-
mappingChain.push(`${requestedMapping} →
|
|
2643
|
+
if (requestedMapping !== "obfuscated") {
|
|
2644
|
+
mappingChain.push(`${requestedMapping} → obfuscated`);
|
|
2622
2645
|
if (mappingApplied !== requestedMapping) {
|
|
2623
2646
|
mappingChain.push(`fallback to ${mappingApplied}`);
|
|
2624
2647
|
}
|
|
@@ -2713,45 +2736,123 @@ export class SourceService {
|
|
|
2713
2736
|
}
|
|
2714
2737
|
return result;
|
|
2715
2738
|
}
|
|
2716
|
-
|
|
2717
|
-
const
|
|
2739
|
+
resolveMixinInputPath(rawPath, fieldName) {
|
|
2740
|
+
const normalizedPath = normalizePathForHost(rawPath, undefined, fieldName);
|
|
2741
|
+
return isAbsolute(normalizedPath)
|
|
2742
|
+
? normalizedPath
|
|
2743
|
+
: resolvePath(process.cwd(), normalizedPath);
|
|
2744
|
+
}
|
|
2745
|
+
async resolveMixinConfigSources(input) {
|
|
2746
|
+
if (input.input.mode !== "config") {
|
|
2747
|
+
return [];
|
|
2748
|
+
}
|
|
2749
|
+
const results = [];
|
|
2750
|
+
for (const rawConfigPath of input.input.configPaths) {
|
|
2751
|
+
const resolvedConfigPath = this.resolveMixinInputPath(rawConfigPath, "configPath");
|
|
2752
|
+
let configJson;
|
|
2753
|
+
try {
|
|
2754
|
+
const raw = await readFile(resolvedConfigPath, "utf-8");
|
|
2755
|
+
configJson = JSON.parse(raw);
|
|
2756
|
+
}
|
|
2757
|
+
catch (err) {
|
|
2758
|
+
throw createError({
|
|
2759
|
+
code: ERROR_CODES.INVALID_INPUT,
|
|
2760
|
+
message: `Could not read/parse mixin config "${rawConfigPath}": ${err instanceof Error ? err.message : String(err)}`
|
|
2761
|
+
});
|
|
2762
|
+
}
|
|
2763
|
+
const pkg = configJson.package ?? "";
|
|
2764
|
+
const classNames = [
|
|
2765
|
+
...(configJson.mixins ?? []),
|
|
2766
|
+
...(configJson.client ?? []),
|
|
2767
|
+
...(configJson.server ?? [])
|
|
2768
|
+
];
|
|
2769
|
+
if (classNames.length === 0) {
|
|
2770
|
+
continue;
|
|
2771
|
+
}
|
|
2772
|
+
const projectBase = input.projectPath
|
|
2773
|
+
? (isAbsolute(input.projectPath) ? input.projectPath : resolvePath(process.cwd(), input.projectPath))
|
|
2774
|
+
: dirname(resolvedConfigPath);
|
|
2775
|
+
let sourceRootCandidates;
|
|
2776
|
+
if (input.sourceRoots && input.sourceRoots.length > 0) {
|
|
2777
|
+
sourceRootCandidates = input.sourceRoots;
|
|
2778
|
+
}
|
|
2779
|
+
else {
|
|
2780
|
+
const detected = COMMON_SOURCE_ROOTS.filter((candidateRoot) => classNames.some((className) => {
|
|
2781
|
+
const fqcn = pkg ? `${pkg}.${className}` : className;
|
|
2782
|
+
const relative = fqcn.replace(/\./g, "/") + ".java";
|
|
2783
|
+
return existsSync(resolvePath(projectBase, candidateRoot, relative));
|
|
2784
|
+
}));
|
|
2785
|
+
sourceRootCandidates = detected.length > 0 ? detected : ["src/main/java"];
|
|
2786
|
+
}
|
|
2787
|
+
for (const cls of classNames) {
|
|
2788
|
+
const fqcn = pkg ? `${pkg}.${cls}` : cls;
|
|
2789
|
+
const relativePath = fqcn.replace(/\./g, "/") + ".java";
|
|
2790
|
+
let sourcePath = resolvePath(projectBase, sourceRootCandidates[0], relativePath);
|
|
2791
|
+
for (const root of sourceRootCandidates) {
|
|
2792
|
+
const candidate = resolvePath(projectBase, root, relativePath);
|
|
2793
|
+
if (existsSync(candidate)) {
|
|
2794
|
+
sourcePath = candidate;
|
|
2795
|
+
break;
|
|
2796
|
+
}
|
|
2797
|
+
}
|
|
2798
|
+
results.push({
|
|
2799
|
+
sourcePath,
|
|
2800
|
+
configPath: resolvedConfigPath
|
|
2801
|
+
});
|
|
2802
|
+
}
|
|
2803
|
+
}
|
|
2804
|
+
return results;
|
|
2805
|
+
}
|
|
2806
|
+
async validateMixinMany(mode, entries, input) {
|
|
2718
2807
|
const results = [];
|
|
2719
|
-
let validCount = 0;
|
|
2720
|
-
let invalidCount = 0;
|
|
2721
|
-
let errorCount = 0;
|
|
2722
|
-
// P5: default warningMode to "aggregated" in batch mode
|
|
2723
2808
|
const batchWarningMode = input.warningMode ?? "aggregated";
|
|
2724
|
-
|
|
2809
|
+
const { input: _discardedInput, ...sharedInput } = input;
|
|
2810
|
+
for (const entry of entries) {
|
|
2725
2811
|
try {
|
|
2726
|
-
const singleResult = await this.
|
|
2727
|
-
...
|
|
2728
|
-
|
|
2729
|
-
source: undefined,
|
|
2730
|
-
sourcePath: sp,
|
|
2812
|
+
const singleResult = await this.validateMixinSingle({
|
|
2813
|
+
...sharedInput,
|
|
2814
|
+
sourcePath: entry.sourcePath,
|
|
2731
2815
|
warningMode: batchWarningMode
|
|
2732
2816
|
});
|
|
2733
|
-
results.push({
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
}
|
|
2737
|
-
else {
|
|
2738
|
-
invalidCount++;
|
|
2739
|
-
}
|
|
2817
|
+
results.push({
|
|
2818
|
+
source: entry.source,
|
|
2819
|
+
result: singleResult
|
|
2820
|
+
});
|
|
2740
2821
|
}
|
|
2741
2822
|
catch (err) {
|
|
2742
2823
|
results.push({
|
|
2743
|
-
|
|
2824
|
+
source: entry.source,
|
|
2744
2825
|
error: err instanceof Error ? err.message : String(err)
|
|
2745
2826
|
});
|
|
2746
|
-
errorCount++;
|
|
2747
2827
|
}
|
|
2748
2828
|
}
|
|
2749
|
-
|
|
2829
|
+
return this.buildValidateMixinOutput(mode, results);
|
|
2830
|
+
}
|
|
2831
|
+
buildValidateMixinOutput(mode, results) {
|
|
2832
|
+
let valid = 0;
|
|
2833
|
+
let invalid = 0;
|
|
2834
|
+
let processingErrors = 0;
|
|
2835
|
+
let totalValidationErrors = 0;
|
|
2836
|
+
let totalValidationWarnings = 0;
|
|
2837
|
+
const warningSet = new Set();
|
|
2750
2838
|
const issueGroupMap = new Map();
|
|
2751
|
-
for (const
|
|
2752
|
-
if (!
|
|
2839
|
+
for (const entry of results) {
|
|
2840
|
+
if (!entry.result) {
|
|
2841
|
+
processingErrors++;
|
|
2753
2842
|
continue;
|
|
2754
|
-
|
|
2843
|
+
}
|
|
2844
|
+
if (entry.result.valid) {
|
|
2845
|
+
valid++;
|
|
2846
|
+
}
|
|
2847
|
+
else {
|
|
2848
|
+
invalid++;
|
|
2849
|
+
}
|
|
2850
|
+
totalValidationErrors += entry.result.summary.errors;
|
|
2851
|
+
totalValidationWarnings += entry.result.summary.warnings;
|
|
2852
|
+
for (const warning of entry.result.warnings) {
|
|
2853
|
+
warningSet.add(warning);
|
|
2854
|
+
}
|
|
2855
|
+
for (const issue of entry.result.issues) {
|
|
2755
2856
|
const key = `${issue.kind}\0${issue.confidence ?? "unknown"}\0${issue.category ?? "validation"}`;
|
|
2756
2857
|
const existing = issueGroupMap.get(key);
|
|
2757
2858
|
if (existing) {
|
|
@@ -2771,39 +2872,26 @@ export class SourceService {
|
|
|
2771
2872
|
}
|
|
2772
2873
|
}
|
|
2773
2874
|
}
|
|
2774
|
-
const issueSummary = issueGroupMap.size > 0
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
let totalValidationWarnings = 0;
|
|
2780
|
-
for (const r of results) {
|
|
2781
|
-
if (r.result) {
|
|
2782
|
-
totalValidationErrors += r.result.summary.errors;
|
|
2783
|
-
totalValidationWarnings += r.result.summary.warnings;
|
|
2784
|
-
}
|
|
2785
|
-
}
|
|
2786
|
-
// Extract shared toolHealth from first result (all share same version/mapping)
|
|
2787
|
-
const sharedHealth = results.find((r) => r.result?.toolHealth)?.result?.toolHealth;
|
|
2788
|
-
// Batch confidenceScore = min of all individual scores
|
|
2789
|
-
const scores = results
|
|
2790
|
-
.map((r) => r.result?.confidenceScore)
|
|
2791
|
-
.filter((s) => s != null);
|
|
2792
|
-
const batchConfidenceScore = scores.length > 0 ? Math.min(...scores) : undefined;
|
|
2875
|
+
const issueSummary = issueGroupMap.size > 0 ? [...issueGroupMap.values()] : undefined;
|
|
2876
|
+
const toolHealth = results.find((entry) => entry.result?.toolHealth)?.result?.toolHealth;
|
|
2877
|
+
const confidenceScores = results
|
|
2878
|
+
.map((entry) => entry.result?.confidenceScore)
|
|
2879
|
+
.filter((score) => score != null);
|
|
2793
2880
|
return {
|
|
2881
|
+
mode,
|
|
2794
2882
|
results,
|
|
2795
2883
|
summary: {
|
|
2796
|
-
total:
|
|
2797
|
-
valid
|
|
2798
|
-
invalid
|
|
2799
|
-
|
|
2800
|
-
processingErrors: errorCount,
|
|
2884
|
+
total: results.length,
|
|
2885
|
+
valid,
|
|
2886
|
+
invalid,
|
|
2887
|
+
processingErrors,
|
|
2801
2888
|
totalValidationErrors,
|
|
2802
|
-
totalValidationWarnings
|
|
2803
|
-
confidenceScore: batchConfidenceScore
|
|
2889
|
+
totalValidationWarnings
|
|
2804
2890
|
},
|
|
2805
2891
|
issueSummary,
|
|
2806
|
-
toolHealth
|
|
2892
|
+
toolHealth,
|
|
2893
|
+
confidenceScore: confidenceScores.length > 0 ? Math.min(...confidenceScores) : undefined,
|
|
2894
|
+
warnings: [...warningSet]
|
|
2807
2895
|
};
|
|
2808
2896
|
}
|
|
2809
2897
|
async validateAccessWidener(input) {
|
|
@@ -2828,7 +2916,7 @@ export class SourceService {
|
|
|
2828
2916
|
if (overrideMapping && headerNamespace && overrideMapping !== headerNamespace) {
|
|
2829
2917
|
warnings.push(`Using mapping override "${overrideMapping}" instead of header namespace "${headerNamespaceRaw}".`);
|
|
2830
2918
|
}
|
|
2831
|
-
const needsMapping = awNamespace !== "
|
|
2919
|
+
const needsMapping = awNamespace !== "obfuscated";
|
|
2832
2920
|
// Collect unique class FQNs from entries
|
|
2833
2921
|
const classFqns = new Set();
|
|
2834
2922
|
for (const entry of parsed.entries) {
|
|
@@ -2837,7 +2925,7 @@ export class SourceService {
|
|
|
2837
2925
|
}
|
|
2838
2926
|
const membersByClass = new Map();
|
|
2839
2927
|
for (const fqn of classFqns) {
|
|
2840
|
-
let
|
|
2928
|
+
let obfuscatedFqn = fqn;
|
|
2841
2929
|
if (needsMapping) {
|
|
2842
2930
|
try {
|
|
2843
2931
|
const mapped = await this.mappingService.findMapping({
|
|
@@ -2845,14 +2933,14 @@ export class SourceService {
|
|
|
2845
2933
|
kind: "class",
|
|
2846
2934
|
name: fqn,
|
|
2847
2935
|
sourceMapping: awNamespace,
|
|
2848
|
-
targetMapping: "
|
|
2936
|
+
targetMapping: "obfuscated",
|
|
2849
2937
|
sourcePriority: input.sourcePriority
|
|
2850
2938
|
});
|
|
2851
2939
|
if (mapped.resolved && mapped.resolvedSymbol) {
|
|
2852
|
-
|
|
2940
|
+
obfuscatedFqn = mapped.resolvedSymbol.name;
|
|
2853
2941
|
}
|
|
2854
2942
|
else {
|
|
2855
|
-
warnings.push(`Could not map class "${fqn}" from ${awNamespace} to
|
|
2943
|
+
warnings.push(`Could not map class "${fqn}" from ${awNamespace} to obfuscated.`);
|
|
2856
2944
|
}
|
|
2857
2945
|
}
|
|
2858
2946
|
catch {
|
|
@@ -2861,7 +2949,7 @@ export class SourceService {
|
|
|
2861
2949
|
}
|
|
2862
2950
|
try {
|
|
2863
2951
|
const sig = await this.explorerService.getSignature({
|
|
2864
|
-
fqn:
|
|
2952
|
+
fqn: obfuscatedFqn,
|
|
2865
2953
|
jarPath,
|
|
2866
2954
|
access: "all"
|
|
2867
2955
|
});
|
|
@@ -2874,7 +2962,7 @@ export class SourceService {
|
|
|
2874
2962
|
});
|
|
2875
2963
|
}
|
|
2876
2964
|
catch {
|
|
2877
|
-
warnings.push(`Could not load signature for class "${
|
|
2965
|
+
warnings.push(`Could not load signature for class "${obfuscatedFqn}".`);
|
|
2878
2966
|
}
|
|
2879
2967
|
}
|
|
2880
2968
|
return validateParsedAccessWidener(parsed, membersByClass, warnings);
|
|
@@ -2915,7 +3003,7 @@ export class SourceService {
|
|
|
2915
3003
|
},
|
|
2916
3004
|
indexedAt: currentMeta.indexedAt,
|
|
2917
3005
|
durationMs: 0,
|
|
2918
|
-
mappingApplied: artifact.mappingApplied ?? "
|
|
3006
|
+
mappingApplied: artifact.mappingApplied ?? "obfuscated"
|
|
2919
3007
|
};
|
|
2920
3008
|
}
|
|
2921
3009
|
const resolved = this.toResolvedArtifact(artifact);
|
|
@@ -2932,33 +3020,16 @@ export class SourceService {
|
|
|
2932
3020
|
},
|
|
2933
3021
|
indexedAt: rebuilt.indexedAt,
|
|
2934
3022
|
durationMs: rebuilt.indexDurationMs,
|
|
2935
|
-
mappingApplied: artifact.mappingApplied ?? "
|
|
3023
|
+
mappingApplied: artifact.mappingApplied ?? "obfuscated"
|
|
2936
3024
|
};
|
|
2937
3025
|
}
|
|
2938
|
-
searchSymbolIntent(artifactId, query, match, scope,
|
|
3026
|
+
searchSymbolIntent(artifactId, query, match, scope, regexPattern, onHit) {
|
|
2939
3027
|
const matchedSymbols = this.findSymbolHits(artifactId, query, match, scope, regexPattern);
|
|
2940
|
-
const filePaths = [...new Set(matchedSymbols.map((item) => item.symbol.filePath))];
|
|
2941
|
-
const rows = this.filesRepo.getFileContentsByPaths(artifactId, filePaths);
|
|
2942
|
-
this.metrics.recordSearchDbRoundtrip();
|
|
2943
|
-
this.metrics.recordSearchRowsScanned(rows.length);
|
|
2944
|
-
const rowsByPath = new Map(rows.map((row) => [row.filePath, row]));
|
|
2945
3028
|
for (const item of matchedSymbols) {
|
|
2946
|
-
const row = rowsByPath.get(item.symbol.filePath);
|
|
2947
|
-
const snippet = row
|
|
2948
|
-
? toContextSnippet(row.content, item.symbol.line, snippetWindow.before, snippetWindow.after, true)
|
|
2949
|
-
: {
|
|
2950
|
-
startLine: item.symbol.line,
|
|
2951
|
-
endLine: item.symbol.line,
|
|
2952
|
-
snippet: "",
|
|
2953
|
-
truncated: false
|
|
2954
|
-
};
|
|
2955
3029
|
onHit({
|
|
2956
3030
|
filePath: item.symbol.filePath,
|
|
2957
3031
|
score: item.score,
|
|
2958
3032
|
matchedIn: "symbol",
|
|
2959
|
-
startLine: snippet.startLine,
|
|
2960
|
-
endLine: snippet.endLine,
|
|
2961
|
-
snippet: snippet.snippet,
|
|
2962
3033
|
reasonCodes: [`symbol_${match}`],
|
|
2963
3034
|
symbol: {
|
|
2964
3035
|
symbolKind: item.symbol.symbolKind,
|
|
@@ -2969,7 +3040,7 @@ export class SourceService {
|
|
|
2969
3040
|
});
|
|
2970
3041
|
}
|
|
2971
3042
|
}
|
|
2972
|
-
searchTextIntentIndexed(artifactId, query, match, scope,
|
|
3043
|
+
searchTextIntentIndexed(artifactId, query, match, scope, onHit) {
|
|
2973
3044
|
const candidateLimit = this.indexedCandidateLimitForMatch(match);
|
|
2974
3045
|
const indexed = this.filesRepo.searchFileCandidates(artifactId, {
|
|
2975
3046
|
query,
|
|
@@ -2998,45 +3069,21 @@ export class SourceService {
|
|
|
2998
3069
|
if (contentIndex < 0) {
|
|
2999
3070
|
continue;
|
|
3000
3071
|
}
|
|
3001
|
-
const line = indexToLine(candidate.content, contentIndex);
|
|
3002
3072
|
candidateRows.push({
|
|
3003
3073
|
filePath: candidate.filePath,
|
|
3004
|
-
content: candidate.content,
|
|
3005
|
-
line,
|
|
3006
3074
|
contentIndex
|
|
3007
3075
|
});
|
|
3008
3076
|
}
|
|
3009
|
-
const needSymbols = includeDefinition || !!scope?.symbolKind;
|
|
3010
|
-
const symbolsByFile = needSymbols
|
|
3011
|
-
? this.symbolsRepo.listSymbolsForFiles(artifactId, candidateRows.map((candidate) => candidate.filePath), scope?.symbolKind)
|
|
3012
|
-
: new Map();
|
|
3013
|
-
if (needSymbols) {
|
|
3014
|
-
this.metrics.recordSearchDbRoundtrip();
|
|
3015
|
-
this.metrics.recordSearchRowsScanned([...symbolsByFile.values()].reduce((acc, symbols) => acc + symbols.length, 0));
|
|
3016
|
-
}
|
|
3017
3077
|
for (const candidate of candidateRows) {
|
|
3018
|
-
// When symbolKind filter is set, skip files that have no symbols of that kind
|
|
3019
|
-
if (scope?.symbolKind && !symbolsByFile.has(candidate.filePath)) {
|
|
3020
|
-
continue;
|
|
3021
|
-
}
|
|
3022
|
-
const snippet = toContextSnippet(candidate.content, candidate.line, snippetWindow.before, snippetWindow.after, true);
|
|
3023
|
-
const definition = includeDefinition
|
|
3024
|
-
? this.findNearestSymbolFromList(symbolsByFile.get(candidate.filePath) ?? [], candidate.line)
|
|
3025
|
-
: undefined;
|
|
3026
|
-
const resolvedSymbol = definition ? lineToSymbol(definition) : undefined;
|
|
3027
3078
|
onHit({
|
|
3028
3079
|
filePath: candidate.filePath,
|
|
3029
|
-
score: scoreTextMatch(match, candidate.contentIndex)
|
|
3080
|
+
score: scoreTextMatch(match, candidate.contentIndex),
|
|
3030
3081
|
matchedIn: "content",
|
|
3031
|
-
|
|
3032
|
-
endLine: snippet.endLine,
|
|
3033
|
-
snippet: snippet.snippet,
|
|
3034
|
-
reasonCodes: ["content_match", `text_${match}`, "indexed"],
|
|
3035
|
-
symbol: resolvedSymbol
|
|
3082
|
+
reasonCodes: ["content_match", `text_${match}`, "indexed"]
|
|
3036
3083
|
});
|
|
3037
3084
|
}
|
|
3038
3085
|
}
|
|
3039
|
-
searchPathIntentIndexed(artifactId, query, match, scope,
|
|
3086
|
+
searchPathIntentIndexed(artifactId, query, match, scope, onHit) {
|
|
3040
3087
|
const candidateLimit = this.indexedCandidateLimitForMatch(match);
|
|
3041
3088
|
const indexed = this.filesRepo.searchFileCandidates(artifactId, {
|
|
3042
3089
|
query,
|
|
@@ -3071,59 +3118,22 @@ export class SourceService {
|
|
|
3071
3118
|
pathIndex
|
|
3072
3119
|
});
|
|
3073
3120
|
}
|
|
3074
|
-
const candidateContentRows = this.filesRepo.getFileContentsByPaths(artifactId, candidateRows.map((candidate) => candidate.filePath));
|
|
3075
|
-
this.metrics.recordSearchDbRoundtrip();
|
|
3076
|
-
this.metrics.recordSearchRowsScanned(candidateContentRows.length);
|
|
3077
|
-
const contentByPath = new Map(candidateContentRows.map((row) => [row.filePath, row.content]));
|
|
3078
|
-
const needSymbols = includeDefinition || !!scope?.symbolKind;
|
|
3079
|
-
const symbolsByFile = needSymbols
|
|
3080
|
-
? this.symbolsRepo.listSymbolsForFiles(artifactId, candidateRows.map((candidate) => candidate.filePath), scope?.symbolKind)
|
|
3081
|
-
: new Map();
|
|
3082
|
-
if (needSymbols) {
|
|
3083
|
-
this.metrics.recordSearchDbRoundtrip();
|
|
3084
|
-
this.metrics.recordSearchRowsScanned([...symbolsByFile.values()].reduce((acc, symbols) => acc + symbols.length, 0));
|
|
3085
|
-
}
|
|
3086
3121
|
for (const candidate of candidateRows) {
|
|
3087
|
-
const content = contentByPath.get(candidate.filePath);
|
|
3088
|
-
if (!content) {
|
|
3089
|
-
continue;
|
|
3090
|
-
}
|
|
3091
|
-
// When symbolKind filter is set, skip files that have no symbols of that kind
|
|
3092
|
-
if (scope?.symbolKind && !symbolsByFile.has(candidate.filePath)) {
|
|
3093
|
-
continue;
|
|
3094
|
-
}
|
|
3095
|
-
const definition = includeDefinition
|
|
3096
|
-
? this.findNearestSymbolFromList(symbolsByFile.get(candidate.filePath) ?? [], 1)
|
|
3097
|
-
: undefined;
|
|
3098
|
-
const centerLine = definition?.line ?? 1;
|
|
3099
|
-
const snippet = toContextSnippet(content, centerLine, snippetWindow.before, snippetWindow.after, true);
|
|
3100
|
-
const resolvedSymbol = definition ? lineToSymbol(definition) : undefined;
|
|
3101
3122
|
onHit({
|
|
3102
3123
|
filePath: candidate.filePath,
|
|
3103
|
-
score: scorePathMatch(match, candidate.pathIndex)
|
|
3124
|
+
score: scorePathMatch(match, candidate.pathIndex),
|
|
3104
3125
|
matchedIn: "path",
|
|
3105
|
-
|
|
3106
|
-
endLine: snippet.endLine,
|
|
3107
|
-
snippet: snippet.snippet,
|
|
3108
|
-
reasonCodes: ["path_match", `path_${match}`, "indexed"],
|
|
3109
|
-
symbol: resolvedSymbol
|
|
3126
|
+
reasonCodes: ["path_match", `path_${match}`, "indexed"]
|
|
3110
3127
|
});
|
|
3111
3128
|
}
|
|
3112
3129
|
}
|
|
3113
|
-
searchTextIntent(artifactId, query, match, scope,
|
|
3130
|
+
searchTextIntent(artifactId, query, match, scope, regexPattern, onHit) {
|
|
3114
3131
|
const filePaths = this.loadScopedFilePaths(artifactId, scope);
|
|
3115
3132
|
const pageSize = Math.max(1, this.config.searchScanPageSize ?? 250);
|
|
3116
3133
|
for (const chunk of chunkArray(filePaths, pageSize)) {
|
|
3117
3134
|
const rows = this.filesRepo.getFileContentsByPaths(artifactId, chunk);
|
|
3118
3135
|
this.metrics.recordSearchDbRoundtrip();
|
|
3119
3136
|
this.metrics.recordSearchRowsScanned(rows.length);
|
|
3120
|
-
const symbolsByFile = includeDefinition
|
|
3121
|
-
? this.symbolsRepo.listSymbolsForFiles(artifactId, rows.map((row) => row.filePath), scope?.symbolKind)
|
|
3122
|
-
: new Map();
|
|
3123
|
-
if (includeDefinition) {
|
|
3124
|
-
this.metrics.recordSearchDbRoundtrip();
|
|
3125
|
-
this.metrics.recordSearchRowsScanned([...symbolsByFile.values()].reduce((acc, symbols) => acc + symbols.length, 0));
|
|
3126
|
-
}
|
|
3127
3137
|
for (const row of rows) {
|
|
3128
3138
|
const contentIndex = match === "regex"
|
|
3129
3139
|
? matchRegexIndex(row.content, regexPattern)
|
|
@@ -3131,26 +3141,16 @@ export class SourceService {
|
|
|
3131
3141
|
if (contentIndex < 0) {
|
|
3132
3142
|
continue;
|
|
3133
3143
|
}
|
|
3134
|
-
const line = indexToLine(row.content, contentIndex);
|
|
3135
|
-
const snippet = toContextSnippet(row.content, line, snippetWindow.before, snippetWindow.after, true);
|
|
3136
|
-
const definition = includeDefinition
|
|
3137
|
-
? this.findNearestSymbolFromList(symbolsByFile.get(row.filePath) ?? [], line)
|
|
3138
|
-
: undefined;
|
|
3139
|
-
const resolvedSymbol = definition ? lineToSymbol(definition) : undefined;
|
|
3140
3144
|
onHit({
|
|
3141
3145
|
filePath: row.filePath,
|
|
3142
|
-
score: scoreTextMatch(match, contentIndex)
|
|
3146
|
+
score: scoreTextMatch(match, contentIndex),
|
|
3143
3147
|
matchedIn: "content",
|
|
3144
|
-
|
|
3145
|
-
endLine: snippet.endLine,
|
|
3146
|
-
snippet: snippet.snippet,
|
|
3147
|
-
reasonCodes: ["content_match", `text_${match}`],
|
|
3148
|
-
symbol: resolvedSymbol
|
|
3148
|
+
reasonCodes: ["content_match", `text_${match}`]
|
|
3149
3149
|
});
|
|
3150
3150
|
}
|
|
3151
3151
|
}
|
|
3152
3152
|
}
|
|
3153
|
-
searchPathIntent(artifactId, query, match, scope,
|
|
3153
|
+
searchPathIntent(artifactId, query, match, scope, regexPattern, onHit) {
|
|
3154
3154
|
const filePaths = this.loadScopedFilePaths(artifactId, scope);
|
|
3155
3155
|
const matching = filePaths.flatMap((filePath) => {
|
|
3156
3156
|
const pathIndex = match === "regex"
|
|
@@ -3163,37 +3163,12 @@ export class SourceService {
|
|
|
3163
3163
|
});
|
|
3164
3164
|
const pageSize = Math.max(1, this.config.searchScanPageSize ?? 250);
|
|
3165
3165
|
for (const chunk of chunkArray(matching, pageSize)) {
|
|
3166
|
-
const rows = this.filesRepo.getFileContentsByPaths(artifactId, chunk.map((item) => item.filePath));
|
|
3167
|
-
this.metrics.recordSearchDbRoundtrip();
|
|
3168
|
-
this.metrics.recordSearchRowsScanned(rows.length);
|
|
3169
|
-
const contentByPath = new Map(rows.map((row) => [row.filePath, row.content]));
|
|
3170
|
-
const symbolsByFile = includeDefinition
|
|
3171
|
-
? this.symbolsRepo.listSymbolsForFiles(artifactId, chunk.map((item) => item.filePath), scope?.symbolKind)
|
|
3172
|
-
: new Map();
|
|
3173
|
-
if (includeDefinition) {
|
|
3174
|
-
this.metrics.recordSearchDbRoundtrip();
|
|
3175
|
-
this.metrics.recordSearchRowsScanned([...symbolsByFile.values()].reduce((acc, symbols) => acc + symbols.length, 0));
|
|
3176
|
-
}
|
|
3177
3166
|
for (const candidate of chunk) {
|
|
3178
|
-
const content = contentByPath.get(candidate.filePath);
|
|
3179
|
-
if (!content) {
|
|
3180
|
-
continue;
|
|
3181
|
-
}
|
|
3182
|
-
const definition = includeDefinition
|
|
3183
|
-
? this.findNearestSymbolFromList(symbolsByFile.get(candidate.filePath) ?? [], 1)
|
|
3184
|
-
: undefined;
|
|
3185
|
-
const centerLine = definition?.line ?? 1;
|
|
3186
|
-
const snippet = toContextSnippet(content, centerLine, snippetWindow.before, snippetWindow.after, true);
|
|
3187
|
-
const resolvedSymbol = definition ? lineToSymbol(definition) : undefined;
|
|
3188
3167
|
onHit({
|
|
3189
3168
|
filePath: candidate.filePath,
|
|
3190
|
-
score: scorePathMatch(match, candidate.pathIndex)
|
|
3169
|
+
score: scorePathMatch(match, candidate.pathIndex),
|
|
3191
3170
|
matchedIn: "path",
|
|
3192
|
-
|
|
3193
|
-
endLine: snippet.endLine,
|
|
3194
|
-
snippet: snippet.snippet,
|
|
3195
|
-
reasonCodes: ["path_match", `path_${match}`],
|
|
3196
|
-
symbol: resolvedSymbol
|
|
3171
|
+
reasonCodes: ["path_match", `path_${match}`]
|
|
3197
3172
|
});
|
|
3198
3173
|
}
|
|
3199
3174
|
}
|
|
@@ -3262,13 +3237,6 @@ export class SourceService {
|
|
|
3262
3237
|
}
|
|
3263
3238
|
loadScopedFilePaths(artifactId, scope) {
|
|
3264
3239
|
const glob = scope?.fileGlob ? buildGlobRegex(normalizePathStyle(scope.fileGlob)) : undefined;
|
|
3265
|
-
const scopedFilesBySymbolKind = scope?.symbolKind
|
|
3266
|
-
? new Set(this.symbolsRepo.listDistinctFilePathsByKind(artifactId, scope.symbolKind))
|
|
3267
|
-
: undefined;
|
|
3268
|
-
if (scopedFilesBySymbolKind) {
|
|
3269
|
-
this.metrics.recordSearchDbRoundtrip();
|
|
3270
|
-
this.metrics.recordSearchRowsScanned(scopedFilesBySymbolKind.size);
|
|
3271
|
-
}
|
|
3272
3240
|
const result = [];
|
|
3273
3241
|
let cursor = undefined;
|
|
3274
3242
|
const pageSize = Math.max(1, this.config.searchScanPageSize ?? 250);
|
|
@@ -3283,9 +3251,6 @@ export class SourceService {
|
|
|
3283
3251
|
if (glob && !glob.test(filePath)) {
|
|
3284
3252
|
continue;
|
|
3285
3253
|
}
|
|
3286
|
-
if (scopedFilesBySymbolKind && !scopedFilesBySymbolKind.has(filePath)) {
|
|
3287
|
-
continue;
|
|
3288
|
-
}
|
|
3289
3254
|
result.push(filePath);
|
|
3290
3255
|
}
|
|
3291
3256
|
if (!page.nextCursor) {
|
|
@@ -3365,141 +3330,27 @@ export class SourceService {
|
|
|
3365
3330
|
}
|
|
3366
3331
|
return undefined;
|
|
3367
3332
|
}
|
|
3368
|
-
|
|
3369
|
-
const
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
return this.findNearestSymbolFromList(symbols, line);
|
|
3373
|
-
}
|
|
3374
|
-
findNearestSymbolFromList(symbols, line) {
|
|
3375
|
-
let best;
|
|
3376
|
-
for (const symbol of symbols) {
|
|
3377
|
-
if (symbol.line > line) {
|
|
3378
|
-
continue;
|
|
3379
|
-
}
|
|
3380
|
-
if (!best || symbol.line >= best.line) {
|
|
3381
|
-
best = symbol;
|
|
3382
|
-
}
|
|
3383
|
-
}
|
|
3384
|
-
return best ?? symbols[0];
|
|
3385
|
-
}
|
|
3386
|
-
buildOneHopRelations(artifactId, roots, maxRelations) {
|
|
3387
|
-
if (roots.length === 0 || maxRelations <= 0) {
|
|
3388
|
-
return [];
|
|
3333
|
+
async resolveBinaryFallbackArtifact(input) {
|
|
3334
|
+
const binaryJarPath = normalizeOptionalString(input.binaryJarPath);
|
|
3335
|
+
if (!binaryJarPath) {
|
|
3336
|
+
return undefined;
|
|
3389
3337
|
}
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
};
|
|
3403
|
-
}
|
|
3404
|
-
const aroundRoot = toContextSnippet(contentRow.content, root.line, 2, 3, false).snippet;
|
|
3405
|
-
return {
|
|
3406
|
-
root,
|
|
3407
|
-
calls: Array.from(aroundRoot.matchAll(/\b([A-Za-z_$][\w$]*)\s*\(/g))
|
|
3408
|
-
.map((match) => match[1])
|
|
3409
|
-
.filter((token) => Boolean(token)),
|
|
3410
|
-
types: Array.from(aroundRoot.matchAll(/\b([A-Z][A-Za-z0-9_$]*)\b/g))
|
|
3411
|
-
.map((match) => match[1])
|
|
3412
|
-
.filter((token) => Boolean(token)),
|
|
3413
|
-
imports: Array.from(aroundRoot.matchAll(/import\s+([\w.$]+);/g))
|
|
3414
|
-
.map((match) => match[1]?.split(".").at(-1))
|
|
3415
|
-
.filter((token) => Boolean(token))
|
|
3416
|
-
};
|
|
3417
|
-
});
|
|
3418
|
-
const tokenSet = new Set();
|
|
3419
|
-
for (const entry of rootTokens) {
|
|
3420
|
-
for (const token of entry.calls) {
|
|
3421
|
-
tokenSet.add(toLower(token));
|
|
3422
|
-
}
|
|
3423
|
-
for (const token of entry.types) {
|
|
3424
|
-
tokenSet.add(toLower(token));
|
|
3425
|
-
}
|
|
3426
|
-
for (const token of entry.imports) {
|
|
3427
|
-
tokenSet.add(toLower(token));
|
|
3428
|
-
}
|
|
3338
|
+
try {
|
|
3339
|
+
const fallbackResolved = await resolveSourceTargetInternal({ kind: "jar", value: binaryJarPath }, { allowDecompile: true, preferBinaryOnly: true }, this.config);
|
|
3340
|
+
fallbackResolved.version = fallbackResolved.version ?? input.version;
|
|
3341
|
+
fallbackResolved.coordinate = fallbackResolved.coordinate ?? input.coordinate;
|
|
3342
|
+
fallbackResolved.requestedMapping = input.requestedMapping;
|
|
3343
|
+
fallbackResolved.mappingApplied = input.mappingApplied;
|
|
3344
|
+
fallbackResolved.provenance = input.provenance;
|
|
3345
|
+
fallbackResolved.qualityFlags = [
|
|
3346
|
+
...new Set([...(fallbackResolved.qualityFlags ?? []), ...input.qualityFlags, "binary-fallback"])
|
|
3347
|
+
];
|
|
3348
|
+
await this.ingestIfNeeded(fallbackResolved);
|
|
3349
|
+
return fallbackResolved;
|
|
3429
3350
|
}
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
.filter((symbol) => isSymbolKind(symbol.symbolKind));
|
|
3433
|
-
this.metrics.recordSearchDbRoundtrip();
|
|
3434
|
-
this.metrics.recordSearchRowsScanned(matchedSymbols.length);
|
|
3435
|
-
const symbolMap = new Map();
|
|
3436
|
-
for (const symbol of matchedSymbols) {
|
|
3437
|
-
const key = toLower(symbol.symbolName);
|
|
3438
|
-
const bucket = symbolMap.get(key) ?? [];
|
|
3439
|
-
bucket.push(symbol);
|
|
3440
|
-
symbolMap.set(key, bucket);
|
|
3441
|
-
}
|
|
3442
|
-
const dedupe = new Set();
|
|
3443
|
-
const relations = [];
|
|
3444
|
-
for (const entry of rootTokens) {
|
|
3445
|
-
const root = entry.root;
|
|
3446
|
-
const attach = (token, relationKind) => {
|
|
3447
|
-
const matches = symbolMap.get(toLower(token)) ?? [];
|
|
3448
|
-
for (const target of matches) {
|
|
3449
|
-
if (!isSymbolKind(target.symbolKind)) {
|
|
3450
|
-
continue;
|
|
3451
|
-
}
|
|
3452
|
-
if (target.filePath === root.filePath &&
|
|
3453
|
-
target.line === root.line &&
|
|
3454
|
-
target.symbolName === root.symbolName &&
|
|
3455
|
-
target.symbolKind === root.symbolKind) {
|
|
3456
|
-
continue;
|
|
3457
|
-
}
|
|
3458
|
-
const key = `${root.symbolKind}:${root.symbolName}:${root.filePath}:${root.line}->${target.symbolKind}:${target.symbolName}:${target.filePath}:${target.line}:${relationKind}`;
|
|
3459
|
-
if (dedupe.has(key)) {
|
|
3460
|
-
continue;
|
|
3461
|
-
}
|
|
3462
|
-
dedupe.add(key);
|
|
3463
|
-
relations.push({
|
|
3464
|
-
fromSymbol: {
|
|
3465
|
-
symbolKind: root.symbolKind,
|
|
3466
|
-
symbolName: root.symbolName,
|
|
3467
|
-
filePath: root.filePath,
|
|
3468
|
-
line: root.line
|
|
3469
|
-
},
|
|
3470
|
-
toSymbol: {
|
|
3471
|
-
symbolKind: target.symbolKind,
|
|
3472
|
-
symbolName: target.symbolName,
|
|
3473
|
-
filePath: target.filePath,
|
|
3474
|
-
line: target.line
|
|
3475
|
-
},
|
|
3476
|
-
relation: relationKind
|
|
3477
|
-
});
|
|
3478
|
-
if (relations.length >= maxRelations) {
|
|
3479
|
-
return;
|
|
3480
|
-
}
|
|
3481
|
-
}
|
|
3482
|
-
};
|
|
3483
|
-
for (const token of entry.calls) {
|
|
3484
|
-
attach(token, "calls");
|
|
3485
|
-
if (relations.length >= maxRelations) {
|
|
3486
|
-
return relations;
|
|
3487
|
-
}
|
|
3488
|
-
}
|
|
3489
|
-
for (const token of entry.types) {
|
|
3490
|
-
attach(token, "uses-type");
|
|
3491
|
-
if (relations.length >= maxRelations) {
|
|
3492
|
-
return relations;
|
|
3493
|
-
}
|
|
3494
|
-
}
|
|
3495
|
-
for (const token of entry.imports) {
|
|
3496
|
-
attach(token, "imports");
|
|
3497
|
-
if (relations.length >= maxRelations) {
|
|
3498
|
-
return relations;
|
|
3499
|
-
}
|
|
3500
|
-
}
|
|
3351
|
+
catch {
|
|
3352
|
+
return undefined;
|
|
3501
3353
|
}
|
|
3502
|
-
return relations;
|
|
3503
3354
|
}
|
|
3504
3355
|
buildProvenance(input) {
|
|
3505
3356
|
const provenance = {
|
|
@@ -3551,31 +3402,107 @@ export class SourceService {
|
|
|
3551
3402
|
transformChain
|
|
3552
3403
|
};
|
|
3553
3404
|
}
|
|
3554
|
-
async
|
|
3555
|
-
if (
|
|
3556
|
-
return className;
|
|
3405
|
+
async resolveClassNameForLookup(input) {
|
|
3406
|
+
if (input.sourceMapping === input.targetMapping) {
|
|
3407
|
+
return input.className;
|
|
3408
|
+
}
|
|
3409
|
+
if (!input.version) {
|
|
3410
|
+
input.warnings.push(`Could not map class "${input.className}" from ${input.sourceMapping} to ${input.targetMapping} for ${input.context} because version is unavailable.`);
|
|
3411
|
+
return input.className;
|
|
3557
3412
|
}
|
|
3558
3413
|
try {
|
|
3559
3414
|
const mapped = await this.mappingService.findMapping({
|
|
3560
|
-
version,
|
|
3415
|
+
version: input.version,
|
|
3561
3416
|
kind: "class",
|
|
3562
|
-
name: className,
|
|
3563
|
-
sourceMapping:
|
|
3564
|
-
targetMapping:
|
|
3565
|
-
sourcePriority
|
|
3417
|
+
name: input.className,
|
|
3418
|
+
sourceMapping: input.sourceMapping,
|
|
3419
|
+
targetMapping: input.targetMapping,
|
|
3420
|
+
sourcePriority: input.sourcePriority
|
|
3566
3421
|
});
|
|
3567
3422
|
if (mapped.resolved && mapped.resolvedSymbol) {
|
|
3568
3423
|
return mapped.resolvedSymbol.name;
|
|
3569
3424
|
}
|
|
3570
|
-
warnings.push(`Could not map class "${className}" from ${
|
|
3425
|
+
input.warnings.push(`Could not map class "${input.className}" from ${input.sourceMapping} to ${input.targetMapping} for ${input.context}.`);
|
|
3571
3426
|
}
|
|
3572
3427
|
catch {
|
|
3573
|
-
warnings.push(`Mapping lookup failed for class "${className}".`);
|
|
3428
|
+
input.warnings.push(`Mapping lookup failed for class "${input.className}" while preparing ${input.context} in ${input.targetMapping}.`);
|
|
3429
|
+
}
|
|
3430
|
+
return input.className;
|
|
3431
|
+
}
|
|
3432
|
+
buildClassSourceNotFoundError(input) {
|
|
3433
|
+
const simpleName = input.className.split(/[.$]/).at(-1) ?? input.className;
|
|
3434
|
+
const details = {
|
|
3435
|
+
artifactId: input.artifactId,
|
|
3436
|
+
className: input.className,
|
|
3437
|
+
mapping: input.mappingApplied,
|
|
3438
|
+
qualityFlags: input.qualityFlags,
|
|
3439
|
+
...(input.lookupClassName !== input.className ? { lookupClassName: input.lookupClassName } : {}),
|
|
3440
|
+
...(input.filePath ? { filePath: input.filePath } : {}),
|
|
3441
|
+
...(input.scope ? { scope: input.scope } : {}),
|
|
3442
|
+
...(input.targetKind ? { targetKind: input.targetKind } : {}),
|
|
3443
|
+
...(input.targetValue ? { targetValue: input.targetValue } : {}),
|
|
3444
|
+
...(input.attemptedBinaryFallback ? { binaryFallbackAttempted: true } : {})
|
|
3445
|
+
};
|
|
3446
|
+
let nextAction = `Use find-class to resolve the correct fully-qualified name for "${simpleName}".`;
|
|
3447
|
+
let suggestedCall = {
|
|
3448
|
+
tool: "find-class",
|
|
3449
|
+
params: { className: simpleName, artifactId: input.artifactId }
|
|
3450
|
+
};
|
|
3451
|
+
if (input.targetKind === "version" && input.scope && input.scope !== "merged" && !input.projectPath) {
|
|
3452
|
+
nextAction +=
|
|
3453
|
+
` If the class exists in a modded environment, retry with scope: "merged" and projectPath pointing to your mod project.`;
|
|
3454
|
+
}
|
|
3455
|
+
else if (input.targetKind === "version" && input.scope && input.scope !== "merged" && input.projectPath) {
|
|
3456
|
+
nextAction += ` The class may exist in merged sources; retry with scope: "merged".`;
|
|
3457
|
+
}
|
|
3458
|
+
if (hasPartialNetMinecraftCoverage(input.qualityFlags)) {
|
|
3459
|
+
nextAction =
|
|
3460
|
+
`Resolved source coverage does not include net.minecraft for "${input.className}",` +
|
|
3461
|
+
(input.attemptedBinaryFallback
|
|
3462
|
+
? " and binary fallback did not produce source for that class."
|
|
3463
|
+
: " and a binary fallback has not produced source for that class.") +
|
|
3464
|
+
" Use get-class-api-matrix or find-mapping instead of find-class for vanilla API discovery.";
|
|
3465
|
+
if (input.version) {
|
|
3466
|
+
suggestedCall = {
|
|
3467
|
+
tool: "get-class-api-matrix",
|
|
3468
|
+
params: {
|
|
3469
|
+
version: input.version,
|
|
3470
|
+
className: input.className,
|
|
3471
|
+
classNameMapping: input.requestedMapping
|
|
3472
|
+
}
|
|
3473
|
+
};
|
|
3474
|
+
}
|
|
3475
|
+
else {
|
|
3476
|
+
suggestedCall = {
|
|
3477
|
+
tool: "find-class",
|
|
3478
|
+
params: { className: simpleName, artifactId: input.artifactId }
|
|
3479
|
+
};
|
|
3480
|
+
}
|
|
3481
|
+
}
|
|
3482
|
+
if (input.mappingApplied === "obfuscated" && looksLikeDeobfuscatedClassName(input.className)) {
|
|
3483
|
+
nextAction += ` ${obfuscatedNamespaceHint(input.className)}`;
|
|
3574
3484
|
}
|
|
3575
|
-
|
|
3485
|
+
details.nextAction = nextAction;
|
|
3486
|
+
details.suggestedCall = suggestedCall;
|
|
3487
|
+
return createError({
|
|
3488
|
+
code: ERROR_CODES.CLASS_NOT_FOUND,
|
|
3489
|
+
message: `Source for class "${input.className}" was not found.`,
|
|
3490
|
+
details
|
|
3491
|
+
});
|
|
3492
|
+
}
|
|
3493
|
+
async resolveToObfuscatedClassName(className, version, mapping, sourcePriority, warnings) {
|
|
3494
|
+
return this.resolveClassNameForLookup({
|
|
3495
|
+
className,
|
|
3496
|
+
version,
|
|
3497
|
+
sourceMapping: mapping,
|
|
3498
|
+
targetMapping: "obfuscated",
|
|
3499
|
+
sourcePriority,
|
|
3500
|
+
warnings,
|
|
3501
|
+
context: "bytecode lookup"
|
|
3502
|
+
});
|
|
3576
3503
|
}
|
|
3577
|
-
async
|
|
3578
|
-
if (mapping === "
|
|
3504
|
+
async resolveToObfuscatedMemberName(name, ownerInSourceMapping, descriptor, kind, version, mapping, sourcePriority, warnings) {
|
|
3505
|
+
if (mapping === "obfuscated") {
|
|
3579
3506
|
return {
|
|
3580
3507
|
name,
|
|
3581
3508
|
descriptor: kind === "method" ? descriptor : undefined
|
|
@@ -3589,7 +3516,7 @@ export class SourceService {
|
|
|
3589
3516
|
owner: ownerInSourceMapping,
|
|
3590
3517
|
descriptor,
|
|
3591
3518
|
sourceMapping: mapping,
|
|
3592
|
-
targetMapping: "
|
|
3519
|
+
targetMapping: "obfuscated",
|
|
3593
3520
|
sourcePriority
|
|
3594
3521
|
});
|
|
3595
3522
|
if (mapped.resolved && mapped.resolvedSymbol) {
|
|
@@ -3598,7 +3525,7 @@ export class SourceService {
|
|
|
3598
3525
|
descriptor: kind === "method" ? mapped.resolvedSymbol.descriptor ?? descriptor : undefined
|
|
3599
3526
|
};
|
|
3600
3527
|
}
|
|
3601
|
-
warnings.push(`Could not map ${kind} "${name}" from ${mapping} to
|
|
3528
|
+
warnings.push(`Could not map ${kind} "${name}" from ${mapping} to obfuscated.`);
|
|
3602
3529
|
}
|
|
3603
3530
|
catch {
|
|
3604
3531
|
warnings.push(`Mapping lookup failed for ${kind} "${name}".`);
|
|
@@ -3608,9 +3535,9 @@ export class SourceService {
|
|
|
3608
3535
|
descriptor: kind === "method" ? descriptor : undefined
|
|
3609
3536
|
};
|
|
3610
3537
|
}
|
|
3611
|
-
async remapSignatureMembers(members, kind, version,
|
|
3538
|
+
async remapSignatureMembers(members, kind, version, sourceMapping, targetMapping, sourcePriority, warnings) {
|
|
3612
3539
|
const failedNames = new Set();
|
|
3613
|
-
if (
|
|
3540
|
+
if (sourceMapping === targetMapping) {
|
|
3614
3541
|
return { members, failedNames };
|
|
3615
3542
|
}
|
|
3616
3543
|
// Build deduplicated lookup tables for member names and owner FQNs
|
|
@@ -3619,35 +3546,35 @@ export class SourceService {
|
|
|
3619
3546
|
for (const member of members) {
|
|
3620
3547
|
const memberKey = `${member.ownerFqn}\0${member.name}\0${member.jvmDescriptor}`;
|
|
3621
3548
|
if (!memberKeyToRemapped.has(memberKey)) {
|
|
3622
|
-
memberKeyToRemapped.set(memberKey, member.name); // default =
|
|
3549
|
+
memberKeyToRemapped.set(memberKey, member.name); // default = obfuscated name
|
|
3623
3550
|
}
|
|
3624
3551
|
if (!ownerToRemapped.has(member.ownerFqn)) {
|
|
3625
|
-
ownerToRemapped.set(member.ownerFqn, member.ownerFqn); // default =
|
|
3552
|
+
ownerToRemapped.set(member.ownerFqn, member.ownerFqn); // default = obfuscated FQN
|
|
3626
3553
|
}
|
|
3627
3554
|
}
|
|
3628
3555
|
// Phase 1: Remap owner FQNs first (needed for member disambiguation)
|
|
3629
3556
|
const ownerEntries = [...ownerToRemapped.entries()];
|
|
3630
|
-
await Promise.all(ownerEntries.map(async ([
|
|
3557
|
+
await Promise.all(ownerEntries.map(async ([obfuscatedFqn]) => {
|
|
3631
3558
|
try {
|
|
3632
3559
|
const mapped = await this.mappingService.findMapping({
|
|
3633
3560
|
version,
|
|
3634
3561
|
kind: "class",
|
|
3635
|
-
name:
|
|
3636
|
-
sourceMapping
|
|
3637
|
-
targetMapping
|
|
3562
|
+
name: obfuscatedFqn,
|
|
3563
|
+
sourceMapping,
|
|
3564
|
+
targetMapping,
|
|
3638
3565
|
sourcePriority
|
|
3639
3566
|
});
|
|
3640
3567
|
if (mapped.resolved && mapped.resolvedSymbol) {
|
|
3641
|
-
ownerToRemapped.set(
|
|
3568
|
+
ownerToRemapped.set(obfuscatedFqn, mapped.resolvedSymbol.name);
|
|
3642
3569
|
}
|
|
3643
3570
|
}
|
|
3644
3571
|
catch {
|
|
3645
|
-
// keep
|
|
3572
|
+
// keep obfuscated FQN as fallback
|
|
3646
3573
|
}
|
|
3647
3574
|
}));
|
|
3648
3575
|
// Phase 2: Remap member names using resolved owners for disambiguation
|
|
3649
3576
|
const memberEntries = [...memberKeyToRemapped.entries()];
|
|
3650
|
-
await Promise.all(memberEntries.map(async ([key,
|
|
3577
|
+
await Promise.all(memberEntries.map(async ([key, _obfuscatedName]) => {
|
|
3651
3578
|
const [ownerFqn, name, descriptor] = key.split("\0");
|
|
3652
3579
|
try {
|
|
3653
3580
|
const targetOwner = ownerToRemapped.get(ownerFqn) ?? ownerFqn;
|
|
@@ -3657,8 +3584,8 @@ export class SourceService {
|
|
|
3657
3584
|
name,
|
|
3658
3585
|
owner: ownerFqn,
|
|
3659
3586
|
descriptor: kind === "method" ? descriptor : undefined,
|
|
3660
|
-
sourceMapping
|
|
3661
|
-
targetMapping
|
|
3587
|
+
sourceMapping,
|
|
3588
|
+
targetMapping,
|
|
3662
3589
|
sourcePriority,
|
|
3663
3590
|
disambiguation: { ownerHint: targetOwner }
|
|
3664
3591
|
});
|
|
@@ -3677,17 +3604,17 @@ export class SourceService {
|
|
|
3677
3604
|
}
|
|
3678
3605
|
}
|
|
3679
3606
|
else {
|
|
3680
|
-
warnings.push(`Could not remap ${kind} "${name}" to ${
|
|
3607
|
+
warnings.push(`Could not remap ${kind} "${name}" from ${sourceMapping} to ${targetMapping}.`);
|
|
3681
3608
|
failedNames.add(name);
|
|
3682
3609
|
}
|
|
3683
3610
|
}
|
|
3684
3611
|
else {
|
|
3685
|
-
warnings.push(`Could not remap ${kind} "${name}" to ${
|
|
3612
|
+
warnings.push(`Could not remap ${kind} "${name}" from ${sourceMapping} to ${targetMapping}.`);
|
|
3686
3613
|
failedNames.add(name);
|
|
3687
3614
|
}
|
|
3688
3615
|
}
|
|
3689
3616
|
catch {
|
|
3690
|
-
warnings.push(`Remap failed for ${kind} "${name}".`);
|
|
3617
|
+
warnings.push(`Remap failed for ${kind} "${name}" from ${sourceMapping} to ${targetMapping}.`);
|
|
3691
3618
|
failedNames.add(name);
|
|
3692
3619
|
}
|
|
3693
3620
|
}));
|