@adhisang/minecraft-modding-mcp 1.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.
Files changed (106) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/LICENSE +21 -0
  3. package/README.md +765 -0
  4. package/dist/access-widener-parser.d.ts +24 -0
  5. package/dist/access-widener-parser.js +77 -0
  6. package/dist/cli.d.ts +2 -0
  7. package/dist/cli.js +4 -0
  8. package/dist/config.d.ts +27 -0
  9. package/dist/config.js +178 -0
  10. package/dist/decompiler/vineflower.d.ts +15 -0
  11. package/dist/decompiler/vineflower.js +185 -0
  12. package/dist/errors.d.ts +50 -0
  13. package/dist/errors.js +49 -0
  14. package/dist/hash.d.ts +1 -0
  15. package/dist/hash.js +12 -0
  16. package/dist/index.d.ts +7 -0
  17. package/dist/index.js +1447 -0
  18. package/dist/java-process.d.ts +16 -0
  19. package/dist/java-process.js +120 -0
  20. package/dist/logger.d.ts +3 -0
  21. package/dist/logger.js +21 -0
  22. package/dist/mapping-pipeline-service.d.ts +18 -0
  23. package/dist/mapping-pipeline-service.js +60 -0
  24. package/dist/mapping-service.d.ts +161 -0
  25. package/dist/mapping-service.js +1706 -0
  26. package/dist/maven-resolver.d.ts +22 -0
  27. package/dist/maven-resolver.js +122 -0
  28. package/dist/minecraft-explorer-service.d.ts +43 -0
  29. package/dist/minecraft-explorer-service.js +562 -0
  30. package/dist/mixin-parser.d.ts +34 -0
  31. package/dist/mixin-parser.js +194 -0
  32. package/dist/mixin-validator.d.ts +59 -0
  33. package/dist/mixin-validator.js +274 -0
  34. package/dist/mod-analyzer.d.ts +23 -0
  35. package/dist/mod-analyzer.js +346 -0
  36. package/dist/mod-decompile-service.d.ts +39 -0
  37. package/dist/mod-decompile-service.js +136 -0
  38. package/dist/mod-remap-service.d.ts +17 -0
  39. package/dist/mod-remap-service.js +186 -0
  40. package/dist/mod-search-service.d.ts +28 -0
  41. package/dist/mod-search-service.js +174 -0
  42. package/dist/mojang-tiny-mapping-service.d.ts +13 -0
  43. package/dist/mojang-tiny-mapping-service.js +351 -0
  44. package/dist/nbt/java-nbt-codec.d.ts +3 -0
  45. package/dist/nbt/java-nbt-codec.js +385 -0
  46. package/dist/nbt/json-patch.d.ts +3 -0
  47. package/dist/nbt/json-patch.js +352 -0
  48. package/dist/nbt/pipeline.d.ts +39 -0
  49. package/dist/nbt/pipeline.js +173 -0
  50. package/dist/nbt/typed-json.d.ts +10 -0
  51. package/dist/nbt/typed-json.js +205 -0
  52. package/dist/nbt/types.d.ts +66 -0
  53. package/dist/nbt/types.js +2 -0
  54. package/dist/observability.d.ts +88 -0
  55. package/dist/observability.js +165 -0
  56. package/dist/path-converter.d.ts +12 -0
  57. package/dist/path-converter.js +161 -0
  58. package/dist/path-resolver.d.ts +19 -0
  59. package/dist/path-resolver.js +78 -0
  60. package/dist/registry-service.d.ts +29 -0
  61. package/dist/registry-service.js +214 -0
  62. package/dist/repo-downloader.d.ts +15 -0
  63. package/dist/repo-downloader.js +111 -0
  64. package/dist/resources.d.ts +3 -0
  65. package/dist/resources.js +154 -0
  66. package/dist/search-hit-accumulator.d.ts +38 -0
  67. package/dist/search-hit-accumulator.js +153 -0
  68. package/dist/source-jar-reader.d.ts +13 -0
  69. package/dist/source-jar-reader.js +216 -0
  70. package/dist/source-resolver.d.ts +14 -0
  71. package/dist/source-resolver.js +274 -0
  72. package/dist/source-service.d.ts +404 -0
  73. package/dist/source-service.js +2881 -0
  74. package/dist/storage/artifacts-repo.d.ts +45 -0
  75. package/dist/storage/artifacts-repo.js +209 -0
  76. package/dist/storage/db.d.ts +14 -0
  77. package/dist/storage/db.js +132 -0
  78. package/dist/storage/files-repo.d.ts +78 -0
  79. package/dist/storage/files-repo.js +437 -0
  80. package/dist/storage/index-meta-repo.d.ts +35 -0
  81. package/dist/storage/index-meta-repo.js +97 -0
  82. package/dist/storage/migrations.d.ts +11 -0
  83. package/dist/storage/migrations.js +71 -0
  84. package/dist/storage/schema.d.ts +1 -0
  85. package/dist/storage/schema.js +160 -0
  86. package/dist/storage/sqlite.d.ts +20 -0
  87. package/dist/storage/sqlite.js +111 -0
  88. package/dist/storage/symbols-repo.d.ts +63 -0
  89. package/dist/storage/symbols-repo.js +401 -0
  90. package/dist/symbols/symbol-extractor.d.ts +7 -0
  91. package/dist/symbols/symbol-extractor.js +64 -0
  92. package/dist/tiny-remapper-resolver.d.ts +1 -0
  93. package/dist/tiny-remapper-resolver.js +62 -0
  94. package/dist/tiny-remapper-service.d.ts +16 -0
  95. package/dist/tiny-remapper-service.js +73 -0
  96. package/dist/types.d.ts +120 -0
  97. package/dist/types.js +2 -0
  98. package/dist/version-diff-service.d.ts +41 -0
  99. package/dist/version-diff-service.js +222 -0
  100. package/dist/version-service.d.ts +70 -0
  101. package/dist/version-service.js +411 -0
  102. package/dist/vineflower-resolver.d.ts +1 -0
  103. package/dist/vineflower-resolver.js +62 -0
  104. package/dist/workspace-mapping-service.d.ts +18 -0
  105. package/dist/workspace-mapping-service.js +89 -0
  106. package/package.json +61 -0
