@adhisang/minecraft-modding-mcp 5.0.0 → 6.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 +36 -0
- package/dist/access-widener-parser.d.ts +1 -0
- package/dist/access-widener-parser.js +6 -3
- package/dist/cache-registry.js +35 -31
- package/dist/decompiler/vineflower.js +8 -2
- package/dist/entry-tools/analyze-mod-service.js +2 -1
- package/dist/entry-tools/analyze-symbol-service.js +18 -9
- package/dist/entry-tools/compare-minecraft-service.js +1 -1
- package/dist/entry-tools/inspect-minecraft/handlers/class-members.js +2 -1
- package/dist/entry-tools/inspect-minecraft/handlers/file.js +26 -1
- package/dist/entry-tools/inspect-minecraft/handlers/versions.js +2 -1
- package/dist/entry-tools/inspect-minecraft/internal.js +2 -0
- package/dist/entry-tools/response-contract.d.ts +2 -0
- package/dist/entry-tools/response-contract.js +2 -2
- package/dist/entry-tools/validate-project-service.d.ts +4 -4
- package/dist/entry-tools/verify-mixin-target-service.js +7 -7
- package/dist/index.js +20 -16
- package/dist/java-process.js +1 -1
- package/dist/json-rpc-framing.d.ts +4 -0
- package/dist/json-rpc-framing.js +23 -1
- package/dist/mapping/internal-types.d.ts +17 -0
- package/dist/mapping/loaders/tiny-maven.js +46 -20
- package/dist/mapping/lookup.d.ts +12 -1
- package/dist/mapping/lookup.js +53 -1
- package/dist/mapping-service.js +36 -21
- package/dist/mixin/annotation-validators.js +21 -3
- package/dist/mixin/parsed-validator.js +1 -0
- package/dist/mixin-parser.js +71 -3
- package/dist/mod-decompile-service.d.ts +2 -0
- package/dist/mod-decompile-service.js +19 -2
- package/dist/mod-remap-service.js +6 -5
- package/dist/mojang-tiny-mapping-service.js +5 -2
- package/dist/nbt/java-nbt-codec.js +40 -16
- package/dist/nbt/json-patch.js +51 -12
- package/dist/nbt/typed-json.d.ts +1 -0
- package/dist/nbt/typed-json.js +12 -2
- package/dist/nbt/types.d.ts +2 -2
- package/dist/path-converter.js +10 -3
- package/dist/registry-service.d.ts +2 -0
- package/dist/registry-service.js +16 -1
- package/dist/repo-downloader.js +4 -3
- package/dist/source/artifact-resolver.js +1 -1
- package/dist/source/class-source/snippet-builder.js +6 -0
- package/dist/source/class-source-helpers.d.ts +4 -4
- package/dist/source/class-source-helpers.js +12 -11
- package/dist/source/class-source.d.ts +19 -0
- package/dist/source/class-source.js +48 -21
- package/dist/source/file-access.js +3 -2
- package/dist/source/indexer.js +24 -7
- package/dist/source/lifecycle/mapping-helpers.js +28 -3
- package/dist/source/lifecycle/runtime-check.js +20 -6
- package/dist/source/search.js +23 -8
- package/dist/source/validate-mixin/pipeline/resolve.js +23 -3
- package/dist/source/workspace-target.js +2 -1
- package/dist/source-resolver.js +2 -2
- package/dist/source-service.js +9 -1
- package/dist/stdio-supervisor.d.ts +2 -2
- package/dist/stdio-supervisor.js +29 -9
- package/dist/storage/db.js +24 -1
- package/dist/storage/files-repo.d.ts +3 -0
- package/dist/storage/files-repo.js +43 -37
- package/dist/storage/symbols-repo.d.ts +9 -0
- package/dist/storage/symbols-repo.js +47 -19
- package/dist/symbols/symbol-extractor.js +1 -1
- package/dist/tool-guidance.d.ts +2 -1
- package/dist/tool-guidance.js +26 -1
- package/dist/tool-schemas.d.ts +4 -4
- package/dist/tool-schemas.js +91 -91
- package/dist/warning-details.d.ts +12 -0
- package/dist/warning-details.js +17 -0
- package/dist/workspace-mapping-service.js +9 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -15,7 +15,7 @@ import { remapModJar } from "./mod-remap-service.js";
|
|
|
15
15
|
import { registerResources } from "./resources.js";
|
|
16
16
|
import { SourceService } from "./source-service.js";
|
|
17
17
|
import { ToolExecutionGate } from "./tool-execution-gate.js";
|
|
18
|
-
import { classifyWarnings } from "./warning-details.js";
|
|
18
|
+
import { capWarningDetailsForSummary, classifyWarnings } from "./warning-details.js";
|
|
19
19
|
import { WorkspaceMappingService } from "./workspace-mapping-service.js";
|
|
20
20
|
import { InspectMinecraftService, inspectMinecraftSchema, inspectMinecraftShape } from "./entry-tools/inspect-minecraft-service.js";
|
|
21
21
|
import { AnalyzeSymbolService, analyzeSymbolSchema, analyzeSymbolShape } from "./entry-tools/analyze-symbol-service.js";
|
|
@@ -90,7 +90,7 @@ server.validateToolInput = async (_tool, args) => args;
|
|
|
90
90
|
// server.tool() but appends a note steering agents to the entry tools first.
|
|
91
91
|
// Entry tools, batch tools, and the NBT/runtime utilities (which have no entry
|
|
92
92
|
// equivalent) keep their plain descriptions via server.tool().
|
|
93
|
-
const EXPERT_TOOL_NOTE = " Expert
|
|
93
|
+
const EXPERT_TOOL_NOTE = " Expert tool: prefer the entry tools (inspect-minecraft, analyze-symbol, compare-minecraft, analyze-mod, validate-project) first.";
|
|
94
94
|
const expertTool = ((name, description, ...rest) => server.tool(name, description + EXPERT_TOOL_NOTE, ...rest));
|
|
95
95
|
const config = loadConfig();
|
|
96
96
|
const nbtLimits = {
|
|
@@ -385,18 +385,23 @@ async function runTool(tool, rawInput, schema, action) {
|
|
|
385
385
|
if (DETAIL_ENABLED_TOOL_NAMES.has(tool)) {
|
|
386
386
|
projectedResult = projectByDetail(tool, projectedResult, effectiveDetail, shapeInput.include);
|
|
387
387
|
}
|
|
388
|
-
// Entry, expert, and batch tools
|
|
388
|
+
// Entry, expert, and batch tools report the applied detail/include shape in meta.
|
|
389
|
+
// detailApplied is omitted when it matches the tool's default to keep responses lean.
|
|
390
|
+
const defaultDetail = ENTRY_TOOL_NAMES.has(tool)
|
|
391
|
+
? "summary"
|
|
392
|
+
: DEFAULT_DETAIL_BY_TOOL[tool] ?? "summary";
|
|
389
393
|
const entryMeta = ENTRY_TOOL_NAMES.has(tool) ||
|
|
390
394
|
DETAIL_ENABLED_TOOL_NAMES.has(tool) ||
|
|
391
395
|
BATCH_DETAIL_TOOL_NAMES.has(tool)
|
|
392
396
|
? buildEntryToolMeta({
|
|
393
397
|
detail: effectiveDetail,
|
|
398
|
+
defaultDetail,
|
|
394
399
|
include: shapeInput.include.size > 0 ? [...shapeInput.include] : undefined
|
|
395
400
|
})
|
|
396
401
|
: undefined;
|
|
397
402
|
const durationMs = Date.now() - startedAt;
|
|
398
403
|
sourceService.recordToolCall(tool, durationMs);
|
|
399
|
-
const warningDetails = classifyWarnings(warnings);
|
|
404
|
+
const warningDetails = capWarningDetailsForSummary(classifyWarnings(warnings), effectiveDetail === "summary");
|
|
400
405
|
return objectResult({
|
|
401
406
|
result: projectedResult,
|
|
402
407
|
meta: {
|
|
@@ -405,7 +410,7 @@ async function runTool(tool, rawInput, schema, action) {
|
|
|
405
410
|
requestId,
|
|
406
411
|
tool,
|
|
407
412
|
durationMs,
|
|
408
|
-
warnings,
|
|
413
|
+
...(warnings.length > 0 ? { warnings } : {}),
|
|
409
414
|
...(warningDetails.length > 0 ? { warningDetails } : {})
|
|
410
415
|
}
|
|
411
416
|
});
|
|
@@ -450,8 +455,7 @@ async function runTool(tool, rawInput, schema, action) {
|
|
|
450
455
|
const errorMeta = {
|
|
451
456
|
requestId,
|
|
452
457
|
tool,
|
|
453
|
-
durationMs: errorDurationMs
|
|
454
|
-
warnings: []
|
|
458
|
+
durationMs: errorDurationMs
|
|
455
459
|
};
|
|
456
460
|
applyErrorMetaExtensions(errorMeta, caughtError);
|
|
457
461
|
return objectResult({
|
|
@@ -467,7 +471,7 @@ expertTool("list-versions", "List available Minecraft versions from Mojang manif
|
|
|
467
471
|
registerToolSchema("list-versions", listVersionsSchema);
|
|
468
472
|
server.tool("inspect-minecraft", "Top-level workflow tool for version discovery, artifact resolution, class inspection, source search, file reads, and file listings.", inspectMinecraftShape, { readOnlyHint: true }, async (args) => runTool("inspect-minecraft", args, inspectMinecraftSchema, async (input) => inspectMinecraftService.execute(input)));
|
|
469
473
|
registerToolSchema("inspect-minecraft", inspectMinecraftSchema);
|
|
470
|
-
server.tool("analyze-symbol", "Top-level workflow tool for symbol existence, mapping, lifecycle
|
|
474
|
+
server.tool("analyze-symbol", "Top-level workflow tool for symbol existence, mapping, lifecycle, workspace analysis, and API overview. subject.kind='symbol' auto-detects class/field/method from the selector.", analyzeSymbolShape, { readOnlyHint: true }, async (args) => runTool("analyze-symbol", args, analyzeSymbolSchema, async (input) => analyzeSymbolService.execute(input)));
|
|
471
475
|
registerToolSchema("analyze-symbol", analyzeSymbolSchema);
|
|
472
476
|
server.tool("compare-minecraft", "Top-level workflow tool for version comparisons, class diffs, registry diffs, and migration overviews.", compareMinecraftShape, { readOnlyHint: true }, async (args) => runTool("compare-minecraft", args, compareMinecraftSchema, async (input) => compareMinecraftService.execute(input)));
|
|
473
477
|
registerToolSchema("compare-minecraft", compareMinecraftSchema);
|
|
@@ -497,16 +501,16 @@ if (!VERIFY_MIXIN_TARGET_OFF) {
|
|
|
497
501
|
registerToolSchema("verify-mixin-target", verifyMixinTargetSchema);
|
|
498
502
|
}
|
|
499
503
|
if (!BATCH_TOOLS_OFF) {
|
|
500
|
-
server.tool("batch-class-source", "Batch
|
|
504
|
+
server.tool("batch-class-source", "Batch get-class-source for many classes sharing one resolved artifact; returns per-entry status plus aggregate summary. Not read-only: per-entry outputFile writes to disk.", batchClassSourceShape, { readOnlyHint: false }, async (args) => runTool("batch-class-source", args, batchClassSourceSchema, async (input) => batchClassSourceService.execute(input)));
|
|
501
505
|
registerToolSchema("batch-class-source", batchClassSourceSchema);
|
|
502
|
-
server.tool("batch-class-members", "Batch
|
|
506
|
+
server.tool("batch-class-members", "Batch get-class-members for many classes sharing one resolved artifact; returns per-entry status plus aggregate summary.", batchClassMembersShape, { readOnlyHint: true }, async (args) => runTool("batch-class-members", args, batchClassMembersSchema, async (input) => batchClassMembersService.execute(input)));
|
|
503
507
|
registerToolSchema("batch-class-members", batchClassMembersSchema);
|
|
504
|
-
server.tool("batch-symbol-exists", "Batch
|
|
508
|
+
server.tool("batch-symbol-exists", "Batch check-symbol-exists for many symbols against one shared Minecraft version (target.kind=version or workspace only).", batchSymbolExistsShape, { readOnlyHint: true }, async (args) => runTool("batch-symbol-exists", args, batchSymbolExistsSchema, async (input) => batchSymbolExistsService.execute(input)));
|
|
505
509
|
registerToolSchema("batch-symbol-exists", batchSymbolExistsSchema);
|
|
506
|
-
server.tool("batch-mappings", "Batch mapping
|
|
510
|
+
server.tool("batch-mappings", "Batch find-mapping: resolve many symbols across mapping namespaces with one shared Minecraft version.", batchMappingsShape, { readOnlyHint: true }, async (args) => runTool("batch-mappings", args, batchMappingsSchema, async (input) => batchMappingsService.execute(input)));
|
|
507
511
|
registerToolSchema("batch-mappings", batchMappingsSchema);
|
|
508
512
|
}
|
|
509
|
-
expertTool("resolve-artifact", "Resolve source artifact
|
|
513
|
+
expertTool("resolve-artifact", "Resolve a source artifact and return artifact metadata. For target.kind=jar, only <basename>-sources.jar is auto-adopted.", resolveArtifactShape, { readOnlyHint: true }, async (args) => runTool("resolve-artifact", args, resolveArtifactSchema, async (input) => sourceService.resolveArtifact({
|
|
510
514
|
target: input.target,
|
|
511
515
|
mapping: input.mapping,
|
|
512
516
|
sourcePriority: input.sourcePriority,
|
|
@@ -532,7 +536,7 @@ expertTool("find-class", "Resolve a simple or qualified class name to fully-qual
|
|
|
532
536
|
limit: input.limit
|
|
533
537
|
})));
|
|
534
538
|
registerToolSchema("find-class", findClassSchema);
|
|
535
|
-
expertTool("get-class-source", "Get Java source for a class
|
|
539
|
+
expertTool("get-class-source", "Get Java source for a class. Default mode=metadata returns a symbol outline only; pass mode=snippet or mode=full to read source text. Not read-only: outputFile writes to disk.", getClassSourceShape, { readOnlyHint: false }, async (args) => runTool("get-class-source", args, getClassSourceSchema, async (input) => {
|
|
536
540
|
const normalizedTarget = normalizeSourceLookupTarget(input.target);
|
|
537
541
|
return sourceService.getClassSource({
|
|
538
542
|
className: input.className,
|
|
@@ -555,7 +559,7 @@ expertTool("get-class-source", "Get Java source for a class by target ({ kind: '
|
|
|
555
559
|
});
|
|
556
560
|
}));
|
|
557
561
|
registerToolSchema("get-class-source", getClassSourceSchema);
|
|
558
|
-
expertTool("get-class-members", "Get fields/methods/constructors for one class from binary bytecode
|
|
562
|
+
expertTool("get-class-members", "Get fields/methods/constructors for one class from binary bytecode.", getClassMembersShape, { readOnlyHint: true }, async (args) => runTool("get-class-members", args, getClassMembersSchema, async (input) => {
|
|
559
563
|
const normalizedTarget = normalizeSourceLookupTarget(input.target);
|
|
560
564
|
return sourceService.getClassMembers({
|
|
561
565
|
className: input.className,
|
|
@@ -657,7 +661,7 @@ expertTool("find-mapping", "Find symbol mapping candidates between namespaces us
|
|
|
657
661
|
maxCandidates: input.maxCandidates
|
|
658
662
|
})));
|
|
659
663
|
registerToolSchema("find-mapping", findMappingSchema);
|
|
660
|
-
expertTool("resolve-method-mapping-exact", "Strict
|
|
664
|
+
expertTool("resolve-method-mapping-exact", "Strict variant of find-mapping(kind=method, signatureMode=exact): requires a COMPLETE descriptor projection and returns mapping_unavailable when any descriptor class reference cannot be projected. Prefer find-mapping unless you need that guarantee.", resolveMethodMappingExactShape, { readOnlyHint: true }, async (args) => runTool("resolve-method-mapping-exact", args, resolveMethodMappingExactSchema, async (input) => sourceService.resolveMethodMappingExact({
|
|
661
665
|
version: input.version,
|
|
662
666
|
name: input.name,
|
|
663
667
|
owner: input.owner,
|
package/dist/java-process.js
CHANGED
|
@@ -9,6 +9,9 @@ export declare function encodeJsonRpcMessage(message: JSONRPCMessage, mode: Conc
|
|
|
9
9
|
export declare class JsonRpcFrameReader {
|
|
10
10
|
private mode;
|
|
11
11
|
private buffer;
|
|
12
|
+
private pendingChunks;
|
|
13
|
+
private pendingBytes;
|
|
14
|
+
private awaitedFrameEnd;
|
|
12
15
|
get currentMode(): FramingMode;
|
|
13
16
|
reset(): void;
|
|
14
17
|
clear(): void;
|
|
@@ -16,6 +19,7 @@ export declare class JsonRpcFrameReader {
|
|
|
16
19
|
onFrame: (frame: ParsedJsonRpcFrame) => void;
|
|
17
20
|
onError: (error: Error) => void;
|
|
18
21
|
}): void;
|
|
22
|
+
private canCompleteFrame;
|
|
19
23
|
private detectMode;
|
|
20
24
|
private readLineDelimitedMessage;
|
|
21
25
|
private readContentLengthMessage;
|
package/dist/json-rpc-framing.js
CHANGED
|
@@ -25,21 +25,35 @@ export function encodeJsonRpcMessage(message, mode) {
|
|
|
25
25
|
export class JsonRpcFrameReader {
|
|
26
26
|
mode = "unknown";
|
|
27
27
|
buffer = Buffer.alloc(0);
|
|
28
|
+
pendingChunks = [];
|
|
29
|
+
pendingBytes = 0;
|
|
30
|
+
awaitedFrameEnd = -1;
|
|
28
31
|
get currentMode() {
|
|
29
32
|
return this.mode;
|
|
30
33
|
}
|
|
31
34
|
reset() {
|
|
32
35
|
this.mode = "unknown";
|
|
36
|
+
this.awaitedFrameEnd = -1;
|
|
33
37
|
}
|
|
34
38
|
clear() {
|
|
35
39
|
this.mode = "unknown";
|
|
36
40
|
this.buffer = Buffer.alloc(0);
|
|
41
|
+
this.pendingChunks = [];
|
|
42
|
+
this.pendingBytes = 0;
|
|
43
|
+
this.awaitedFrameEnd = -1;
|
|
37
44
|
}
|
|
38
45
|
processChunk(chunk, handlers) {
|
|
39
46
|
if (chunk.length === 0) {
|
|
40
47
|
return;
|
|
41
48
|
}
|
|
42
|
-
this.
|
|
49
|
+
this.pendingChunks.push(chunk);
|
|
50
|
+
this.pendingBytes += chunk.length;
|
|
51
|
+
if (!this.canCompleteFrame(chunk)) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
this.buffer = Buffer.concat([this.buffer, ...this.pendingChunks]);
|
|
55
|
+
this.pendingChunks = [];
|
|
56
|
+
this.pendingBytes = 0;
|
|
43
57
|
while (true) {
|
|
44
58
|
try {
|
|
45
59
|
if (this.mode === "unknown") {
|
|
@@ -71,6 +85,12 @@ export class JsonRpcFrameReader {
|
|
|
71
85
|
}
|
|
72
86
|
}
|
|
73
87
|
}
|
|
88
|
+
canCompleteFrame(chunk) {
|
|
89
|
+
if (this.mode === "content-length" && this.awaitedFrameEnd >= 0) {
|
|
90
|
+
return this.buffer.length + this.pendingBytes >= this.awaitedFrameEnd;
|
|
91
|
+
}
|
|
92
|
+
return chunk.includes(0x0a);
|
|
93
|
+
}
|
|
74
94
|
detectMode() {
|
|
75
95
|
while (this.buffer.length > 0) {
|
|
76
96
|
if (this.buffer[0] === 0x0a) {
|
|
@@ -124,6 +144,7 @@ export class JsonRpcFrameReader {
|
|
|
124
144
|
}
|
|
125
145
|
}
|
|
126
146
|
readContentLengthMessage() {
|
|
147
|
+
this.awaitedFrameEnd = -1;
|
|
127
148
|
const headerBoundary = findHeaderBoundary(this.buffer);
|
|
128
149
|
if (!headerBoundary) {
|
|
129
150
|
return undefined;
|
|
@@ -158,6 +179,7 @@ export class JsonRpcFrameReader {
|
|
|
158
179
|
const messageStart = headerBoundary.index + headerBoundary.delimiterBytes;
|
|
159
180
|
const frameEnd = messageStart + contentLength;
|
|
160
181
|
if (this.buffer.length < frameEnd) {
|
|
182
|
+
this.awaitedFrameEnd = frameEnd;
|
|
161
183
|
return undefined;
|
|
162
184
|
}
|
|
163
185
|
const body = this.buffer.subarray(messageStart, frameEnd).toString("utf8");
|
|
@@ -20,6 +20,18 @@ export type DirectionIndex = {
|
|
|
20
20
|
simple: Map<string, Set<string>>;
|
|
21
21
|
records: Map<string, MappingSymbolRecord>;
|
|
22
22
|
};
|
|
23
|
+
/**
|
|
24
|
+
* O(1) exact-lookup index over a single target namespace's records, used by
|
|
25
|
+
* existence checks. `byKey` is keyed by `kind|owner|name` (owner empty for
|
|
26
|
+
* classes, which key on their FQCN symbol); `classBySimpleName` resolves the
|
|
27
|
+
* auto-class short-name path. Values are arrays so callers can still tell a
|
|
28
|
+
* unique match (resolved) from multiple (ambiguous).
|
|
29
|
+
*/
|
|
30
|
+
export type ExactRecordIndex = {
|
|
31
|
+
byKey: Map<string, MappingSymbolRecord[]>;
|
|
32
|
+
classBySimpleName: Map<string, MappingSymbolRecord[]>;
|
|
33
|
+
membersByOwner: Map<string, MappingSymbolRecord[]>;
|
|
34
|
+
};
|
|
23
35
|
export type MappingLookupSource = "loom-cache" | "maven" | "mojang-client-mappings";
|
|
24
36
|
export type PairRecord = {
|
|
25
37
|
index: DirectionIndex;
|
|
@@ -50,6 +62,11 @@ export type LoadedGraph = {
|
|
|
50
62
|
adjacency: Map<import("../types.js").SourceMapping, import("../types.js").SourceMapping[]>;
|
|
51
63
|
pathCache: Map<PairKey, import("../types.js").SourceMapping[] | undefined>;
|
|
52
64
|
recordsByTarget: Map<import("../types.js").SourceMapping, MappingSymbolRecord[]>;
|
|
65
|
+
/**
|
|
66
|
+
* Graph-scoped, lazily-built exact-lookup indexes keyed by target namespace.
|
|
67
|
+
* Populated on first existence check for a namespace; dies with graph eviction.
|
|
68
|
+
*/
|
|
69
|
+
exactRecordIndex: Map<import("../types.js").SourceMapping, ExactRecordIndex>;
|
|
53
70
|
/**
|
|
54
71
|
* Graph-scoped cache of class-to-class descriptor projections, keyed by
|
|
55
72
|
* `path.join(">") + NUL + internalName`. Value is the projected internal name,
|
|
@@ -53,36 +53,62 @@ export async function loadTinyPairsFromMaven(deps, version) {
|
|
|
53
53
|
const warnings = [];
|
|
54
54
|
const merged = new Map();
|
|
55
55
|
const repos = deps.config.sourceRepos;
|
|
56
|
-
const
|
|
57
|
-
const yarnUrls = [];
|
|
56
|
+
const attemptedUrls = [];
|
|
58
57
|
const repoBases = repos.map((repo) => repo.replace(/\/+$/, ""));
|
|
59
58
|
const yarnCoordinatesByRepo = await Promise.all(repoBases.map(async (base) => ({
|
|
60
59
|
base,
|
|
61
60
|
yarnCoordinates: await fetchYarnCoordinates(deps.fetchFn, base, version)
|
|
62
61
|
})));
|
|
62
|
+
const tryUrls = async (urls) => {
|
|
63
|
+
for (const url of urls) {
|
|
64
|
+
attemptedUrls.push(url);
|
|
65
|
+
try {
|
|
66
|
+
const downloaded = await downloadToCache(url, defaultDownloadPath(deps.config.cacheDir, url), {
|
|
67
|
+
fetchFn: deps.fetchFn,
|
|
68
|
+
retries: deps.config.fetchRetries,
|
|
69
|
+
timeoutMs: deps.config.fetchTimeoutMs
|
|
70
|
+
});
|
|
71
|
+
if (!downloaded.ok || !downloaded.path) {
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
const parsed = await parseTinyFromJar(downloaded.path);
|
|
75
|
+
if (parsed.size > 0) {
|
|
76
|
+
return parsed;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
// try the next candidate URL
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return undefined;
|
|
84
|
+
};
|
|
85
|
+
let intermediaryParsed;
|
|
86
|
+
let yarnParsed;
|
|
63
87
|
for (const { base, yarnCoordinates } of yarnCoordinatesByRepo) {
|
|
64
|
-
|
|
88
|
+
if (!intermediaryParsed) {
|
|
89
|
+
intermediaryParsed = await tryUrls([
|
|
90
|
+
`${base}/net/fabricmc/intermediary/${version}/intermediary-${version}-v2.jar`,
|
|
91
|
+
`${base}/net/fabricmc/intermediary/${version}/intermediary-${version}.jar`
|
|
92
|
+
]);
|
|
93
|
+
}
|
|
65
94
|
for (const coordinate of yarnCoordinates) {
|
|
66
|
-
|
|
95
|
+
if (yarnParsed) {
|
|
96
|
+
break;
|
|
97
|
+
}
|
|
98
|
+
yarnParsed = await tryUrls([
|
|
99
|
+
`${base}/net/fabricmc/yarn/${coordinate}/yarn-${coordinate}-v2.jar`,
|
|
100
|
+
`${base}/net/fabricmc/yarn/${coordinate}/yarn-${coordinate}.jar`
|
|
101
|
+
]);
|
|
67
102
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
const parsedResults = await Promise.allSettled(allUrls.map(async (url) => {
|
|
71
|
-
const downloaded = await downloadToCache(url, defaultDownloadPath(deps.config.cacheDir, url), {
|
|
72
|
-
fetchFn: deps.fetchFn,
|
|
73
|
-
retries: deps.config.fetchRetries,
|
|
74
|
-
timeoutMs: deps.config.fetchTimeoutMs
|
|
75
|
-
});
|
|
76
|
-
if (!downloaded.ok || !downloaded.path) {
|
|
77
|
-
return undefined;
|
|
103
|
+
if (intermediaryParsed && yarnParsed) {
|
|
104
|
+
break;
|
|
78
105
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
if (result.status !== "fulfilled" || !result.value) {
|
|
106
|
+
}
|
|
107
|
+
for (const parsed of [intermediaryParsed, yarnParsed]) {
|
|
108
|
+
if (!parsed) {
|
|
83
109
|
continue;
|
|
84
110
|
}
|
|
85
|
-
for (const [key, index] of
|
|
111
|
+
for (const [key, index] of parsed.entries()) {
|
|
86
112
|
const existing = merged.get(key);
|
|
87
113
|
if (!existing) {
|
|
88
114
|
merged.set(key, index);
|
|
@@ -98,7 +124,7 @@ export async function loadTinyPairsFromMaven(deps, version) {
|
|
|
98
124
|
return {
|
|
99
125
|
pairs: merged,
|
|
100
126
|
warnings,
|
|
101
|
-
mappingArtifact:
|
|
127
|
+
mappingArtifact: attemptedUrls[0] ?? "maven:none"
|
|
102
128
|
};
|
|
103
129
|
}
|
|
104
130
|
//# sourceMappingURL=tiny-maven.js.map
|
package/dist/mapping/lookup.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { MappingSourcePriority, SourceMapping } from "../types.js";
|
|
2
|
-
import type { CandidateAccumulator, DirectionIndex, LoadedGraph, MappingLookupSource, MappingSymbolRecord, MatchRankKey, PairKey, PairRecord } from "./internal-types.js";
|
|
2
|
+
import type { CandidateAccumulator, DirectionIndex, ExactRecordIndex, LoadedGraph, MappingLookupSource, MappingSymbolRecord, MatchRankKey, PairKey, PairRecord } from "./internal-types.js";
|
|
3
3
|
import { MAX_CANDIDATES } from "./internal-types.js";
|
|
4
4
|
import type { ClassApiMatrixKind, DescriptorProjection, MappingLookupCandidate, ResolutionCandidate, SymbolQueryInput, SymbolReference } from "./types.js";
|
|
5
5
|
export type { DescriptorProjection };
|
|
@@ -41,6 +41,17 @@ export declare function applyDisambiguationHints(candidates: MappingLookupCandid
|
|
|
41
41
|
export declare function projectLookupCandidateDescriptor(candidate: MappingLookupCandidate, sourceDescriptor: string, targetDescriptor: string | undefined): MappingLookupCandidate;
|
|
42
42
|
export declare function effectiveLoomSearchProjectPath(projectPath: string | undefined): string | undefined;
|
|
43
43
|
export declare function collectTargetRecords(graph: LoadedGraph, targetMapping: SourceMapping): MappingSymbolRecord[];
|
|
44
|
+
/**
|
|
45
|
+
* Lazily build (and memoize on the graph) an O(1) exact-lookup index for a
|
|
46
|
+
* target namespace. Classes key on their FQCN symbol (owner left empty) and are
|
|
47
|
+
* additionally indexed by simple name for the auto-class short-name path;
|
|
48
|
+
* fields/methods key on `kind|owner|name`.
|
|
49
|
+
*/
|
|
50
|
+
export declare function collectExactRecordIndex(graph: LoadedGraph, targetMapping: SourceMapping): ExactRecordIndex;
|
|
51
|
+
export declare function lookupExactClassBySymbol(index: ExactRecordIndex, symbol: string): MappingSymbolRecord[];
|
|
52
|
+
export declare function lookupExactMembersByOwner(index: ExactRecordIndex, owner: string): MappingSymbolRecord[];
|
|
53
|
+
export declare function lookupExactClassBySimpleName(index: ExactRecordIndex, name: string): MappingSymbolRecord[];
|
|
54
|
+
export declare function lookupExactMembers(index: ExactRecordIndex, kind: "field" | "method", owner: string, name: string): MappingSymbolRecord[];
|
|
44
55
|
export declare function normalizeIncludedKinds(inputKinds: ClassApiMatrixKind[] | undefined): Set<ClassApiMatrixKind>;
|
|
45
56
|
export declare function inferAmbiguityReasons(candidates: ResolutionCandidate[], usedMojangClientMappings: boolean): string[];
|
|
46
57
|
export declare function clampCandidateLimit(limit: number | undefined): number;
|
package/dist/mapping/lookup.js
CHANGED
|
@@ -429,7 +429,59 @@ export function effectiveLoomSearchProjectPath(projectPath) {
|
|
|
429
429
|
return normalizeOptionalProjectPath(projectPath) ?? normalizeOptionalProjectPath(process.cwd());
|
|
430
430
|
}
|
|
431
431
|
export function collectTargetRecords(graph, targetMapping) {
|
|
432
|
-
return
|
|
432
|
+
return graph.recordsByTarget.get(targetMapping) ?? [];
|
|
433
|
+
}
|
|
434
|
+
function exactRecordKey(kind, owner, name) {
|
|
435
|
+
return `${kind}|${owner}|${name}`;
|
|
436
|
+
}
|
|
437
|
+
/**
|
|
438
|
+
* Lazily build (and memoize on the graph) an O(1) exact-lookup index for a
|
|
439
|
+
* target namespace. Classes key on their FQCN symbol (owner left empty) and are
|
|
440
|
+
* additionally indexed by simple name for the auto-class short-name path;
|
|
441
|
+
* fields/methods key on `kind|owner|name`.
|
|
442
|
+
*/
|
|
443
|
+
export function collectExactRecordIndex(graph, targetMapping) {
|
|
444
|
+
const cached = graph.exactRecordIndex.get(targetMapping);
|
|
445
|
+
if (cached) {
|
|
446
|
+
return cached;
|
|
447
|
+
}
|
|
448
|
+
const byKey = new Map();
|
|
449
|
+
const classBySimpleName = new Map();
|
|
450
|
+
const membersByOwner = new Map();
|
|
451
|
+
const push = (map, key, record) => {
|
|
452
|
+
const bucket = map.get(key);
|
|
453
|
+
if (bucket) {
|
|
454
|
+
bucket.push(record);
|
|
455
|
+
}
|
|
456
|
+
else {
|
|
457
|
+
map.set(key, [record]);
|
|
458
|
+
}
|
|
459
|
+
};
|
|
460
|
+
for (const record of collectTargetRecords(graph, targetMapping)) {
|
|
461
|
+
if (record.kind === "class") {
|
|
462
|
+
push(byKey, exactRecordKey("class", "", record.symbol), record);
|
|
463
|
+
push(classBySimpleName, record.name, record);
|
|
464
|
+
}
|
|
465
|
+
else {
|
|
466
|
+
push(byKey, exactRecordKey(record.kind, record.owner ?? "", record.name), record);
|
|
467
|
+
push(membersByOwner, record.owner ?? "", record);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
const index = { byKey, classBySimpleName, membersByOwner };
|
|
471
|
+
graph.exactRecordIndex.set(targetMapping, index);
|
|
472
|
+
return index;
|
|
473
|
+
}
|
|
474
|
+
export function lookupExactClassBySymbol(index, symbol) {
|
|
475
|
+
return index.byKey.get(exactRecordKey("class", "", symbol)) ?? [];
|
|
476
|
+
}
|
|
477
|
+
export function lookupExactMembersByOwner(index, owner) {
|
|
478
|
+
return index.membersByOwner.get(owner) ?? [];
|
|
479
|
+
}
|
|
480
|
+
export function lookupExactClassBySimpleName(index, name) {
|
|
481
|
+
return index.classBySimpleName.get(name) ?? [];
|
|
482
|
+
}
|
|
483
|
+
export function lookupExactMembers(index, kind, owner, name) {
|
|
484
|
+
return index.byKey.get(exactRecordKey(kind, owner, name)) ?? [];
|
|
433
485
|
}
|
|
434
486
|
export function normalizeIncludedKinds(inputKinds) {
|
|
435
487
|
const normalized = new Set();
|
package/dist/mapping-service.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
-
import { mkdir, writeFile } from "node:fs/promises";
|
|
2
|
+
import { mkdir, rename, writeFile } from "node:fs/promises";
|
|
3
3
|
import { dirname, join } from "node:path";
|
|
4
|
+
import { randomBytes } from "node:crypto";
|
|
4
5
|
import { buildSuggestedCall } from "./build-suggested-call.js";
|
|
5
6
|
import { createError, ERROR_CODES } from "./errors.js";
|
|
6
7
|
import { buildPageContextKey, encodeOffsetCursor, resolveCursorOffset } from "./page-cursor.js";
|
|
@@ -11,7 +12,7 @@ import { VersionService, isUnobfuscatedVersion } from "./version-service.js";
|
|
|
11
12
|
import { buildSymbolKey, createClassSymbolRecord, mergeDirectionIndexes, normalizeMappedSymbolOutput } from "./mapping/parsers/symbol-records.js";
|
|
12
13
|
import { buildAdjacency, buildTargetRecordIndex, pairKey } from "./mapping/parsers/normalize.js";
|
|
13
14
|
import { MATCH_RANK, MAX_CANDIDATES } from "./mapping/internal-types.js";
|
|
14
|
-
import { applyDisambiguationHints, clampRowLimit, collectTargetRecords, effectiveLoomSearchProjectPath, inferAmbiguityReasons, invalidInputError, limitResolutionCandidates, lookupCandidates, mappingPriorityFromInput, mappingSourceOrder, namespacePath, normalizeIncludedKinds, normalizeQuerySymbol, pathToTransformChain, pathUsesSource, projectLookupCandidateDescriptor, requiresOnlyObfuscatedMojangGraph, toLookupCandidate, toResolutionCandidate } from "./mapping/lookup.js";
|
|
15
|
+
import { applyDisambiguationHints, clampRowLimit, collectExactRecordIndex, collectTargetRecords, effectiveLoomSearchProjectPath, inferAmbiguityReasons, invalidInputError, limitResolutionCandidates, lookupCandidates, lookupExactClassBySimpleName, lookupExactClassBySymbol, lookupExactMembers, lookupExactMembersByOwner, mappingPriorityFromInput, mappingSourceOrder, namespacePath, normalizeIncludedKinds, normalizeQuerySymbol, pathToTransformChain, pathUsesSource, projectLookupCandidateDescriptor, requiresOnlyObfuscatedMojangGraph, toLookupCandidate, toResolutionCandidate } from "./mapping/lookup.js";
|
|
15
16
|
import { loadMojangPairs } from "./mapping/loaders/mojang.js";
|
|
16
17
|
import { loadTinyPairsFromLoom } from "./mapping/loaders/tiny-loom.js";
|
|
17
18
|
import { loadTinyPairsFromMaven } from "./mapping/loaders/tiny-maven.js";
|
|
@@ -375,7 +376,13 @@ export class MappingService {
|
|
|
375
376
|
const candidates = rawCandidates.map(toResolutionCandidate);
|
|
376
377
|
const limitedCandidates = limitResolutionCandidates(candidates, input.maxCandidates);
|
|
377
378
|
const strictDescriptor = projectedDescriptor ?? descriptor;
|
|
378
|
-
const
|
|
379
|
+
const acceptedDescriptors = new Set([descriptor, strictDescriptor]);
|
|
380
|
+
const toObfuscatedPath = namespacePath(graph, sourceMapping, "obfuscated");
|
|
381
|
+
if (toObfuscatedPath) {
|
|
382
|
+
const obfuscatedProjection = this.projectMethodDescriptorToTarget(graph, toObfuscatedPath, descriptor);
|
|
383
|
+
acceptedDescriptors.add(obfuscatedProjection.descriptor);
|
|
384
|
+
}
|
|
385
|
+
const strictCandidates = rawCandidates.filter((candidate) => candidate.descriptor !== undefined && acceptedDescriptors.has(candidate.descriptor));
|
|
379
386
|
if (strictCandidates.length === 1) {
|
|
380
387
|
const resolved = toResolutionCandidate(strictCandidates[0]);
|
|
381
388
|
return {
|
|
@@ -510,18 +517,18 @@ export class MappingService {
|
|
|
510
517
|
warnings
|
|
511
518
|
};
|
|
512
519
|
}
|
|
513
|
-
const
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
return false;
|
|
520
|
+
const exactIndex = collectExactRecordIndex(graph, baseMapping);
|
|
521
|
+
const baseRecords = [];
|
|
522
|
+
if (includeKinds.has("class")) {
|
|
523
|
+
for (const record of lookupExactClassBySymbol(exactIndex, baseClass.symbol)) {
|
|
524
|
+
baseRecords.push(record);
|
|
519
525
|
}
|
|
520
|
-
|
|
521
|
-
|
|
526
|
+
}
|
|
527
|
+
for (const record of lookupExactMembersByOwner(exactIndex, baseClass.symbol)) {
|
|
528
|
+
if (record.kind === "field" ? includeKinds.has("field") : includeKinds.has("method")) {
|
|
529
|
+
baseRecords.push(record);
|
|
522
530
|
}
|
|
523
|
-
|
|
524
|
-
});
|
|
531
|
+
}
|
|
525
532
|
const rowKindOrder = {
|
|
526
533
|
class: 0,
|
|
527
534
|
field: 1,
|
|
@@ -564,7 +571,8 @@ export class MappingService {
|
|
|
564
571
|
className,
|
|
565
572
|
classNameMapping,
|
|
566
573
|
(input.includeKinds ?? []).join(","),
|
|
567
|
-
input.sourcePriority
|
|
574
|
+
input.sourcePriority,
|
|
575
|
+
resolveGradleUserHomePath(input.gradleUserHome)
|
|
568
576
|
]);
|
|
569
577
|
const { offset: rowOffset, cursorIgnored: rowCursorIgnored } = resolveCursorOffset(input.cursor, rowCursorContext);
|
|
570
578
|
const windowBase = rowLimit != null ? dedupedBase.slice(rowOffset, rowOffset + rowLimit) : dedupedBase.slice(rowOffset);
|
|
@@ -709,7 +717,9 @@ export class MappingService {
|
|
|
709
717
|
};
|
|
710
718
|
})()
|
|
711
719
|
: (() => {
|
|
712
|
-
const { record: queryRecord, querySymbol } = normalizeQuerySymbol(input, effectiveSignatureMode
|
|
720
|
+
const { record: queryRecord, querySymbol } = normalizeQuerySymbol(input, effectiveSignatureMode, {
|
|
721
|
+
allowShortClassName: input.kind === "class" && sourceMapping === "obfuscated"
|
|
722
|
+
});
|
|
713
723
|
return {
|
|
714
724
|
mode: "strict",
|
|
715
725
|
queryRecord,
|
|
@@ -730,6 +740,7 @@ export class MappingService {
|
|
|
730
740
|
warnings
|
|
731
741
|
};
|
|
732
742
|
}
|
|
743
|
+
const exactIndex = collectExactRecordIndex(graph, sourceMapping);
|
|
733
744
|
const buildOutput = (querySymbol, matched, status) => {
|
|
734
745
|
const candidates = matched.map((record) => toResolutionCandidate(toLookupCandidate(record)));
|
|
735
746
|
const limitedCandidates = limitResolutionCandidates(candidates, input.maxCandidates);
|
|
@@ -751,11 +762,11 @@ export class MappingService {
|
|
|
751
762
|
if (normalizedQuery.mode === "auto-class") {
|
|
752
763
|
const autoClassName = normalizedQuery.className;
|
|
753
764
|
if (autoClassName.includes(".")) {
|
|
754
|
-
const matched =
|
|
765
|
+
const matched = lookupExactClassBySymbol(exactIndex, autoClassName);
|
|
755
766
|
const status = matched.length === 1 ? "resolved" : matched.length > 1 ? "ambiguous" : "not_found";
|
|
756
767
|
return buildOutput(normalizedQuery.querySymbol, matched, status);
|
|
757
768
|
}
|
|
758
|
-
const matched =
|
|
769
|
+
const matched = lookupExactClassBySimpleName(exactIndex, autoClassName);
|
|
759
770
|
const status = matched.length === 1 ? "resolved" : matched.length > 1 ? "ambiguous" : "not_found";
|
|
760
771
|
if (status === "ambiguous") {
|
|
761
772
|
warnings.push(`Multiple class symbols matched short name "${autoClassName}". Provide fully-qualified class name.`);
|
|
@@ -764,16 +775,16 @@ export class MappingService {
|
|
|
764
775
|
}
|
|
765
776
|
const { queryRecord, querySymbol } = normalizedQuery;
|
|
766
777
|
if (queryRecord.kind === "class") {
|
|
767
|
-
const matched =
|
|
778
|
+
const matched = lookupExactClassBySymbol(exactIndex, queryRecord.symbol);
|
|
768
779
|
const status = matched.length === 1 ? "resolved" : matched.length > 1 ? "ambiguous" : "not_found";
|
|
769
780
|
return buildOutput(querySymbol, matched, status);
|
|
770
781
|
}
|
|
771
782
|
if (queryRecord.kind === "field") {
|
|
772
|
-
const matched =
|
|
783
|
+
const matched = lookupExactMembers(exactIndex, "field", queryRecord.owner ?? "", queryRecord.name);
|
|
773
784
|
const status = matched.length === 1 ? "resolved" : matched.length > 1 ? "ambiguous" : "not_found";
|
|
774
785
|
return buildOutput(querySymbol, matched, status);
|
|
775
786
|
}
|
|
776
|
-
const methodCandidates =
|
|
787
|
+
const methodCandidates = lookupExactMembers(exactIndex, "method", queryRecord.owner ?? "", queryRecord.name);
|
|
777
788
|
// name-only mode: skip descriptor matching, resolve by owner+name
|
|
778
789
|
if (effectiveSignatureMode === "name-only") {
|
|
779
790
|
const status = methodCandidates.length === 1 ? "resolved" : methodCandidates.length > 1 ? "ambiguous" : "not_found";
|
|
@@ -1102,6 +1113,7 @@ export class MappingService {
|
|
|
1102
1113
|
adjacency: new Map(),
|
|
1103
1114
|
pathCache: new Map(),
|
|
1104
1115
|
recordsByTarget: new Map(),
|
|
1116
|
+
exactRecordIndex: new Map(),
|
|
1105
1117
|
classProjectionCache: new Map(),
|
|
1106
1118
|
warnings: [
|
|
1107
1119
|
`Version ${version} is unobfuscated; mapping graph is empty because the runtime already uses deobfuscated names.`
|
|
@@ -1116,6 +1128,7 @@ export class MappingService {
|
|
|
1116
1128
|
adjacency: new Map(),
|
|
1117
1129
|
pathCache: new Map(),
|
|
1118
1130
|
recordsByTarget: new Map(),
|
|
1131
|
+
exactRecordIndex: new Map(),
|
|
1119
1132
|
classProjectionCache: new Map(),
|
|
1120
1133
|
warnings: []
|
|
1121
1134
|
};
|
|
@@ -1282,7 +1295,9 @@ async function extractTinyFromJar(jarPath, outputPath) {
|
|
|
1282
1295
|
return false;
|
|
1283
1296
|
}
|
|
1284
1297
|
await mkdir(dirname(outputPath), { recursive: true });
|
|
1285
|
-
|
|
1298
|
+
const tempPath = `${outputPath}.${randomBytes(4).toString("hex")}.tmp`;
|
|
1299
|
+
await writeFile(tempPath, tinyEntry.content, "utf8");
|
|
1300
|
+
await rename(tempPath, outputPath);
|
|
1286
1301
|
return true;
|
|
1287
1302
|
}
|
|
1288
1303
|
/**
|
|
@@ -6,8 +6,26 @@ export function validateInjection(inj, targetMembers, targetNames, issues, resol
|
|
|
6
6
|
continue;
|
|
7
7
|
const methodNames = allMethodNames(members);
|
|
8
8
|
// Strip owner prefix and JVM descriptor from the method reference
|
|
9
|
-
const
|
|
10
|
-
|
|
9
|
+
const rawMethodName = extractMethodName(inj.method);
|
|
10
|
+
// Strip a trailing target-selector quantifier (e.g. "tick*", "tick+", "tick{2,3}")
|
|
11
|
+
const quantifier = /(?:\*|\+|\{\d+(?:,\d+)?\})$/.exec(rawMethodName)?.[0];
|
|
12
|
+
const methodName = quantifier
|
|
13
|
+
? rawMethodName.slice(0, rawMethodName.length - quantifier.length)
|
|
14
|
+
: rawMethodName;
|
|
15
|
+
if (quantifier && methodName === "") {
|
|
16
|
+
// Bare wildcard selector (e.g. method = "*") matches everything; unverifiable.
|
|
17
|
+
resolvedMembers.push({
|
|
18
|
+
annotation: `@${inj.annotation}`,
|
|
19
|
+
name: rawMethodName,
|
|
20
|
+
line: inj.line,
|
|
21
|
+
status: "skipped"
|
|
22
|
+
});
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
const matched = (quantifier === "*" || quantifier === "+")
|
|
26
|
+
? methodNames.some((n) => n.startsWith(methodName))
|
|
27
|
+
: methodNames.includes(methodName);
|
|
28
|
+
if (!matched) {
|
|
11
29
|
const suggestions = suggestSimilar(methodName, methodNames);
|
|
12
30
|
const descriptor = extractMethodDescriptor(inj.method);
|
|
13
31
|
const descriptorHint = descriptor ? ` (descriptor: ${descriptor})` : "";
|
|
@@ -46,7 +64,7 @@ export function validateInjection(inj, targetMembers, targetNames, issues, resol
|
|
|
46
64
|
// Name matched. If the reference carries a JVM descriptor, require an
|
|
47
65
|
// overload to match it; otherwise a wrong-signature target (e.g. tick(D)V
|
|
48
66
|
// when only tick(I)V exists) is silently reported as resolved.
|
|
49
|
-
const refDescriptor = extractMethodDescriptor(inj.method);
|
|
67
|
+
const refDescriptor = quantifier ? undefined : extractMethodDescriptor(inj.method);
|
|
50
68
|
const sameNameMembers = [...members.constructors, ...members.methods].filter((member) => member.name === methodName);
|
|
51
69
|
if (refDescriptor &&
|
|
52
70
|
sameNameMembers.length > 0 &&
|
|
@@ -27,6 +27,7 @@ export function validateParsedMixin(parsed, targetMembers, warnings, provenance,
|
|
|
27
27
|
resolutionPath: "target-mapping-failed",
|
|
28
28
|
falsePositiveRisk: healthReport?.overallHealthy === false ? "high" : "medium"
|
|
29
29
|
});
|
|
30
|
+
addSkippedMembers(parsed, resolvedMembers);
|
|
30
31
|
}
|
|
31
32
|
else if (symbolExistsButSignatureFailed?.has(target.className)) {
|
|
32
33
|
// Symbol exists in mapping graph but getSignature failed — tool limitation, not code issue
|