@adhisang/minecraft-modding-mcp 4.0.0 → 4.1.1
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 +61 -0
- package/README.md +40 -23
- package/dist/build-suggested-call.d.ts +29 -0
- package/dist/build-suggested-call.js +58 -0
- package/dist/cache-registry.d.ts +3 -1
- package/dist/cache-registry.js +50 -6
- package/dist/entry-tools/analyze-symbol-service.d.ts +16 -16
- package/dist/entry-tools/batch-class-members-service.d.ts +34 -0
- package/dist/entry-tools/batch-class-members-service.js +97 -0
- package/dist/entry-tools/batch-class-source-service.d.ts +37 -0
- package/dist/entry-tools/batch-class-source-service.js +100 -0
- package/dist/entry-tools/batch-mappings-service.d.ts +36 -0
- package/dist/entry-tools/batch-mappings-service.js +66 -0
- package/dist/entry-tools/batch-runner.d.ts +72 -0
- package/dist/entry-tools/batch-runner.js +90 -0
- package/dist/entry-tools/batch-symbol-exists-service.d.ts +46 -0
- package/dist/entry-tools/batch-symbol-exists-service.js +113 -0
- package/dist/entry-tools/compare-minecraft-service.d.ts +6 -6
- package/dist/entry-tools/inspect-minecraft/handlers/artifact.d.ts +5 -0
- package/dist/entry-tools/inspect-minecraft/handlers/artifact.js +83 -0
- package/dist/entry-tools/inspect-minecraft/handlers/class-members.d.ts +6 -0
- package/dist/entry-tools/inspect-minecraft/handlers/class-members.js +80 -0
- package/dist/entry-tools/inspect-minecraft/handlers/class-overview.d.ts +5 -0
- package/dist/entry-tools/inspect-minecraft/handlers/class-overview.js +248 -0
- package/dist/entry-tools/inspect-minecraft/handlers/class-source.d.ts +5 -0
- package/dist/entry-tools/inspect-minecraft/handlers/class-source.js +60 -0
- package/dist/entry-tools/inspect-minecraft/handlers/file.d.ts +5 -0
- package/dist/entry-tools/inspect-minecraft/handlers/file.js +54 -0
- package/dist/entry-tools/inspect-minecraft/handlers/list-files.d.ts +5 -0
- package/dist/entry-tools/inspect-minecraft/handlers/list-files.js +100 -0
- package/dist/entry-tools/inspect-minecraft/handlers/search.d.ts +5 -0
- package/dist/entry-tools/inspect-minecraft/handlers/search.js +155 -0
- package/dist/entry-tools/inspect-minecraft/handlers/versions.d.ts +6 -0
- package/dist/entry-tools/inspect-minecraft/handlers/versions.js +49 -0
- package/dist/entry-tools/inspect-minecraft/internal.d.ts +1042 -0
- package/dist/entry-tools/inspect-minecraft/internal.js +448 -0
- package/dist/entry-tools/inspect-minecraft-service.d.ts +193 -308
- package/dist/entry-tools/inspect-minecraft-service.js +20 -1244
- package/dist/entry-tools/manage-cache-service.d.ts +16 -16
- package/dist/entry-tools/validate-project/cases/access-transformer.d.ts +6 -0
- package/dist/entry-tools/validate-project/cases/access-transformer.js +106 -0
- package/dist/entry-tools/validate-project/cases/access-widener.d.ts +6 -0
- package/dist/entry-tools/validate-project/cases/access-widener.js +86 -0
- package/dist/entry-tools/validate-project/cases/mixin.d.ts +6 -0
- package/dist/entry-tools/validate-project/cases/mixin.js +90 -0
- package/dist/entry-tools/validate-project/cases/project-summary.d.ts +102 -0
- package/dist/entry-tools/validate-project/cases/project-summary.js +415 -0
- package/dist/entry-tools/validate-project/internal.d.ts +142 -0
- package/dist/entry-tools/validate-project/internal.js +303 -0
- package/dist/entry-tools/validate-project-service.d.ts +67 -47
- package/dist/entry-tools/validate-project-service.js +13 -563
- package/dist/entry-tools/verify-mixin-target-service.d.ts +133 -0
- package/dist/entry-tools/verify-mixin-target-service.js +323 -0
- package/dist/error-mapping.d.ts +40 -0
- package/dist/error-mapping.js +139 -0
- package/dist/errors.d.ts +6 -0
- package/dist/errors.js +6 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +147 -1354
- package/dist/mapping/internal-types.d.ts +54 -0
- package/dist/mapping/internal-types.js +14 -0
- package/dist/mapping/loaders/mojang.d.ts +2 -0
- package/dist/mapping/loaders/mojang.js +64 -0
- package/dist/mapping/loaders/tiny-loom.d.ts +2 -0
- package/dist/mapping/loaders/tiny-loom.js +73 -0
- package/dist/mapping/loaders/tiny-maven.d.ts +2 -0
- package/dist/mapping/loaders/tiny-maven.js +104 -0
- package/dist/mapping/loaders/types.d.ts +14 -0
- package/dist/mapping/loaders/types.js +2 -0
- package/dist/mapping/lookup.d.ts +52 -0
- package/dist/mapping/lookup.js +496 -0
- package/dist/mapping/parsers/normalize.d.ts +10 -0
- package/dist/mapping/parsers/normalize.js +52 -0
- package/dist/mapping/parsers/proguard.d.ts +20 -0
- package/dist/mapping/parsers/proguard.js +138 -0
- package/dist/mapping/parsers/symbol-records.d.ts +27 -0
- package/dist/mapping/parsers/symbol-records.js +216 -0
- package/dist/mapping/parsers/tiny.d.ts +9 -0
- package/dist/mapping/parsers/tiny.js +96 -0
- package/dist/mapping/types.d.ts +147 -0
- package/dist/mapping/types.js +2 -0
- package/dist/mapping-pipeline-service.js +3 -2
- package/dist/mapping-service.d.ts +8 -145
- package/dist/mapping-service.js +30 -1207
- package/dist/mixin/access-validators.d.ts +9 -0
- package/dist/mixin/access-validators.js +257 -0
- package/dist/mixin/annotation-validators.d.ts +5 -0
- package/dist/mixin/annotation-validators.js +162 -0
- package/dist/mixin/helpers.d.ts +28 -0
- package/dist/mixin/helpers.js +315 -0
- package/dist/mixin/parsed-validator.d.ts +8 -0
- package/dist/mixin/parsed-validator.js +337 -0
- package/dist/mixin/types.d.ts +208 -0
- package/dist/mixin/types.js +28 -0
- package/dist/mixin-validator.d.ts +9 -201
- package/dist/mixin-validator.js +8 -1020
- package/dist/source/access-validate.d.ts +4 -0
- package/dist/source/access-validate.js +254 -0
- package/dist/source/artifact-resolver.d.ts +111 -0
- package/dist/source/artifact-resolver.js +1271 -0
- package/dist/source/cache-metrics.d.ts +26 -0
- package/dist/source/cache-metrics.js +172 -0
- package/dist/source/class-source/members-builder.d.ts +34 -0
- package/dist/source/class-source/members-builder.js +46 -0
- package/dist/source/class-source/snippet-builder.d.ts +19 -0
- package/dist/source/class-source/snippet-builder.js +46 -0
- package/dist/source/class-source-helpers.d.ts +34 -0
- package/dist/source/class-source-helpers.js +140 -0
- package/dist/source/class-source.d.ts +42 -0
- package/dist/source/class-source.js +883 -0
- package/dist/source/descriptor-utils.d.ts +6 -0
- package/dist/source/descriptor-utils.js +37 -0
- package/dist/source/file-access.d.ts +4 -0
- package/dist/source/file-access.js +102 -0
- package/dist/source/indexer.d.ts +82 -0
- package/dist/source/indexer.js +522 -0
- package/dist/source/lifecycle/diff-utils.d.ts +9 -0
- package/dist/source/lifecycle/diff-utils.js +107 -0
- package/dist/source/lifecycle/diff.d.ts +2 -0
- package/dist/source/lifecycle/diff.js +265 -0
- package/dist/source/lifecycle/mapping-helpers.d.ts +22 -0
- package/dist/source/lifecycle/mapping-helpers.js +327 -0
- package/dist/source/lifecycle/runtime-check.d.ts +2 -0
- package/dist/source/lifecycle/runtime-check.js +142 -0
- package/dist/source/lifecycle/trace.d.ts +2 -0
- package/dist/source/lifecycle/trace.js +231 -0
- package/dist/source/lifecycle.d.ts +4 -0
- package/dist/source/lifecycle.js +5 -0
- package/dist/source/search.d.ts +51 -0
- package/dist/source/search.js +676 -0
- package/dist/source/shared-utils.d.ts +6 -0
- package/dist/source/shared-utils.js +55 -0
- package/dist/source/state.d.ts +26 -0
- package/dist/source/state.js +24 -0
- package/dist/source/symbol-resolver.d.ts +3 -0
- package/dist/source/symbol-resolver.js +212 -0
- package/dist/source/validate-mixin/pipeline/mapping-health.d.ts +3 -0
- package/dist/source/validate-mixin/pipeline/mapping-health.js +41 -0
- package/dist/source/validate-mixin/pipeline/parse.d.ts +2 -0
- package/dist/source/validate-mixin/pipeline/parse.js +10 -0
- package/dist/source/validate-mixin/pipeline/resolve.d.ts +3 -0
- package/dist/source/validate-mixin/pipeline/resolve.js +78 -0
- package/dist/source/validate-mixin/pipeline/target-lookup.d.ts +6 -0
- package/dist/source/validate-mixin/pipeline/target-lookup.js +260 -0
- package/dist/source/validate-mixin/pipeline-context.d.ts +72 -0
- package/dist/source/validate-mixin/pipeline-context.js +93 -0
- package/dist/source/validate-mixin.d.ts +22 -0
- package/dist/source/validate-mixin.js +799 -0
- package/dist/source/workspace-target.d.ts +18 -0
- package/dist/source/workspace-target.js +305 -0
- package/dist/source-resolver.d.ts +1 -0
- package/dist/source-resolver.js +1 -1
- package/dist/source-service.d.ts +164 -170
- package/dist/source-service.js +70 -6116
- package/dist/stage-emitter.d.ts +13 -0
- package/dist/stage-emitter.js +30 -0
- package/dist/stdio-supervisor.d.ts +61 -0
- package/dist/stdio-supervisor.js +326 -9
- package/dist/tool-contract-manifest.d.ts +1 -1
- package/dist/tool-contract-manifest.js +23 -6
- package/dist/tool-guidance.d.ts +82 -0
- package/dist/tool-guidance.js +734 -0
- package/dist/tool-schema-registry.d.ts +16 -0
- package/dist/tool-schema-registry.js +37 -0
- package/dist/tool-schemas.d.ts +3518 -0
- package/dist/tool-schemas.js +813 -0
- package/dist/types.d.ts +36 -0
- package/dist/version-service.js +7 -6
- package/dist/workspace-context-cache.d.ts +32 -0
- package/dist/workspace-context-cache.js +66 -0
- package/dist/workspace-mapping-service.d.ts +16 -0
- package/dist/workspace-mapping-service.js +173 -1
- package/docs/README-ja.md +416 -0
- package/docs/examples.md +483 -0
- package/docs/tool-reference.md +462 -0
- package/package.json +17 -4
|
@@ -0,0 +1,883 @@
|
|
|
1
|
+
import { writeFile } from "node:fs/promises";
|
|
2
|
+
import { isAbsolute, resolve as resolvePath } from "node:path";
|
|
3
|
+
import { buildSuggestedCall } from "../build-suggested-call.js";
|
|
4
|
+
import { ERROR_CODES, createError, isAppError } from "../errors.js";
|
|
5
|
+
import * as artifactResolver from "./artifact-resolver.js";
|
|
6
|
+
import * as classSourceHelpers from "./class-source-helpers.js";
|
|
7
|
+
import { buildClassSourceSnippet } from "./class-source/snippet-builder.js";
|
|
8
|
+
import { remapAndCountMembers, sliceMembersWithLimit } from "./class-source/members-builder.js";
|
|
9
|
+
import { dedupeQualityFlags, normalizeMapping, normalizeOptionalString, normalizePathStyle } from "./shared-utils.js";
|
|
10
|
+
const MEMBERS_STATUS_LEGACY = process.env.MEMBERS_STATUS_LEGACY === "1";
|
|
11
|
+
function normalizeStrictPositiveInt(value, field) {
|
|
12
|
+
if (value == null) {
|
|
13
|
+
return undefined;
|
|
14
|
+
}
|
|
15
|
+
if (!Number.isFinite(value) || !Number.isInteger(value) || value <= 0) {
|
|
16
|
+
throw createError({
|
|
17
|
+
code: ERROR_CODES.INVALID_INPUT,
|
|
18
|
+
message: `${field} must be a positive integer.`,
|
|
19
|
+
details: { field, value }
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
return value;
|
|
23
|
+
}
|
|
24
|
+
function normalizeMemberAccess(access) {
|
|
25
|
+
if (access == null) {
|
|
26
|
+
return "public";
|
|
27
|
+
}
|
|
28
|
+
if (access === "public" || access === "all") {
|
|
29
|
+
return access;
|
|
30
|
+
}
|
|
31
|
+
throw createError({
|
|
32
|
+
code: ERROR_CODES.INVALID_INPUT,
|
|
33
|
+
message: `access must be "public" or "all".`,
|
|
34
|
+
details: { access }
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
function buildResolveArtifactParams(target, extra = {}) {
|
|
38
|
+
return {
|
|
39
|
+
target: {
|
|
40
|
+
kind: target.kind,
|
|
41
|
+
value: target.value
|
|
42
|
+
},
|
|
43
|
+
...extra
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
function looksLikeDeobfuscatedClassName(value) {
|
|
47
|
+
const trimmed = value.trim();
|
|
48
|
+
if (!trimmed) {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
if (trimmed.startsWith("net.minecraft.") || trimmed.startsWith("com.mojang.")) {
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
const simpleName = trimmed.split(/[.$]/).at(-1) ?? trimmed;
|
|
55
|
+
return /^[A-Z][A-Za-z0-9_$]{2,}$/.test(simpleName);
|
|
56
|
+
}
|
|
57
|
+
function obfuscatedNamespaceHint(className) {
|
|
58
|
+
return `Artifact is indexed in obfuscated runtime names. Deobfuscated names like "${className}" usually require mapping="mojang" or a find-mapping lookup to obfuscated names.`;
|
|
59
|
+
}
|
|
60
|
+
function hasPartialNetMinecraftCoverage(qualityFlags) {
|
|
61
|
+
return qualityFlags.includes("partial-source-no-net-minecraft");
|
|
62
|
+
}
|
|
63
|
+
function classNameToClassPath(className) {
|
|
64
|
+
const normalized = normalizePathStyle(className.trim()).replace(/\//g, ".");
|
|
65
|
+
const segments = normalized.split(".").filter((segment) => segment.length > 0);
|
|
66
|
+
if (segments.length === 0) {
|
|
67
|
+
return "";
|
|
68
|
+
}
|
|
69
|
+
const firstTypeSegment = segments.findIndex((segment) => /^[A-Z_$]/.test(segment));
|
|
70
|
+
if (firstTypeSegment < 0) {
|
|
71
|
+
return segments.join("/");
|
|
72
|
+
}
|
|
73
|
+
const packagePath = segments.slice(0, firstTypeSegment).join("/");
|
|
74
|
+
const typePath = segments.slice(firstTypeSegment).join("$");
|
|
75
|
+
return packagePath ? `${packagePath}/${typePath}` : typePath;
|
|
76
|
+
}
|
|
77
|
+
export function resolveClassFilePath(svc, artifactId, className) {
|
|
78
|
+
const normalizedClassName = className.trim();
|
|
79
|
+
const classPath = classNameToClassPath(normalizedClassName);
|
|
80
|
+
if (!classPath) {
|
|
81
|
+
return undefined;
|
|
82
|
+
}
|
|
83
|
+
const candidates = new Set([`${classPath}.java`]);
|
|
84
|
+
const innerIndex = classPath.indexOf("$");
|
|
85
|
+
if (innerIndex > 0) {
|
|
86
|
+
candidates.add(`${classPath.slice(0, innerIndex)}.java`);
|
|
87
|
+
}
|
|
88
|
+
const simpleName = normalizedClassName.split(/[.$]/).at(-1);
|
|
89
|
+
if (!simpleName) {
|
|
90
|
+
return undefined;
|
|
91
|
+
}
|
|
92
|
+
const lastSlash = classPath.lastIndexOf("/");
|
|
93
|
+
const expectedPrefix = lastSlash < 0 ? "" : classPath.slice(0, lastSlash + 1);
|
|
94
|
+
return svc.filesRepo.findBestClassLookupPath(artifactId, [...candidates], normalizedClassName, simpleName, expectedPrefix);
|
|
95
|
+
}
|
|
96
|
+
export async function resolveClassNameForLookup(svc, input) {
|
|
97
|
+
if (input.sourceMapping === input.targetMapping) {
|
|
98
|
+
return input.className;
|
|
99
|
+
}
|
|
100
|
+
if (!input.version) {
|
|
101
|
+
input.warnings.push(`Could not map class "${input.className}" from ${input.sourceMapping} to ${input.targetMapping} for ${input.context} because version is unavailable.`);
|
|
102
|
+
return input.className;
|
|
103
|
+
}
|
|
104
|
+
try {
|
|
105
|
+
const mapped = await svc.mappingService.findMapping({
|
|
106
|
+
version: input.version,
|
|
107
|
+
kind: "class",
|
|
108
|
+
name: input.className,
|
|
109
|
+
sourceMapping: input.sourceMapping,
|
|
110
|
+
targetMapping: input.targetMapping,
|
|
111
|
+
sourcePriority: input.sourcePriority
|
|
112
|
+
});
|
|
113
|
+
if (mapped.resolved && mapped.resolvedSymbol) {
|
|
114
|
+
return mapped.resolvedSymbol.name;
|
|
115
|
+
}
|
|
116
|
+
input.warnings.push(`Could not map class "${input.className}" from ${input.sourceMapping} to ${input.targetMapping} for ${input.context}.`);
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
input.warnings.push(`Mapping lookup failed for class "${input.className}" while preparing ${input.context} in ${input.targetMapping}.`);
|
|
120
|
+
}
|
|
121
|
+
return input.className;
|
|
122
|
+
}
|
|
123
|
+
export function buildFallbackProvenance(svc, input) {
|
|
124
|
+
const artifact = svc.getArtifact(input.artifactId);
|
|
125
|
+
const fallbackTarget = artifact.version
|
|
126
|
+
? { kind: "version", value: artifact.version }
|
|
127
|
+
: artifact.coordinate
|
|
128
|
+
? { kind: "coordinate", value: artifact.coordinate }
|
|
129
|
+
: { kind: "jar", value: artifact.sourceJarPath ?? artifact.binaryJarPath ?? input.artifactId };
|
|
130
|
+
const transformChain = artifact.provenance?.transformChain && artifact.provenance.transformChain.length > 0
|
|
131
|
+
? artifact.provenance.transformChain
|
|
132
|
+
: [`mapping:${input.requestedMapping}->${input.mappingApplied}`];
|
|
133
|
+
return {
|
|
134
|
+
target: fallbackTarget,
|
|
135
|
+
resolvedAt: artifact.updatedAt,
|
|
136
|
+
resolvedFrom: {
|
|
137
|
+
origin: artifact.origin,
|
|
138
|
+
sourceJarPath: artifact.sourceJarPath,
|
|
139
|
+
binaryJarPath: artifact.binaryJarPath,
|
|
140
|
+
coordinate: artifact.coordinate,
|
|
141
|
+
version: artifact.version,
|
|
142
|
+
repoUrl: artifact.repoUrl
|
|
143
|
+
},
|
|
144
|
+
transformChain
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
export function buildClassSourceNotFoundError(_svc, input) {
|
|
148
|
+
const simpleName = input.className.split(/[.$]/).at(-1) ?? input.className;
|
|
149
|
+
const details = {
|
|
150
|
+
artifactId: input.artifactId,
|
|
151
|
+
className: input.className,
|
|
152
|
+
mapping: input.mappingApplied,
|
|
153
|
+
qualityFlags: input.qualityFlags,
|
|
154
|
+
...(input.lookupClassName !== input.className ? { lookupClassName: input.lookupClassName } : {}),
|
|
155
|
+
...(input.filePath ? { filePath: input.filePath } : {}),
|
|
156
|
+
...(input.scope ? { scope: input.scope } : {}),
|
|
157
|
+
...(input.targetKind ? { targetKind: input.targetKind } : {}),
|
|
158
|
+
...(input.targetValue ? { targetValue: input.targetValue } : {}),
|
|
159
|
+
...(input.attemptedBinaryFallback ? { binaryFallbackAttempted: true } : {})
|
|
160
|
+
};
|
|
161
|
+
let nextAction = `Use find-class to resolve the correct fully-qualified name for "${simpleName}".`;
|
|
162
|
+
let suggestionSpec = {
|
|
163
|
+
tool: "find-class",
|
|
164
|
+
params: { className: simpleName, artifactId: input.artifactId }
|
|
165
|
+
};
|
|
166
|
+
if (input.targetKind === "version" && input.scope && input.scope !== "merged" && !input.projectPath) {
|
|
167
|
+
nextAction +=
|
|
168
|
+
` If the class exists in a modded environment, retry with scope: "merged" and projectPath pointing to your mod project.`;
|
|
169
|
+
}
|
|
170
|
+
else if (input.targetKind === "version" && input.scope && input.scope !== "merged" && input.projectPath) {
|
|
171
|
+
nextAction += ` The class may exist in merged sources; retry with scope: "merged".`;
|
|
172
|
+
}
|
|
173
|
+
if (hasPartialNetMinecraftCoverage(input.qualityFlags)) {
|
|
174
|
+
nextAction =
|
|
175
|
+
`Resolved source coverage does not include net.minecraft for "${input.className}",` +
|
|
176
|
+
(input.attemptedBinaryFallback
|
|
177
|
+
? " and binary fallback did not produce source for that class."
|
|
178
|
+
: " and a binary fallback has not produced source for that class.") +
|
|
179
|
+
" Use get-class-api-matrix or find-mapping instead of find-class for vanilla API discovery.";
|
|
180
|
+
if (input.version) {
|
|
181
|
+
suggestionSpec = {
|
|
182
|
+
tool: "get-class-api-matrix",
|
|
183
|
+
params: {
|
|
184
|
+
version: input.version,
|
|
185
|
+
className: input.className,
|
|
186
|
+
classNameMapping: input.requestedMapping
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
suggestionSpec = {
|
|
192
|
+
tool: "find-class",
|
|
193
|
+
params: { className: simpleName, artifactId: input.artifactId }
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
if (input.mappingApplied === "obfuscated" && looksLikeDeobfuscatedClassName(input.className)) {
|
|
198
|
+
nextAction += ` ${obfuscatedNamespaceHint(input.className)}`;
|
|
199
|
+
}
|
|
200
|
+
details.nextAction = nextAction;
|
|
201
|
+
Object.assign(details, buildSuggestedCall(suggestionSpec));
|
|
202
|
+
return createError({
|
|
203
|
+
code: ERROR_CODES.CLASS_NOT_FOUND,
|
|
204
|
+
message: `Source for class "${input.className}" was not found.`,
|
|
205
|
+
details
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
export function buildDecompiledFallback(svc, artifactId, lookupClassName, memberPattern, maxMembers) {
|
|
209
|
+
const filePath = resolveClassFilePath(svc, artifactId, lookupClassName);
|
|
210
|
+
if (!filePath) {
|
|
211
|
+
return undefined;
|
|
212
|
+
}
|
|
213
|
+
const row = svc.filesRepo.getFileContent(artifactId, filePath);
|
|
214
|
+
if (!row) {
|
|
215
|
+
return undefined;
|
|
216
|
+
}
|
|
217
|
+
const extracted = classSourceHelpers.extractDecompiledMembers(lookupClassName, filePath, row.content);
|
|
218
|
+
const filterByPattern = (list) => {
|
|
219
|
+
if (!memberPattern) {
|
|
220
|
+
return list;
|
|
221
|
+
}
|
|
222
|
+
const lower = memberPattern.toLowerCase();
|
|
223
|
+
return list.filter((entry) => entry.name.toLowerCase().includes(lower));
|
|
224
|
+
};
|
|
225
|
+
let constructors = filterByPattern(extracted.constructors);
|
|
226
|
+
let fields = filterByPattern(extracted.fields);
|
|
227
|
+
let methods = filterByPattern(extracted.methods);
|
|
228
|
+
const totalBefore = constructors.length + fields.length + methods.length;
|
|
229
|
+
if (totalBefore === 0) {
|
|
230
|
+
return undefined;
|
|
231
|
+
}
|
|
232
|
+
let remaining = maxMembers;
|
|
233
|
+
const takeWithinLimit = (list) => {
|
|
234
|
+
if (remaining <= 0) {
|
|
235
|
+
return [];
|
|
236
|
+
}
|
|
237
|
+
const slice = list.slice(0, remaining);
|
|
238
|
+
remaining -= slice.length;
|
|
239
|
+
return slice;
|
|
240
|
+
};
|
|
241
|
+
constructors = takeWithinLimit(constructors);
|
|
242
|
+
fields = takeWithinLimit(fields);
|
|
243
|
+
methods = takeWithinLimit(methods);
|
|
244
|
+
return {
|
|
245
|
+
fallback: {
|
|
246
|
+
constructors,
|
|
247
|
+
fields,
|
|
248
|
+
methods,
|
|
249
|
+
origin: "source-extracted"
|
|
250
|
+
},
|
|
251
|
+
counts: {
|
|
252
|
+
constructors: constructors.length,
|
|
253
|
+
fields: fields.length,
|
|
254
|
+
methods: methods.length,
|
|
255
|
+
total: constructors.length + fields.length + methods.length
|
|
256
|
+
}
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
export function findClass(svc, input) {
|
|
260
|
+
const className = input.className.trim();
|
|
261
|
+
if (!className) {
|
|
262
|
+
throw createError({
|
|
263
|
+
code: ERROR_CODES.INVALID_INPUT,
|
|
264
|
+
message: "className must be non-empty."
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
const inputArtifactId = input.artifactId.trim();
|
|
268
|
+
if (!inputArtifactId) {
|
|
269
|
+
throw createError({
|
|
270
|
+
code: ERROR_CODES.INVALID_INPUT,
|
|
271
|
+
message: "artifactId must be non-empty."
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
const artifact = svc.getArtifact(inputArtifactId);
|
|
275
|
+
const artifactId = artifact.artifactId;
|
|
276
|
+
const limit = Math.max(1, Math.min(input.limit ?? 20, 200));
|
|
277
|
+
const warnings = [];
|
|
278
|
+
const isQualified = className.includes(".");
|
|
279
|
+
if (isQualified) {
|
|
280
|
+
const classPath = className.replace(/\./g, "/");
|
|
281
|
+
const result = svc.symbolsRepo.findScopedSymbols({
|
|
282
|
+
artifactId,
|
|
283
|
+
query: className.split(".").at(-1) ?? className,
|
|
284
|
+
match: "exact",
|
|
285
|
+
limit: 5000
|
|
286
|
+
});
|
|
287
|
+
const matches = result.items
|
|
288
|
+
.filter((row) => {
|
|
289
|
+
const isTypeSymbol = row.symbolKind === "class" || row.symbolKind === "interface" ||
|
|
290
|
+
row.symbolKind === "enum" || row.symbolKind === "record";
|
|
291
|
+
if (!isTypeSymbol)
|
|
292
|
+
return false;
|
|
293
|
+
const rowQualified = row.qualifiedName ?? row.filePath.replace(/\.java$/, "").replaceAll("/", ".");
|
|
294
|
+
return rowQualified === className || row.filePath === `${classPath}.java`;
|
|
295
|
+
})
|
|
296
|
+
.map((row) => ({
|
|
297
|
+
qualifiedName: row.qualifiedName ?? row.filePath.replace(/\.java$/, "").replaceAll("/", "."),
|
|
298
|
+
filePath: row.filePath,
|
|
299
|
+
line: row.line,
|
|
300
|
+
symbolKind: row.symbolKind
|
|
301
|
+
}))
|
|
302
|
+
.slice(0, limit);
|
|
303
|
+
const partialVanillaLookup = hasPartialNetMinecraftCoverage(artifact.qualityFlags) && looksLikeDeobfuscatedClassName(className);
|
|
304
|
+
const filteredMatches = partialVanillaLookup && matches.every((match) => !match.qualifiedName.startsWith("net.minecraft.") && !match.qualifiedName.startsWith("com.mojang."))
|
|
305
|
+
? []
|
|
306
|
+
: matches;
|
|
307
|
+
if (filteredMatches.length === 0 && partialVanillaLookup) {
|
|
308
|
+
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.`);
|
|
309
|
+
}
|
|
310
|
+
if (filteredMatches.length === 0 && artifact.mappingApplied === "obfuscated" && looksLikeDeobfuscatedClassName(className)) {
|
|
311
|
+
warnings.push(`No exact class symbol matched "${className}". ${obfuscatedNamespaceHint(className)}`);
|
|
312
|
+
}
|
|
313
|
+
return { matches: filteredMatches, total: filteredMatches.length, warnings };
|
|
314
|
+
}
|
|
315
|
+
const result = svc.symbolsRepo.findScopedSymbols({
|
|
316
|
+
artifactId,
|
|
317
|
+
query: className,
|
|
318
|
+
match: "exact",
|
|
319
|
+
limit: limit * 5
|
|
320
|
+
});
|
|
321
|
+
const matches = [];
|
|
322
|
+
for (const row of result.items) {
|
|
323
|
+
if (matches.length >= limit)
|
|
324
|
+
break;
|
|
325
|
+
const isTypeSymbol = row.symbolKind === "class" || row.symbolKind === "interface" ||
|
|
326
|
+
row.symbolKind === "enum" || row.symbolKind === "record";
|
|
327
|
+
if (!isTypeSymbol)
|
|
328
|
+
continue;
|
|
329
|
+
matches.push({
|
|
330
|
+
qualifiedName: row.qualifiedName ?? row.filePath.replace(/\.java$/, "").replaceAll("/", "."),
|
|
331
|
+
filePath: row.filePath,
|
|
332
|
+
line: row.line,
|
|
333
|
+
symbolKind: row.symbolKind
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
const partialVanillaLookup = hasPartialNetMinecraftCoverage(artifact.qualityFlags) && looksLikeDeobfuscatedClassName(className);
|
|
337
|
+
const filteredMatches = partialVanillaLookup && matches.every((match) => !match.qualifiedName.startsWith("net.minecraft.") && !match.qualifiedName.startsWith("com.mojang."))
|
|
338
|
+
? []
|
|
339
|
+
: matches;
|
|
340
|
+
if (filteredMatches.length === 0 && partialVanillaLookup) {
|
|
341
|
+
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.`);
|
|
342
|
+
}
|
|
343
|
+
if (filteredMatches.length === 0 && artifact.mappingApplied === "obfuscated" && looksLikeDeobfuscatedClassName(className)) {
|
|
344
|
+
warnings.push(`No exact class symbol matched "${className}". ${obfuscatedNamespaceHint(className)}`);
|
|
345
|
+
}
|
|
346
|
+
return { matches: filteredMatches, total: filteredMatches.length, warnings };
|
|
347
|
+
}
|
|
348
|
+
export async function getClassSource(svc, input) {
|
|
349
|
+
const className = input.className.trim();
|
|
350
|
+
if (!className) {
|
|
351
|
+
throw createError({
|
|
352
|
+
code: ERROR_CODES.INVALID_INPUT,
|
|
353
|
+
message: "className must be non-empty."
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
const mode = input.mode ?? "metadata";
|
|
357
|
+
const startLine = normalizeStrictPositiveInt(input.startLine, "startLine");
|
|
358
|
+
const endLine = normalizeStrictPositiveInt(input.endLine, "endLine");
|
|
359
|
+
let maxLines = normalizeStrictPositiveInt(input.maxLines, "maxLines");
|
|
360
|
+
const maxChars = normalizeStrictPositiveInt(input.maxChars, "maxChars");
|
|
361
|
+
const outputFile = normalizeOptionalString(input.outputFile);
|
|
362
|
+
if (mode === "snippet" && startLine == null && endLine == null && maxLines == null) {
|
|
363
|
+
maxLines = 200;
|
|
364
|
+
}
|
|
365
|
+
if (startLine != null && endLine != null && startLine > endLine) {
|
|
366
|
+
throw createError({
|
|
367
|
+
code: ERROR_CODES.INVALID_LINE_RANGE,
|
|
368
|
+
message: `Invalid line range: startLine (${startLine}) is greater than endLine (${endLine}).`,
|
|
369
|
+
details: { startLine, endLine }
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
const normalizedArtifactId = normalizeOptionalString(input.artifactId);
|
|
373
|
+
if (normalizedArtifactId && input.target) {
|
|
374
|
+
throw createError({
|
|
375
|
+
code: ERROR_CODES.INVALID_INPUT,
|
|
376
|
+
message: "artifactId and target are mutually exclusive.",
|
|
377
|
+
details: { artifactId: normalizedArtifactId, target: input.target }
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
let artifactId = normalizedArtifactId;
|
|
381
|
+
let origin = "local-jar";
|
|
382
|
+
let warnings = [];
|
|
383
|
+
let requestedMapping = normalizeMapping(input.mapping);
|
|
384
|
+
let mappingApplied = requestedMapping;
|
|
385
|
+
let provenance;
|
|
386
|
+
let qualityFlags = [];
|
|
387
|
+
let sourceJarPath;
|
|
388
|
+
let binaryJarPath;
|
|
389
|
+
let version;
|
|
390
|
+
let coordinate;
|
|
391
|
+
if (!artifactId) {
|
|
392
|
+
if (!input.target) {
|
|
393
|
+
throw createError({
|
|
394
|
+
code: ERROR_CODES.INVALID_INPUT,
|
|
395
|
+
message: "Either artifactId or target must be provided."
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
const resolved = await svc.resolveArtifact({
|
|
399
|
+
target: input.target,
|
|
400
|
+
mapping: input.mapping,
|
|
401
|
+
sourcePriority: input.sourcePriority,
|
|
402
|
+
allowDecompile: input.allowDecompile,
|
|
403
|
+
projectPath: input.projectPath,
|
|
404
|
+
scope: input.scope,
|
|
405
|
+
preferProjectVersion: input.preferProjectVersion,
|
|
406
|
+
strictVersion: input.strictVersion
|
|
407
|
+
});
|
|
408
|
+
artifactId = resolved.artifactId;
|
|
409
|
+
origin = resolved.origin;
|
|
410
|
+
warnings = [...resolved.warnings];
|
|
411
|
+
requestedMapping = resolved.requestedMapping;
|
|
412
|
+
mappingApplied = resolved.mappingApplied;
|
|
413
|
+
provenance = resolved.provenance;
|
|
414
|
+
qualityFlags = [...resolved.qualityFlags];
|
|
415
|
+
sourceJarPath = resolved.resolvedSourceJarPath;
|
|
416
|
+
binaryJarPath = resolved.binaryJarPath;
|
|
417
|
+
version = resolved.version;
|
|
418
|
+
coordinate = resolved.coordinate;
|
|
419
|
+
}
|
|
420
|
+
else {
|
|
421
|
+
const artifact = svc.getArtifact(artifactId);
|
|
422
|
+
artifactId = artifact.artifactId;
|
|
423
|
+
origin = artifact.origin;
|
|
424
|
+
requestedMapping = artifact.requestedMapping ?? requestedMapping;
|
|
425
|
+
mappingApplied = artifact.mappingApplied ?? requestedMapping;
|
|
426
|
+
provenance = artifact.provenance;
|
|
427
|
+
qualityFlags = artifact.qualityFlags;
|
|
428
|
+
sourceJarPath = artifact.sourceJarPath;
|
|
429
|
+
binaryJarPath = artifact.binaryJarPath;
|
|
430
|
+
version = artifact.version;
|
|
431
|
+
coordinate = artifact.coordinate;
|
|
432
|
+
}
|
|
433
|
+
version = await artifactResolver.resolveVersionContext(svc, {
|
|
434
|
+
version,
|
|
435
|
+
provenance,
|
|
436
|
+
coordinate,
|
|
437
|
+
projectPath: input.projectPath,
|
|
438
|
+
preferProjectVersion: input.preferProjectVersion,
|
|
439
|
+
warnings
|
|
440
|
+
});
|
|
441
|
+
let activeArtifactId = artifactId;
|
|
442
|
+
let activeOrigin = origin;
|
|
443
|
+
let activeProvenance = provenance;
|
|
444
|
+
let activeQualityFlags = [...qualityFlags];
|
|
445
|
+
let activeMappingApplied = mappingApplied;
|
|
446
|
+
let activeSourceJarPath = sourceJarPath;
|
|
447
|
+
let attemptedBinaryFallback = false;
|
|
448
|
+
const tryBinaryFallback = async () => {
|
|
449
|
+
if (attemptedBinaryFallback) {
|
|
450
|
+
return false;
|
|
451
|
+
}
|
|
452
|
+
attemptedBinaryFallback = true;
|
|
453
|
+
const normalizedBinaryJarPath = normalizeOptionalString(binaryJarPath);
|
|
454
|
+
if (!normalizedBinaryJarPath) {
|
|
455
|
+
return false;
|
|
456
|
+
}
|
|
457
|
+
if (activeSourceJarPath &&
|
|
458
|
+
normalizePathStyle(activeSourceJarPath) === normalizePathStyle(normalizedBinaryJarPath)) {
|
|
459
|
+
return false;
|
|
460
|
+
}
|
|
461
|
+
const fallbackResolved = await svc.resolveBinaryFallbackArtifact({
|
|
462
|
+
binaryJarPath: normalizedBinaryJarPath,
|
|
463
|
+
version,
|
|
464
|
+
coordinate,
|
|
465
|
+
requestedMapping,
|
|
466
|
+
mappingApplied,
|
|
467
|
+
provenance: activeProvenance,
|
|
468
|
+
qualityFlags: activeQualityFlags
|
|
469
|
+
});
|
|
470
|
+
if (!fallbackResolved || fallbackResolved.artifactId === activeArtifactId) {
|
|
471
|
+
return false;
|
|
472
|
+
}
|
|
473
|
+
activeArtifactId = fallbackResolved.artifactId;
|
|
474
|
+
activeOrigin = fallbackResolved.origin;
|
|
475
|
+
activeMappingApplied = fallbackResolved.mappingApplied ?? activeMappingApplied;
|
|
476
|
+
activeProvenance = fallbackResolved.provenance ?? activeProvenance;
|
|
477
|
+
activeQualityFlags = dedupeQualityFlags([...(fallbackResolved.qualityFlags ?? []), "binary-fallback"]);
|
|
478
|
+
activeSourceJarPath = fallbackResolved.sourceJarPath;
|
|
479
|
+
warnings.push(`Falling back to binary artifact "${normalizedBinaryJarPath}" because source coverage for "${className}" was incomplete.`);
|
|
480
|
+
if (activeMappingApplied !== requestedMapping) {
|
|
481
|
+
warnings.push(`Fallback source text is indexed in ${activeMappingApplied} names; returned source is not remapped to ${requestedMapping}.`);
|
|
482
|
+
}
|
|
483
|
+
return true;
|
|
484
|
+
};
|
|
485
|
+
let activeLookupClassName = await svc.resolveClassNameForLookup({
|
|
486
|
+
className,
|
|
487
|
+
version,
|
|
488
|
+
sourceMapping: requestedMapping,
|
|
489
|
+
targetMapping: activeMappingApplied,
|
|
490
|
+
sourcePriority: input.sourcePriority,
|
|
491
|
+
warnings,
|
|
492
|
+
context: "source lookup"
|
|
493
|
+
});
|
|
494
|
+
let filePath = resolveClassFilePath(svc, activeArtifactId, activeLookupClassName);
|
|
495
|
+
if (!filePath && (await tryBinaryFallback())) {
|
|
496
|
+
activeLookupClassName = await svc.resolveClassNameForLookup({
|
|
497
|
+
className,
|
|
498
|
+
version,
|
|
499
|
+
sourceMapping: requestedMapping,
|
|
500
|
+
targetMapping: activeMappingApplied,
|
|
501
|
+
sourcePriority: input.sourcePriority,
|
|
502
|
+
warnings,
|
|
503
|
+
context: "source lookup"
|
|
504
|
+
});
|
|
505
|
+
filePath = resolveClassFilePath(svc, activeArtifactId, activeLookupClassName);
|
|
506
|
+
}
|
|
507
|
+
if (!filePath) {
|
|
508
|
+
throw buildClassSourceNotFoundError(svc, {
|
|
509
|
+
artifactId: activeArtifactId,
|
|
510
|
+
className,
|
|
511
|
+
lookupClassName: activeLookupClassName,
|
|
512
|
+
mappingApplied: activeMappingApplied,
|
|
513
|
+
requestedMapping,
|
|
514
|
+
qualityFlags: activeQualityFlags,
|
|
515
|
+
attemptedBinaryFallback,
|
|
516
|
+
targetKind: input.target?.kind,
|
|
517
|
+
targetValue: input.target && "value" in input.target ? input.target.value : undefined,
|
|
518
|
+
scope: input.scope,
|
|
519
|
+
projectPath: input.projectPath,
|
|
520
|
+
version
|
|
521
|
+
});
|
|
522
|
+
}
|
|
523
|
+
let row = svc.filesRepo.getFileContent(activeArtifactId, filePath);
|
|
524
|
+
if (!row && (await tryBinaryFallback())) {
|
|
525
|
+
activeLookupClassName = await svc.resolveClassNameForLookup({
|
|
526
|
+
className,
|
|
527
|
+
version,
|
|
528
|
+
sourceMapping: requestedMapping,
|
|
529
|
+
targetMapping: activeMappingApplied,
|
|
530
|
+
sourcePriority: input.sourcePriority,
|
|
531
|
+
warnings,
|
|
532
|
+
context: "source lookup"
|
|
533
|
+
});
|
|
534
|
+
filePath = resolveClassFilePath(svc, activeArtifactId, activeLookupClassName) ?? filePath;
|
|
535
|
+
row = svc.filesRepo.getFileContent(activeArtifactId, filePath);
|
|
536
|
+
}
|
|
537
|
+
if (!row) {
|
|
538
|
+
throw buildClassSourceNotFoundError(svc, {
|
|
539
|
+
artifactId: activeArtifactId,
|
|
540
|
+
className,
|
|
541
|
+
lookupClassName: activeLookupClassName,
|
|
542
|
+
mappingApplied: activeMappingApplied,
|
|
543
|
+
requestedMapping,
|
|
544
|
+
qualityFlags: activeQualityFlags,
|
|
545
|
+
attemptedBinaryFallback,
|
|
546
|
+
filePath,
|
|
547
|
+
targetKind: input.target?.kind,
|
|
548
|
+
targetValue: input.target && "value" in input.target ? input.target.value : undefined,
|
|
549
|
+
scope: input.scope,
|
|
550
|
+
projectPath: input.projectPath,
|
|
551
|
+
version
|
|
552
|
+
});
|
|
553
|
+
}
|
|
554
|
+
const snippet = buildClassSourceSnippet({
|
|
555
|
+
filePath,
|
|
556
|
+
content: row.content,
|
|
557
|
+
mode,
|
|
558
|
+
startLine,
|
|
559
|
+
endLine,
|
|
560
|
+
maxLines,
|
|
561
|
+
maxChars
|
|
562
|
+
});
|
|
563
|
+
let sourceText = snippet.sourceText;
|
|
564
|
+
const totalLines = snippet.totalLines;
|
|
565
|
+
const returnedStart = snippet.returnedStart;
|
|
566
|
+
const returnedEnd = snippet.returnedEnd;
|
|
567
|
+
const truncated = snippet.truncated;
|
|
568
|
+
const charsTruncated = snippet.charsTruncated;
|
|
569
|
+
let resolvedOutputFile;
|
|
570
|
+
if (outputFile) {
|
|
571
|
+
const outputPath = isAbsolute(outputFile)
|
|
572
|
+
? outputFile
|
|
573
|
+
: resolvePath(outputFile);
|
|
574
|
+
await writeFile(outputPath, sourceText, "utf8");
|
|
575
|
+
resolvedOutputFile = outputPath;
|
|
576
|
+
sourceText = `[Written to ${outputPath}]`;
|
|
577
|
+
}
|
|
578
|
+
const normalizedProvenance = activeProvenance ??
|
|
579
|
+
buildFallbackProvenance(svc, {
|
|
580
|
+
artifactId: activeArtifactId,
|
|
581
|
+
origin: activeOrigin,
|
|
582
|
+
requestedMapping,
|
|
583
|
+
mappingApplied: activeMappingApplied
|
|
584
|
+
});
|
|
585
|
+
return {
|
|
586
|
+
className,
|
|
587
|
+
mode,
|
|
588
|
+
sourceText,
|
|
589
|
+
totalLines,
|
|
590
|
+
returnedRange: {
|
|
591
|
+
start: returnedStart,
|
|
592
|
+
end: returnedEnd
|
|
593
|
+
},
|
|
594
|
+
truncated,
|
|
595
|
+
...(charsTruncated ? { charsTruncated } : {}),
|
|
596
|
+
origin: activeOrigin,
|
|
597
|
+
artifactId: activeArtifactId,
|
|
598
|
+
requestedMapping,
|
|
599
|
+
mappingApplied: activeMappingApplied,
|
|
600
|
+
returnedNamespace: activeMappingApplied,
|
|
601
|
+
provenance: normalizedProvenance,
|
|
602
|
+
qualityFlags: activeQualityFlags,
|
|
603
|
+
artifactContents: svc.buildArtifactContentsSummary({
|
|
604
|
+
origin: activeOrigin,
|
|
605
|
+
sourceJarPath: activeSourceJarPath,
|
|
606
|
+
isDecompiled: activeOrigin === "decompiled",
|
|
607
|
+
qualityFlags: activeQualityFlags
|
|
608
|
+
}),
|
|
609
|
+
...(resolvedOutputFile ? { outputFile: resolvedOutputFile } : {}),
|
|
610
|
+
warnings
|
|
611
|
+
};
|
|
612
|
+
}
|
|
613
|
+
export async function getClassMembers(svc, input) {
|
|
614
|
+
const className = input.className.trim();
|
|
615
|
+
if (!className) {
|
|
616
|
+
throw createError({
|
|
617
|
+
code: ERROR_CODES.INVALID_INPUT,
|
|
618
|
+
message: "className must be non-empty."
|
|
619
|
+
});
|
|
620
|
+
}
|
|
621
|
+
let requestedMapping = normalizeMapping(input.mapping);
|
|
622
|
+
const access = normalizeMemberAccess(input.access);
|
|
623
|
+
const includeSynthetic = input.includeSynthetic ?? false;
|
|
624
|
+
const includeInherited = input.includeInherited ?? false;
|
|
625
|
+
const memberPattern = normalizeOptionalString(input.memberPattern);
|
|
626
|
+
const parsedMaxMembers = normalizeStrictPositiveInt(input.maxMembers, "maxMembers");
|
|
627
|
+
const maxMembers = parsedMaxMembers == null ? 500 : Math.min(parsedMaxMembers, 5000);
|
|
628
|
+
const normalizedArtifactId = normalizeOptionalString(input.artifactId);
|
|
629
|
+
if (normalizedArtifactId && input.target) {
|
|
630
|
+
throw createError({
|
|
631
|
+
code: ERROR_CODES.INVALID_INPUT,
|
|
632
|
+
message: "artifactId and target are mutually exclusive.",
|
|
633
|
+
details: { artifactId: normalizedArtifactId, target: input.target }
|
|
634
|
+
});
|
|
635
|
+
}
|
|
636
|
+
let artifactId = normalizedArtifactId;
|
|
637
|
+
let origin = "local-jar";
|
|
638
|
+
let warnings = [];
|
|
639
|
+
let mappingApplied = requestedMapping;
|
|
640
|
+
let provenance;
|
|
641
|
+
let qualityFlags = [];
|
|
642
|
+
let binaryJarPath;
|
|
643
|
+
let sourceJarPath;
|
|
644
|
+
let coordinate;
|
|
645
|
+
if (parsedMaxMembers != null && parsedMaxMembers > 5000) {
|
|
646
|
+
warnings.push(`maxMembers was clamped to 5000 from ${parsedMaxMembers}.`);
|
|
647
|
+
}
|
|
648
|
+
let version;
|
|
649
|
+
if (!artifactId) {
|
|
650
|
+
if (!input.target) {
|
|
651
|
+
throw createError({
|
|
652
|
+
code: ERROR_CODES.INVALID_INPUT,
|
|
653
|
+
message: "Either artifactId or target must be provided."
|
|
654
|
+
});
|
|
655
|
+
}
|
|
656
|
+
const resolved = await svc.resolveArtifact({
|
|
657
|
+
target: input.target,
|
|
658
|
+
mapping: input.mapping,
|
|
659
|
+
sourcePriority: input.sourcePriority,
|
|
660
|
+
allowDecompile: input.allowDecompile,
|
|
661
|
+
projectPath: input.projectPath,
|
|
662
|
+
scope: input.scope,
|
|
663
|
+
preferProjectVersion: input.preferProjectVersion,
|
|
664
|
+
strictVersion: input.strictVersion
|
|
665
|
+
});
|
|
666
|
+
artifactId = resolved.artifactId;
|
|
667
|
+
origin = resolved.origin;
|
|
668
|
+
warnings.push(...resolved.warnings);
|
|
669
|
+
requestedMapping = resolved.requestedMapping;
|
|
670
|
+
mappingApplied = resolved.mappingApplied;
|
|
671
|
+
provenance = resolved.provenance;
|
|
672
|
+
qualityFlags = [...resolved.qualityFlags];
|
|
673
|
+
binaryJarPath = resolved.binaryJarPath;
|
|
674
|
+
sourceJarPath = resolved.resolvedSourceJarPath;
|
|
675
|
+
version = resolved.version;
|
|
676
|
+
coordinate = resolved.coordinate;
|
|
677
|
+
}
|
|
678
|
+
else {
|
|
679
|
+
const artifact = svc.getArtifact(artifactId);
|
|
680
|
+
artifactId = artifact.artifactId;
|
|
681
|
+
origin = artifact.origin;
|
|
682
|
+
mappingApplied = artifact.mappingApplied ?? requestedMapping;
|
|
683
|
+
provenance = artifact.provenance;
|
|
684
|
+
qualityFlags = artifact.qualityFlags;
|
|
685
|
+
binaryJarPath = artifact.binaryJarPath;
|
|
686
|
+
sourceJarPath = artifact.sourceJarPath;
|
|
687
|
+
version = artifact.version;
|
|
688
|
+
coordinate = artifact.coordinate;
|
|
689
|
+
}
|
|
690
|
+
version = await artifactResolver.resolveVersionContext(svc, {
|
|
691
|
+
version,
|
|
692
|
+
provenance,
|
|
693
|
+
coordinate,
|
|
694
|
+
projectPath: input.projectPath,
|
|
695
|
+
preferProjectVersion: input.preferProjectVersion,
|
|
696
|
+
warnings
|
|
697
|
+
});
|
|
698
|
+
if (requestedMapping !== "obfuscated" && !version) {
|
|
699
|
+
throw createError({
|
|
700
|
+
code: ERROR_CODES.MAPPING_NOT_APPLIED,
|
|
701
|
+
message: `Non-obfuscated mapping "${requestedMapping}" requires a version, but none was resolved.`,
|
|
702
|
+
details: {
|
|
703
|
+
mapping: requestedMapping,
|
|
704
|
+
nextAction: "Resolve with target: { kind: \"version\", value: ... } or specify a versioned coordinate.",
|
|
705
|
+
...buildSuggestedCall({
|
|
706
|
+
tool: "resolve-artifact",
|
|
707
|
+
params: buildResolveArtifactParams({ kind: "version", value: "latest" })
|
|
708
|
+
})
|
|
709
|
+
}
|
|
710
|
+
});
|
|
711
|
+
}
|
|
712
|
+
if (!binaryJarPath) {
|
|
713
|
+
throw createError({
|
|
714
|
+
code: ERROR_CODES.CONTEXT_UNRESOLVED,
|
|
715
|
+
message: `Class members require a binary jar, but artifact "${artifactId}" has no binaryJarPath.`,
|
|
716
|
+
details: {
|
|
717
|
+
artifactId,
|
|
718
|
+
className,
|
|
719
|
+
nextAction: "Resolve with target: { kind: \"jar\" | \"version\", value: ... } or use an artifact that has a binary jar."
|
|
720
|
+
}
|
|
721
|
+
});
|
|
722
|
+
}
|
|
723
|
+
const lookupClassName = await svc.resolveClassNameForLookup({
|
|
724
|
+
className,
|
|
725
|
+
version,
|
|
726
|
+
sourceMapping: requestedMapping,
|
|
727
|
+
targetMapping: mappingApplied,
|
|
728
|
+
sourcePriority: input.sourcePriority,
|
|
729
|
+
warnings,
|
|
730
|
+
context: "binary lookup"
|
|
731
|
+
});
|
|
732
|
+
let signatureContext;
|
|
733
|
+
let signatureConstructors;
|
|
734
|
+
let signatureFields;
|
|
735
|
+
let signatureMethods;
|
|
736
|
+
let binaryExtractionFailed = false;
|
|
737
|
+
let binaryExtractionFailureReason;
|
|
738
|
+
try {
|
|
739
|
+
const signature = await svc.explorerService.getSignature({
|
|
740
|
+
fqn: lookupClassName,
|
|
741
|
+
jarPath: binaryJarPath,
|
|
742
|
+
access,
|
|
743
|
+
includeSynthetic,
|
|
744
|
+
includeInherited,
|
|
745
|
+
memberPattern: requestedMapping === mappingApplied ? memberPattern : undefined
|
|
746
|
+
});
|
|
747
|
+
warnings.push(...signature.warnings);
|
|
748
|
+
signatureContext = signature.context;
|
|
749
|
+
signatureConstructors = signature.constructors;
|
|
750
|
+
signatureFields = signature.fields;
|
|
751
|
+
signatureMethods = signature.methods;
|
|
752
|
+
}
|
|
753
|
+
catch (error) {
|
|
754
|
+
if (isAppError(error) && error.code === ERROR_CODES.CLASS_NOT_FOUND) {
|
|
755
|
+
throw error;
|
|
756
|
+
}
|
|
757
|
+
binaryExtractionFailed = true;
|
|
758
|
+
binaryExtractionFailureReason = error instanceof Error ? error.message : String(error);
|
|
759
|
+
signatureContext = {
|
|
760
|
+
minecraftVersion: version ?? "unknown",
|
|
761
|
+
mappingType: "unknown",
|
|
762
|
+
mappingNamespace: mappingApplied === "intermediary" ? "obfuscated" : mappingApplied,
|
|
763
|
+
jarHash: "",
|
|
764
|
+
generatedAt: new Date().toISOString()
|
|
765
|
+
};
|
|
766
|
+
signatureConstructors = [];
|
|
767
|
+
signatureFields = [];
|
|
768
|
+
signatureMethods = [];
|
|
769
|
+
}
|
|
770
|
+
const remapped = await remapAndCountMembers(svc, {
|
|
771
|
+
signatureConstructors,
|
|
772
|
+
signatureFields,
|
|
773
|
+
signatureMethods,
|
|
774
|
+
version,
|
|
775
|
+
mappingApplied,
|
|
776
|
+
requestedMapping,
|
|
777
|
+
sourcePriority: input.sourcePriority,
|
|
778
|
+
memberPattern,
|
|
779
|
+
warnings
|
|
780
|
+
});
|
|
781
|
+
const counts = remapped.counts;
|
|
782
|
+
const sliced = sliceMembersWithLimit(remapped, counts.total, maxMembers, warnings);
|
|
783
|
+
const constructors = sliced.constructors;
|
|
784
|
+
const fields = sliced.fields;
|
|
785
|
+
const methods = sliced.methods;
|
|
786
|
+
const truncated = sliced.truncated;
|
|
787
|
+
const normalizedProvenance = provenance ??
|
|
788
|
+
buildFallbackProvenance(svc, {
|
|
789
|
+
artifactId,
|
|
790
|
+
origin,
|
|
791
|
+
requestedMapping,
|
|
792
|
+
mappingApplied
|
|
793
|
+
});
|
|
794
|
+
let decompiledFallback;
|
|
795
|
+
let decompiledMemberCounts;
|
|
796
|
+
let fallbackQualityFlags = qualityFlags;
|
|
797
|
+
if (counts.total === 0) {
|
|
798
|
+
const namespaceMismatch = requestedMapping !== mappingApplied;
|
|
799
|
+
const fallbackPattern = namespaceMismatch ? undefined : memberPattern;
|
|
800
|
+
const sourceFallback = buildDecompiledFallback(svc, artifactId, lookupClassName, fallbackPattern, maxMembers);
|
|
801
|
+
if (sourceFallback) {
|
|
802
|
+
decompiledFallback = sourceFallback.fallback;
|
|
803
|
+
decompiledMemberCounts = sourceFallback.counts;
|
|
804
|
+
fallbackQualityFlags = dedupeQualityFlags([
|
|
805
|
+
...qualityFlags,
|
|
806
|
+
"members-from-decompiled-source"
|
|
807
|
+
]);
|
|
808
|
+
const namespaceNote = namespaceMismatch
|
|
809
|
+
? ` Member names are in ${mappingApplied} (artifact namespace); the request asked for ${requestedMapping}.`
|
|
810
|
+
: "";
|
|
811
|
+
warnings.push("Bytecode member enumeration returned zero; populated decompiledFallback from decompiled source. "
|
|
812
|
+
+ "Descriptors and access modifiers are unavailable — use get-class-source for full details."
|
|
813
|
+
+ namespaceNote);
|
|
814
|
+
if (namespaceMismatch && memberPattern) {
|
|
815
|
+
warnings.push(`memberPattern="${memberPattern}" was not applied to decompiledFallback because the artifact namespace (${mappingApplied}) differs from the requested namespace (${requestedMapping}); filter the response client-side after mapping.`);
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
let statusFields = {};
|
|
820
|
+
if (!MEMBERS_STATUS_LEGACY) {
|
|
821
|
+
let status;
|
|
822
|
+
let unavailableReason;
|
|
823
|
+
let suggestedCall;
|
|
824
|
+
if (counts.total > 0) {
|
|
825
|
+
status = "ok";
|
|
826
|
+
}
|
|
827
|
+
else if (decompiledFallback) {
|
|
828
|
+
status = "partial";
|
|
829
|
+
}
|
|
830
|
+
else if (binaryExtractionFailed) {
|
|
831
|
+
status = "members_unavailable";
|
|
832
|
+
unavailableReason =
|
|
833
|
+
binaryExtractionFailureReason
|
|
834
|
+
?? `binary extraction failed for "${className}".`;
|
|
835
|
+
suggestedCall = buildSuggestedCall({
|
|
836
|
+
tool: "get-class-source",
|
|
837
|
+
params: {
|
|
838
|
+
target: { type: "artifact", artifactId },
|
|
839
|
+
className,
|
|
840
|
+
mode: "snippet",
|
|
841
|
+
mapping: requestedMapping
|
|
842
|
+
}
|
|
843
|
+
}).suggestedCall;
|
|
844
|
+
}
|
|
845
|
+
else {
|
|
846
|
+
status = "ok";
|
|
847
|
+
}
|
|
848
|
+
statusFields = {
|
|
849
|
+
status,
|
|
850
|
+
...(unavailableReason ? { unavailableReason } : {}),
|
|
851
|
+
...(suggestedCall ? { suggestedCall } : {})
|
|
852
|
+
};
|
|
853
|
+
}
|
|
854
|
+
return {
|
|
855
|
+
className,
|
|
856
|
+
members: {
|
|
857
|
+
constructors,
|
|
858
|
+
fields,
|
|
859
|
+
methods
|
|
860
|
+
},
|
|
861
|
+
counts,
|
|
862
|
+
truncated,
|
|
863
|
+
context: signatureContext,
|
|
864
|
+
origin,
|
|
865
|
+
artifactId,
|
|
866
|
+
requestedMapping,
|
|
867
|
+
mappingApplied,
|
|
868
|
+
returnedNamespace: requestedMapping,
|
|
869
|
+
provenance: normalizedProvenance,
|
|
870
|
+
qualityFlags: fallbackQualityFlags,
|
|
871
|
+
artifactContents: svc.buildArtifactContentsSummary({
|
|
872
|
+
origin,
|
|
873
|
+
sourceJarPath,
|
|
874
|
+
isDecompiled: origin === "decompiled",
|
|
875
|
+
qualityFlags: fallbackQualityFlags
|
|
876
|
+
}),
|
|
877
|
+
...(decompiledFallback ? { decompiledFallback } : {}),
|
|
878
|
+
...(decompiledMemberCounts ? { decompiledMemberCounts } : {}),
|
|
879
|
+
...statusFields,
|
|
880
|
+
warnings
|
|
881
|
+
};
|
|
882
|
+
}
|
|
883
|
+
//# sourceMappingURL=class-source.js.map
|