@@ -0,0 +1,351 @@
1
+ import { existsSync } from "node:fs";
2
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
3
+ import { dirname, join } from "node:path";
4
+ import { createError, ERROR_CODES } from "./errors.js";
5
+ import { log } from "./logger.js";
6
+ import { downloadToCache } from "./repo-downloader.js";
7
+ import { VersionService } from "./version-service.js";
8
+ function stripLineInfo(input) {
9
+ let value = input.trim();
10
+ while (/^\d+:\d+:/.test(value)) {
11
+ value = value.replace(/^\d+:\d+:/, "");
12
+ }
13
+ return value.replace(/:\d+:\d+$/, "").trim();
14
+ }
15
+ function normalizeFqn(value) {
16
+ return value.trim().replace(/\//g, ".");
17
+ }
18
+ function normalizeInternalName(value) {
19
+ return value.trim().replace(/\./g, "/");
20
+ }
21
+ function parseProguardMappings(text) {
22
+ const classes = [];
23
+ const members = [];
24
+ let currentClass;
25
+ for (const rawLine of text.split(/\r?\n/)) {
26
+ const line = rawLine.trim();
27
+ if (!line || line.startsWith("#")) {
28
+ continue;
29
+ }
30
+ const classMatch = /^(.+?)\s+->\s+(.+):$/.exec(line);
31
+ if (classMatch) {
32
+ const mojangFqn = normalizeFqn(classMatch[1] ?? "");
33
+ const officialInternal = normalizeInternalName(classMatch[2] ?? "");
34
+ if (!mojangFqn || !officialInternal) {
35
+ currentClass = undefined;
36
+ continue;
37
+ }
38
+ currentClass = mojangFqn;
39
+ classes.push({ mojangFqn, officialInternal });
40
+ continue;
41
+ }
42
+ if (!currentClass) {
43
+ continue;
44
+ }
45
+ const arrowIndex = line.indexOf(" -> ");
46
+ if (arrowIndex < 0) {
47
+ continue;
48
+ }
49
+ const leftRaw = stripLineInfo(line.slice(0, arrowIndex));
50
+ const officialName = line.slice(arrowIndex + 4).trim();
51
+ if (!leftRaw || !officialName) {
52
+ continue;
53
+ }
54
+ members.push({
55
+ ownerMojangFqn: currentClass,
56
+ leftSignature: leftRaw,
57
+ officialName
58
+ });
59
+ }
60
+ return { classes, members };
61
+ }
62
+ function stripGenericTypes(value) {
63
+ let depth = 0;
64
+ let output = "";
65
+ for (const char of value) {
66
+ if (char === "<") {
67
+ depth += 1;
68
+ continue;
69
+ }
70
+ if (char === ">") {
71
+ depth = Math.max(0, depth - 1);
72
+ continue;
73
+ }
74
+ if (depth === 0) {
75
+ output += char;
76
+ }
77
+ }
78
+ return output;
79
+ }
80
+ function splitMethodArgs(argList) {
81
+ const trimmed = argList.trim();
82
+ if (!trimmed) {
83
+ return [];
84
+ }
85
+ const parts = [];
86
+ let depth = 0;
87
+ let current = "";
88
+ for (const char of trimmed) {
89
+ if (char === "<") {
90
+ depth += 1;
91
+ current += char;
92
+ continue;
93
+ }
94
+ if (char === ">") {
95
+ depth = Math.max(0, depth - 1);
96
+ current += char;
97
+ continue;
98
+ }
99
+ if (char === "," && depth === 0) {
100
+ if (current.trim()) {
101
+ parts.push(current.trim());
102
+ }
103
+ current = "";
104
+ continue;
105
+ }
106
+ current += char;
107
+ }
108
+ if (current.trim()) {
109
+ parts.push(current.trim());
110
+ }
111
+ return parts;
112
+ }
113
+ function typeToDescriptor(typeName, classMap) {
114
+ const primitive = {
115
+ byte: "B",
116
+ char: "C",
117
+ double: "D",
118
+ float: "F",
119
+ int: "I",
120
+ long: "J",
121
+ short: "S",
122
+ boolean: "Z",
123
+ void: "V"
124
+ };
125
+ let value = stripGenericTypes(typeName).trim();
126
+ if (!value) {
127
+ return undefined;
128
+ }
129
+ value = value.replace(/\s+/g, "");
130
+ let dimensions = 0;
131
+ while (value.endsWith("[]")) {
132
+ dimensions += 1;
133
+ value = value.slice(0, -2);
134
+ }
135
+ if (value.endsWith("...")) {
136
+ dimensions += 1;
137
+ value = value.slice(0, -3);
138
+ }
139
+ const primitiveDescriptor = primitive[value];
140
+ if (primitiveDescriptor) {
141
+ return `${"[".repeat(dimensions)}${primitiveDescriptor}`;
142
+ }
143
+ const normalized = normalizeFqn(value);
144
+ const internal = classMap.get(normalized) ?? normalized.replace(/\./g, "/");
145
+ return `${"[".repeat(dimensions)}L${internal};`;
146
+ }
147
+ function buildMethodDescriptor(returnType, params, classMap) {
148
+ const paramDescriptors = [];
149
+ for (const param of params) {
150
+ const descriptor = typeToDescriptor(param, classMap);
151
+ if (!descriptor) {
152
+ return undefined;
153
+ }
154
+ paramDescriptors.push(descriptor);
155
+ }
156
+ const returnDescriptor = typeToDescriptor(returnType, classMap);
157
+ if (!returnDescriptor) {
158
+ return undefined;
159
+ }
160
+ return `(${paramDescriptors.join("")})${returnDescriptor}`;
161
+ }
162
+ function normalizeMemberMappings(rawMappings, classMap, warnings) {
163
+ const membersByOwner = new Map();
164
+ const seen = new Set();
165
+ for (const mapping of rawMappings) {
166
+ for (const member of mapping.members) {
167
+ const ownerOfficial = classMap.get(normalizeFqn(member.ownerMojangFqn));
168
+ if (!ownerOfficial) {
169
+ warnings.push(`Skipping member mapping for "${member.ownerMojangFqn}" because class mapping is missing.`);
170
+ continue;
171
+ }
172
+ const methodMatch = /^(.+?)\s+([^\s(]+)\((.*)\)$/.exec(member.leftSignature);
173
+ let record;
174
+ if (methodMatch) {
175
+ const returnType = methodMatch[1]?.trim() ?? "";
176
+ const mojangName = methodMatch[2]?.trim() ?? "";
177
+ const params = splitMethodArgs(methodMatch[3] ?? "");
178
+ const descriptor = buildMethodDescriptor(returnType, params, classMap);
179
+ if (!mojangName || !descriptor) {
180
+ warnings.push(`Skipping method mapping "${member.leftSignature}" because descriptor conversion failed.`);
181
+ continue;
182
+ }
183
+ record = {
184
+ kind: "m",
185
+ descriptor,
186
+ officialName: member.officialName,
187
+ mojangName
188
+ };
189
+ }
190
+ else {
191
+ const fieldMatch = /^(.+?)\s+([^\s]+)$/.exec(member.leftSignature);
192
+ if (!fieldMatch) {
193
+ warnings.push(`Skipping unsupported member mapping syntax "${member.leftSignature}".`);
194
+ continue;
195
+ }
196
+ const fieldType = fieldMatch[1]?.trim() ?? "";
197
+ const mojangName = fieldMatch[2]?.trim() ?? "";
198
+ const descriptor = typeToDescriptor(fieldType, classMap);
199
+ if (!mojangName || !descriptor) {
200
+ warnings.push(`Skipping field mapping "${member.leftSignature}" because descriptor conversion failed.`);
201
+ continue;
202
+ }
203
+ record = {
204
+ kind: "f",
205
+ descriptor,
206
+ officialName: member.officialName,
207
+ mojangName
208
+ };
209
+ }
210
+ const dedupeKey = `${ownerOfficial}|${record.kind}|${record.officialName}|` +
211
+ `${record.mojangName}|${record.descriptor}`;
212
+ if (seen.has(dedupeKey)) {
213
+ continue;
214
+ }
215
+ seen.add(dedupeKey);
216
+ const list = membersByOwner.get(ownerOfficial) ?? [];
217
+ list.push(record);
218
+ membersByOwner.set(ownerOfficial, list);
219
+ }
220
+ }
221
+ return membersByOwner;
222
+ }
223
+ function mergeClasses(rawMappings, warnings) {
224
+ const classMap = new Map();
225
+ for (const mapping of rawMappings) {
226
+ for (const clazz of mapping.classes) {
227
+ const mojang = normalizeFqn(clazz.mojangFqn);
228
+ const official = normalizeInternalName(clazz.officialInternal);
229
+ const existing = classMap.get(mojang);
230
+ if (existing && existing !== official) {
231
+ warnings.push(`Conflicting class mapping for "${mojang}" (${existing} vs ${official}); keeping first.`);
232
+ continue;
233
+ }
234
+ classMap.set(mojang, official);
235
+ }
236
+ }
237
+ return classMap;
238
+ }
239
+ function renderTinyV2(classMap, membersByOwner) {
240
+ const classEntries = [...classMap.entries()]
241
+ .map(([mojangFqn, officialInternal]) => ({
242
+ officialInternal,
243
+ mojangInternal: mojangFqn.replace(/\./g, "/")
244
+ }))
245
+ .sort((left, right) => left.officialInternal.localeCompare(right.officialInternal));
246
+ const lines = ["tiny\t2\t0\tofficial\tmojang"];
247
+ for (const entry of classEntries) {
248
+ lines.push(`c\t${entry.officialInternal}\t${entry.mojangInternal}`);
249
+ const members = [...(membersByOwner.get(entry.officialInternal) ?? [])].sort((left, right) => {
250
+ if (left.kind !== right.kind) {
251
+ return left.kind.localeCompare(right.kind);
252
+ }
253
+ if (left.officialName !== right.officialName) {
254
+ return left.officialName.localeCompare(right.officialName);
255
+ }
256
+ return left.descriptor.localeCompare(right.descriptor);
257
+ });
258
+ for (const member of members) {
259
+ lines.push(`\t${member.kind}\t${member.descriptor}\t${member.officialName}\t${member.mojangName}`);
260
+ }
261
+ }
262
+ return `${lines.join("\n")}\n`;
263
+ }
264
+ async function ensureMappingsFile(label, version, url, config, fetchFn) {
265
+ if (!url) {
266
+ return { warning: `${label} mappings URL is not available for ${version}.` };
267
+ }
268
+ const destination = join(config.cacheDir, "mappings", version, `${label}_mappings.txt`);
269
+ if (!existsSync(destination)) {
270
+ await mkdir(dirname(destination), { recursive: true });
271
+ const downloaded = await downloadToCache(url, destination, {
272
+ fetchFn,
273
+ retries: config.fetchRetries,
274
+ timeoutMs: config.fetchTimeoutMs
275
+ });
276
+ if (!downloaded.ok || !downloaded.path) {
277
+ return {
278
+ warning: `Failed to download ${label} mappings for ${version} from "${url}" ` +
279
+ `(status: ${downloaded.statusCode ?? "unknown"}).`
280
+ };
281
+ }
282
+ }
283
+ return { path: destination };
284
+ }
285
+ function resolveMappingsUrls(metadata) {
286
+ return {
287
+ clientUrl: metadata.clientMappingsUrl ?? metadata.mappingsUrl,
288
+ serverUrl: metadata.serverMappingsUrl
289
+ };
290
+ }
291
+ export async function resolveMojangTinyFile(version, config, deps = {}) {
292
+ const cachedTiny = join(config.cacheDir, "mappings", `${version}-mojang-merged.tiny`);
293
+ if (existsSync(cachedTiny)) {
294
+ return { path: cachedTiny, warnings: [] };
295
+ }
296
+ const fetchFn = deps.fetchFn ?? globalThis.fetch;
297
+ const versionService = deps.versionService ?? new VersionService(config, fetchFn);
298
+ const metadata = await versionService.resolveVersionMappings(version);
299
+ const { clientUrl, serverUrl } = resolveMappingsUrls(metadata);
300
+ if (!clientUrl && !serverUrl) {
301
+ throw createError({
302
+ code: ERROR_CODES.MAPPING_UNAVAILABLE,
303
+ message: `Minecraft version "${version}" does not expose Mojang mappings URLs.`,
304
+ details: { version, versionDetailUrl: metadata.versionDetailUrl }
305
+ });
306
+ }
307
+ const warnings = [];
308
+ const [clientResult, serverResult] = await Promise.all([
309
+ ensureMappingsFile("client", version, clientUrl, config, fetchFn),
310
+ ensureMappingsFile("server", version, serverUrl, config, fetchFn)
311
+ ]);
312
+ if (clientResult.warning) {
313
+ warnings.push(clientResult.warning);
314
+ }
315
+ if (serverResult.warning) {
316
+ warnings.push(serverResult.warning);
317
+ }
318
+ const sourcePaths = [clientResult.path, serverResult.path].filter((value) => typeof value === "string");
319
+ if (sourcePaths.length === 0) {
320
+ throw createError({
321
+ code: ERROR_CODES.MAPPING_UNAVAILABLE,
322
+ message: `Failed to retrieve Mojang mappings for "${version}".`,
323
+ details: { version, clientUrl, serverUrl }
324
+ });
325
+ }
326
+ const parsedMappings = [];
327
+ for (const path of sourcePaths) {
328
+ const content = await readFile(path, "utf8");
329
+ parsedMappings.push(parseProguardMappings(content));
330
+ }
331
+ const classMap = mergeClasses(parsedMappings, warnings);
332
+ if (classMap.size === 0) {
333
+ throw createError({
334
+ code: ERROR_CODES.MAPPING_UNAVAILABLE,
335
+ message: `No class mappings could be parsed for "${version}".`,
336
+ details: { version, sourcePaths }
337
+ });
338
+ }
339
+ const membersByOwner = normalizeMemberMappings(parsedMappings, classMap, warnings);
340
+ const tinyContent = renderTinyV2(classMap, membersByOwner);
341
+ await mkdir(dirname(cachedTiny), { recursive: true });
342
+ await writeFile(cachedTiny, tinyContent, "utf8");
343
+ log("info", "mojang-tiny.generated", {
344
+ version,
345
+ path: cachedTiny,
346
+ classes: classMap.size,
347
+ warnings: warnings.length
348
+ });
349
+ return { path: cachedTiny, warnings };
350
+ }
351
+ //# sourceMappingURL=mojang-tiny-mapping-service.js.map
@@ -0,0 +1,3 @@
1
+ import { type TypedNbtDocument } from "./typed-json.js";
2
+ export declare function decodeJavaNbt(buffer: Buffer): TypedNbtDocument;
3
+ export declare function encodeJavaNbt(document: TypedNbtDocument): Buffer;