@adhisang/minecraft-modding-mcp 4.1.1 → 4.2.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.
Files changed (73) hide show
  1. package/CHANGELOG.md +14 -2
  2. package/README.md +6 -2
  3. package/dist/cache-registry.js +84 -5
  4. package/dist/entry-tools/analyze-symbol-service.d.ts +12 -0
  5. package/dist/entry-tools/analyze-symbol-service.js +7 -0
  6. package/dist/entry-tools/batch-class-members-service.d.ts +1 -0
  7. package/dist/entry-tools/batch-class-members-service.js +2 -0
  8. package/dist/entry-tools/batch-class-source-service.d.ts +1 -0
  9. package/dist/entry-tools/batch-class-source-service.js +2 -0
  10. package/dist/entry-tools/batch-mappings-service.d.ts +1 -0
  11. package/dist/entry-tools/batch-mappings-service.js +1 -0
  12. package/dist/entry-tools/batch-symbol-exists-service.d.ts +1 -0
  13. package/dist/entry-tools/batch-symbol-exists-service.js +2 -0
  14. package/dist/entry-tools/compare-minecraft-service.d.ts +9 -0
  15. package/dist/entry-tools/compare-minecraft-service.js +3 -1
  16. package/dist/entry-tools/inspect-minecraft/handlers/class-members.js +1 -0
  17. package/dist/entry-tools/inspect-minecraft/handlers/class-overview.js +3 -1
  18. package/dist/entry-tools/inspect-minecraft/handlers/class-source.js +1 -0
  19. package/dist/entry-tools/inspect-minecraft/handlers/list-files.js +1 -0
  20. package/dist/entry-tools/inspect-minecraft/handlers/search.js +2 -1
  21. package/dist/entry-tools/inspect-minecraft/internal.d.ts +17 -0
  22. package/dist/entry-tools/inspect-minecraft/internal.js +10 -0
  23. package/dist/entry-tools/inspect-minecraft-service.d.ts +40 -0
  24. package/dist/entry-tools/validate-project/cases/project-summary.js +8 -3
  25. package/dist/entry-tools/validate-project/internal.d.ts +6 -2
  26. package/dist/entry-tools/validate-project/internal.js +9 -7
  27. package/dist/entry-tools/validate-project-service.d.ts +10 -0
  28. package/dist/entry-tools/validate-project-service.js +1 -0
  29. package/dist/entry-tools/verify-mixin-target-service.d.ts +2 -0
  30. package/dist/entry-tools/verify-mixin-target-service.js +1 -0
  31. package/dist/errors.js +4 -1
  32. package/dist/gradle-paths.d.ts +8 -3
  33. package/dist/gradle-paths.js +34 -5
  34. package/dist/index.js +24 -7
  35. package/dist/java-process.d.ts +9 -0
  36. package/dist/java-process.js +11 -5
  37. package/dist/mapping/loaders/tiny-loom.d.ts +1 -1
  38. package/dist/mapping/loaders/tiny-loom.js +5 -2
  39. package/dist/mapping/types.d.ts +5 -0
  40. package/dist/mapping-service.d.ts +1 -0
  41. package/dist/mapping-service.js +16 -13
  42. package/dist/mixin-validator.d.ts +1 -1
  43. package/dist/mixin-validator.js +1 -1
  44. package/dist/nbt/java-nbt-codec.js +81 -2
  45. package/dist/source/access-validate.js +12 -8
  46. package/dist/source/artifact-resolver.d.ts +5 -0
  47. package/dist/source/artifact-resolver.js +23 -7
  48. package/dist/source/cache-metrics.js +2 -2
  49. package/dist/source/class-source/members-builder.d.ts +1 -0
  50. package/dist/source/class-source/members-builder.js +1 -1
  51. package/dist/source/class-source.d.ts +1 -0
  52. package/dist/source/class-source.js +9 -1
  53. package/dist/source/indexer.d.ts +31 -6
  54. package/dist/source/indexer.js +90 -23
  55. package/dist/source/lifecycle/diff.js +6 -6
  56. package/dist/source/lifecycle/mapping-helpers.d.ts +3 -3
  57. package/dist/source/lifecycle/mapping-helpers.js +17 -9
  58. package/dist/source/lifecycle/trace.js +2 -2
  59. package/dist/source/search.js +1 -0
  60. package/dist/source/symbol-resolver.js +4 -1
  61. package/dist/source/validate-mixin/pipeline/mapping-health.js +2 -1
  62. package/dist/source/validate-mixin/pipeline/resolve.js +1 -0
  63. package/dist/source/validate-mixin/pipeline/target-lookup.js +12 -7
  64. package/dist/source-service.d.ts +18 -1
  65. package/dist/source-service.js +2 -2
  66. package/dist/tiny-remapper-service.js +3 -3
  67. package/dist/tool-contract-manifest.d.ts +1 -1
  68. package/dist/tool-contract-manifest.js +2 -2
  69. package/dist/tool-schemas.d.ts +91 -0
  70. package/dist/tool-schemas.js +20 -0
  71. package/docs/README-ja.md +2 -2
  72. package/docs/tool-reference.md +5 -1
  73. package/package.json +1 -1
@@ -3,6 +3,7 @@ import { mkdir, writeFile } from "node:fs/promises";
3
3
  import { dirname, join } from "node:path";
4
4
  import { buildSuggestedCall } from "./build-suggested-call.js";
5
5
  import { createError, ERROR_CODES } from "./errors.js";
6
+ import { resolveGradleUserHomePath } from "./gradle-paths.js";
6
7
  import { defaultDownloadPath, downloadToCache } from "./repo-downloader.js";
7
8
  import { collectMatchedJarEntriesAsUtf8 } from "./source-jar-reader.js";
8
9
  import { VersionService, isUnobfuscatedVersion } from "./version-service.js";
@@ -112,7 +113,7 @@ export class MappingService {
112
113
  warnings: []
113
114
  };
114
115
  }
115
- const graph = await this.loadGraph(version, priority, requiresOnlyObfuscatedMojangGraph(sourceMapping, targetMapping) ? "obfuscated-mojang-only" : "full", input.projectPath);
116
+ const graph = await this.loadGraph(version, priority, requiresOnlyObfuscatedMojangGraph(sourceMapping, targetMapping) ? "obfuscated-mojang-only" : "full", input.projectPath, input.gradleUserHome);
116
117
  const path = namespacePath(graph, sourceMapping, targetMapping);
117
118
  if (!path) {
118
119
  return {
@@ -254,7 +255,7 @@ export class MappingService {
254
255
  warnings: []
255
256
  };
256
257
  }
257
- const graph = await this.loadGraph(version, priority, requiresOnlyObfuscatedMojangGraph(sourceMapping, targetMapping) ? "obfuscated-mojang-only" : "full", input.projectPath);
258
+ const graph = await this.loadGraph(version, priority, requiresOnlyObfuscatedMojangGraph(sourceMapping, targetMapping) ? "obfuscated-mojang-only" : "full", input.projectPath, input.gradleUserHome);
258
259
  const path = namespacePath(graph, sourceMapping, targetMapping);
259
260
  if (!path) {
260
261
  throw createError({
@@ -338,7 +339,7 @@ export class MappingService {
338
339
  warnings: []
339
340
  };
340
341
  }
341
- const graph = await this.loadGraph(version, priority, requiresOnlyObfuscatedMojangGraph(sourceMapping, targetMapping) ? "obfuscated-mojang-only" : "full", input.projectPath);
342
+ const graph = await this.loadGraph(version, priority, requiresOnlyObfuscatedMojangGraph(sourceMapping, targetMapping) ? "obfuscated-mojang-only" : "full", input.projectPath, input.gradleUserHome);
342
343
  const path = namespacePath(graph, sourceMapping, targetMapping);
343
344
  if (!path) {
344
345
  return {
@@ -448,7 +449,7 @@ export class MappingService {
448
449
  });
449
450
  }
450
451
  const priority = mappingPriorityFromInput(this.config.mappingSourcePriority, input.sourcePriority);
451
- const graph = await this.loadGraph(version, priority, "full");
452
+ const graph = await this.loadGraph(version, priority, "full", undefined, input.gradleUserHome);
452
453
  const warnings = [...graph.warnings];
453
454
  const includeKinds = normalizeIncludedKinds(input.includeKinds);
454
455
  const pathCache = new Map();
@@ -669,7 +670,7 @@ export class MappingService {
669
670
  querySymbol
670
671
  };
671
672
  })();
672
- const graph = await this.loadGraph(version, priority, sourceMapping === "mojang" ? "obfuscated-mojang-only" : "full");
673
+ const graph = await this.loadGraph(version, priority, sourceMapping === "mojang" ? "obfuscated-mojang-only" : "full", undefined, input.gradleUserHome);
673
674
  const warnings = [...graph.warnings];
674
675
  const records = collectTargetRecords(graph, sourceMapping);
675
676
  if (records.length === 0) {
@@ -950,7 +951,7 @@ export class MappingService {
950
951
  }
951
952
  let graph;
952
953
  try {
953
- graph = await this.loadGraph(input.version, priority, needsTinyMappings ? "full" : "obfuscated-mojang-only");
954
+ graph = await this.loadGraph(input.version, priority, needsTinyMappings ? "full" : "obfuscated-mojang-only", undefined, input.gradleUserHome);
954
955
  }
955
956
  catch {
956
957
  return {
@@ -994,9 +995,10 @@ export class MappingService {
994
995
  degradations
995
996
  };
996
997
  }
997
- async loadGraph(version, priority, mode, projectPath) {
998
+ async loadGraph(version, priority, mode, projectPath, gradleUserHome) {
998
999
  const effectiveProjectPath = effectiveLoomSearchProjectPath(projectPath);
999
- const cacheKey = `${version}|${priority}|${mode}|${effectiveProjectPath ?? ""}`;
1000
+ const effectiveGradleUserHome = resolveGradleUserHomePath(gradleUserHome);
1001
+ const cacheKey = `${version}|${priority}|${mode}|${effectiveProjectPath ?? ""}|${effectiveGradleUserHome}`;
1000
1002
  const cached = this.graphCache.get(cacheKey);
1001
1003
  if (cached) {
1002
1004
  this.graphCache.delete(cacheKey);
@@ -1007,7 +1009,7 @@ export class MappingService {
1007
1009
  if (existingLock) {
1008
1010
  return existingLock;
1009
1011
  }
1010
- const buildPromise = this.buildGraph(version, priority, mode, effectiveProjectPath);
1012
+ const buildPromise = this.buildGraph(version, priority, mode, effectiveProjectPath, effectiveGradleUserHome);
1011
1013
  this.buildLocks.set(cacheKey, buildPromise);
1012
1014
  try {
1013
1015
  const built = await buildPromise;
@@ -1019,7 +1021,7 @@ export class MappingService {
1019
1021
  this.buildLocks.delete(cacheKey);
1020
1022
  }
1021
1023
  }
1022
- async buildGraph(version, priority, mode, projectPath) {
1024
+ async buildGraph(version, priority, mode, projectPath, gradleUserHome) {
1023
1025
  if (isUnobfuscatedVersion(version)) {
1024
1026
  return {
1025
1027
  version,
@@ -1052,7 +1054,7 @@ export class MappingService {
1052
1054
  const deferredTinyWarnings = [];
1053
1055
  for (const source of mappingSourceOrder(priority)) {
1054
1056
  const tinyLoad = source === "loom-cache"
1055
- ? await this.loadTinyPairsFromLoom(version, projectPath)
1057
+ ? await this.loadTinyPairsFromLoom(version, projectPath, gradleUserHome)
1056
1058
  : await this.loadTinyPairsFromMaven(version);
1057
1059
  if (tinyLoad.pairs.size === 0) {
1058
1060
  deferredTinyWarnings.push(...tinyLoad.warnings);
@@ -1095,8 +1097,8 @@ export class MappingService {
1095
1097
  async loadMojangPairs(version) {
1096
1098
  return loadMojangPairs(this.loaderDeps(), version);
1097
1099
  }
1098
- async loadTinyPairsFromLoom(version, projectPath) {
1099
- return loadTinyPairsFromLoom(version, projectPath);
1100
+ async loadTinyPairsFromLoom(version, projectPath, gradleUserHome) {
1101
+ return loadTinyPairsFromLoom(version, projectPath, gradleUserHome);
1100
1102
  }
1101
1103
  async loadTinyPairsFromMaven(version) {
1102
1104
  return loadTinyPairsFromMaven(this.loaderDeps(), version);
@@ -1152,6 +1154,7 @@ export class MappingService {
1152
1154
  input.targetMapping,
1153
1155
  input.sourcePriority ?? "",
1154
1156
  effectiveLoomSearchProjectPath(input.projectPath) ?? "",
1157
+ resolveGradleUserHomePath(input.gradleUserHome),
1155
1158
  effectiveSignatureMode,
1156
1159
  String(input.maxCandidates ?? ""),
1157
1160
  JSON.stringify(input.disambiguation ?? "")
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Public barrel for the mixin validation engine. The implementation lives
3
3
  * under `src/mixin/`; this file preserves the historical entry point used
4
- * by source-service, entry tools, and tests.
4
+ * by source-service, top-level workflow tools, and tests.
5
5
  */
6
6
  export type { AccessTransformerValidationResult, AccessWidenerValidationResult, AggregatedWarningGroup, ConfidenceBreakdown, ConfidencePenalty, IssueCategory, IssueConfidence, MappingHealthReport, MixinStageBudgets, MixinValidationProvenance, MixinValidationResult, ResolutionPath, ResolvedMember, ResolvedTargetMembers, StructuredWarning, TargetOutcome, ValidationIssue, ValidationStatus, ValidationSummary } from "./mixin/types.js";
7
7
  export { loadMixinStageBudgets } from "./mixin/types.js";
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Public barrel for the mixin validation engine. The implementation lives
3
3
  * under `src/mixin/`; this file preserves the historical entry point used
4
- * by source-service, entry tools, and tests.
4
+ * by source-service, top-level workflow tools, and tests.
5
5
  */
6
6
  export { loadMixinStageBudgets } from "./mixin/types.js";
7
7
  export { accessLevelFromFlags, buildQuickSummary, computeConfidenceBreakdown, computeFalsePositiveRisk, computeValidationStatus, extractMethodDescriptor, extractMethodName, levenshteinDistance, refreshMixinValidationOutcome, suggestSimilar, summarizeResolvedMembers } from "./mixin/helpers.js";
@@ -37,6 +37,85 @@ function resolveTagName(tagId, pointer) {
37
37
  }
38
38
  return tagName;
39
39
  }
40
+ // Java NBT TAG_String uses MUTF-8 (Mojang's on-disk format matches the JVM
41
+ // `DataInput/OutputStream.{read,write}UTF` contract), which differs from UTF-8
42
+ // for two ranges: `U+0000` encodes as `C0 80`, and supplementary code points
43
+ // (`U+10000`+) encode as two 3-byte sequences for the UTF-16 surrogate pair
44
+ // (6 bytes total). All other characters share the UTF-8 byte sequence.
45
+ function mutf8ByteLength(value) {
46
+ let length = 0;
47
+ for (let i = 0; i < value.length; i += 1) {
48
+ const c = value.charCodeAt(i);
49
+ if (c === 0x0000) {
50
+ length += 2;
51
+ }
52
+ else if (c <= 0x007f) {
53
+ length += 1;
54
+ }
55
+ else if (c <= 0x07ff) {
56
+ length += 2;
57
+ }
58
+ else {
59
+ length += 3;
60
+ }
61
+ }
62
+ return length;
63
+ }
64
+ function encodeMutf8(value) {
65
+ const buffer = Buffer.allocUnsafe(mutf8ByteLength(value));
66
+ let offset = 0;
67
+ for (let i = 0; i < value.length; i += 1) {
68
+ const c = value.charCodeAt(i);
69
+ if (c === 0x0000) {
70
+ buffer[offset++] = 0xc0;
71
+ buffer[offset++] = 0x80;
72
+ }
73
+ else if (c <= 0x007f) {
74
+ buffer[offset++] = c;
75
+ }
76
+ else if (c <= 0x07ff) {
77
+ buffer[offset++] = 0xc0 | (c >> 6);
78
+ buffer[offset++] = 0x80 | (c & 0x3f);
79
+ }
80
+ else {
81
+ buffer[offset++] = 0xe0 | (c >> 12);
82
+ buffer[offset++] = 0x80 | ((c >> 6) & 0x3f);
83
+ buffer[offset++] = 0x80 | (c & 0x3f);
84
+ }
85
+ }
86
+ return buffer;
87
+ }
88
+ function decodeMutf8(buffer, start, end) {
89
+ const codeUnits = [];
90
+ let i = start;
91
+ while (i < end) {
92
+ const b0 = buffer[i++];
93
+ if ((b0 & 0x80) === 0) {
94
+ codeUnits.push(b0);
95
+ }
96
+ else if ((b0 & 0xe0) === 0xc0) {
97
+ if (i >= end) {
98
+ throw parseError("Truncated MUTF-8 2-byte sequence.", { offset: i - 1 });
99
+ }
100
+ const b1 = buffer[i++];
101
+ codeUnits.push(((b0 & 0x1f) << 6) | (b1 & 0x3f));
102
+ }
103
+ else if ((b0 & 0xf0) === 0xe0) {
104
+ if (i + 1 >= end) {
105
+ throw parseError("Truncated MUTF-8 3-byte sequence.", { offset: i - 1 });
106
+ }
107
+ const b1 = buffer[i++];
108
+ const b2 = buffer[i++];
109
+ codeUnits.push(((b0 & 0x0f) << 12) | ((b1 & 0x3f) << 6) | (b2 & 0x3f));
110
+ }
111
+ else {
112
+ throw parseError("Invalid MUTF-8 lead byte.", { byte: b0, offset: i - 1 });
113
+ }
114
+ }
115
+ // NBT TAG_String is bounded by uint16 length so codeUnits.length <= 65535,
116
+ // safely within String.fromCharCode's argument cap.
117
+ return String.fromCharCode(...codeUnits);
118
+ }
40
119
  class NbtReader {
41
120
  buffer;
42
121
  offset = 0;
@@ -103,7 +182,7 @@ class NbtReader {
103
182
  readString() {
104
183
  const byteLength = this.readUInt16();
105
184
  this.ensure(byteLength);
106
- const value = this.buffer.toString("utf8", this.offset, this.offset + byteLength);
185
+ const value = decodeMutf8(this.buffer, this.offset, this.offset + byteLength);
107
186
  this.offset += byteLength;
108
187
  return value;
109
188
  }
@@ -154,7 +233,7 @@ class NbtWriter {
154
233
  this.chunks.push(chunk);
155
234
  }
156
235
  writeString(value) {
157
- const encoded = Buffer.from(value, "utf8");
236
+ const encoded = encodeMutf8(value);
158
237
  if (encoded.length > 0xffff) {
159
238
  throw encodeError("NBT string length exceeds uint16.", { byteLength: encoded.length });
160
239
  }
@@ -56,6 +56,7 @@ export async function validateAccessWidener(svc, input) {
56
56
  version,
57
57
  awNamespace,
58
58
  projectPath: input.projectPath,
59
+ gradleUserHome: input.gradleUserHome,
59
60
  scope: input.scope,
60
61
  preferProjectVersion: input.preferProjectVersion
61
62
  });
@@ -85,7 +86,8 @@ export async function validateAccessWidener(svc, input) {
85
86
  sourceMapping: awNamespace,
86
87
  targetMapping: lookupMapping,
87
88
  sourcePriority: input.sourcePriority,
88
- projectPath: input.projectPath
89
+ projectPath: input.projectPath,
90
+ gradleUserHome: input.gradleUserHome
89
91
  });
90
92
  if (mapped.resolved && mapped.resolvedSymbol) {
91
93
  lookupFqn = mapped.resolvedSymbol.name;
@@ -110,9 +112,9 @@ export async function validateAccessWidener(svc, input) {
110
112
  let fields = sig.fields;
111
113
  if (needsLookupMapping) {
112
114
  const [ctorResult, methodResult, fieldResult] = await Promise.all([
113
- svc.remapSignatureMembers(sig.constructors, "method", resolvedVersion, lookupMapping, awNamespace, input.sourcePriority, warnings, input.projectPath),
114
- svc.remapSignatureMembers(sig.methods, "method", resolvedVersion, lookupMapping, awNamespace, input.sourcePriority, warnings, input.projectPath),
115
- svc.remapSignatureMembers(sig.fields, "field", resolvedVersion, lookupMapping, awNamespace, input.sourcePriority, warnings, input.projectPath)
115
+ svc.remapSignatureMembers(sig.constructors, "method", resolvedVersion, lookupMapping, awNamespace, input.sourcePriority, warnings, input.projectPath, input.gradleUserHome),
116
+ svc.remapSignatureMembers(sig.methods, "method", resolvedVersion, lookupMapping, awNamespace, input.sourcePriority, warnings, input.projectPath, input.gradleUserHome),
117
+ svc.remapSignatureMembers(sig.fields, "field", resolvedVersion, lookupMapping, awNamespace, input.sourcePriority, warnings, input.projectPath, input.gradleUserHome)
116
118
  ]);
117
119
  constructors = ctorResult.members;
118
120
  methods = methodResult.members;
@@ -163,6 +165,7 @@ export async function validateAccessTransformer(svc, input) {
163
165
  version,
164
166
  atNamespace,
165
167
  projectPath: input.projectPath,
168
+ gradleUserHome: input.gradleUserHome,
166
169
  scope: input.scope,
167
170
  preferProjectVersion: input.preferProjectVersion
168
171
  });
@@ -197,7 +200,8 @@ export async function validateAccessTransformer(svc, input) {
197
200
  sourceMapping: atNamespace,
198
201
  targetMapping: lookupMapping,
199
202
  sourcePriority: input.sourcePriority,
200
- projectPath: input.projectPath
203
+ projectPath: input.projectPath,
204
+ gradleUserHome: input.gradleUserHome
201
205
  });
202
206
  if (mapped.resolved && mapped.resolvedSymbol) {
203
207
  lookupFqn = mapped.resolvedSymbol.name;
@@ -223,9 +227,9 @@ export async function validateAccessTransformer(svc, input) {
223
227
  let fields = sig.fields;
224
228
  if (needsLookupMapping && isSourceMappingNamespace(atNamespace) && isSourceMappingNamespace(lookupMapping)) {
225
229
  const [ctorResult, methodResult, fieldResult] = await Promise.all([
226
- svc.remapSignatureMembers(sig.constructors, "method", resolvedVersion, lookupMapping, atNamespace, input.sourcePriority, warnings, input.projectPath),
227
- svc.remapSignatureMembers(sig.methods, "method", resolvedVersion, lookupMapping, atNamespace, input.sourcePriority, warnings, input.projectPath),
228
- svc.remapSignatureMembers(sig.fields, "field", resolvedVersion, lookupMapping, atNamespace, input.sourcePriority, warnings, input.projectPath)
230
+ svc.remapSignatureMembers(sig.constructors, "method", resolvedVersion, lookupMapping, atNamespace, input.sourcePriority, warnings, input.projectPath, input.gradleUserHome),
231
+ svc.remapSignatureMembers(sig.methods, "method", resolvedVersion, lookupMapping, atNamespace, input.sourcePriority, warnings, input.projectPath, input.gradleUserHome),
232
+ svc.remapSignatureMembers(sig.fields, "field", resolvedVersion, lookupMapping, atNamespace, input.sourcePriority, warnings, input.projectPath, input.gradleUserHome)
229
233
  ]);
230
234
  constructors = ctorResult.members;
231
235
  methods = methodResult.members;
@@ -32,11 +32,13 @@ export declare function hasExactVersionToken(path: string, version: string): boo
32
32
  export declare function discoverVersionSourceJar(_svc: SourceService, input: {
33
33
  version: string;
34
34
  projectPath?: string;
35
+ gradleUserHome?: string;
35
36
  }): Promise<VersionSourceDiscovery>;
36
37
  export declare function probeMinecraftArtifact(svc: SourceService, input: ProbeMinecraftArtifactInput): Promise<ProbeMinecraftArtifactOutput>;
37
38
  export declare function discoverAccessWidenerRuntimeCandidates(_svc: SourceService, input: {
38
39
  version: string;
39
40
  projectPath?: string;
41
+ gradleUserHome?: string;
40
42
  requestedScope: ArtifactScope;
41
43
  }): Promise<{
42
44
  searchedPaths: string[];
@@ -46,6 +48,7 @@ export declare function discoverAccessWidenerRuntimeCandidates(_svc: SourceServi
46
48
  export declare function discoverAccessTransformerRuntimeCandidates(_svc: SourceService, input: {
47
49
  version: string;
48
50
  projectPath?: string;
51
+ gradleUserHome?: string;
49
52
  requestedScope: ArtifactScope;
50
53
  atNamespace: AccessTransformerNamespace;
51
54
  loader: WorkspaceProjectLoader | "unknown";
@@ -58,6 +61,7 @@ export declare function resolveAccessWidenerRuntimeArtifact(svc: SourceService,
58
61
  version: string;
59
62
  awNamespace: SourceMapping;
60
63
  projectPath?: string;
64
+ gradleUserHome?: string;
61
65
  scope?: ArtifactScope;
62
66
  preferProjectVersion?: boolean;
63
67
  }): Promise<RuntimeValidationProvenance<SourceMapping>>;
@@ -69,6 +73,7 @@ export declare function resolveAccessTransformerRuntimeArtifact(svc: SourceServi
69
73
  version: string;
70
74
  atNamespace: AccessTransformerNamespace;
71
75
  projectPath?: string;
76
+ gradleUserHome?: string;
72
77
  scope?: ArtifactScope;
73
78
  preferProjectVersion?: boolean;
74
79
  }): Promise<RuntimeValidationProvenance<AccessTransformerNamespace>>;
@@ -138,7 +138,10 @@ function buildProvenance(input) {
138
138
  }
139
139
  export async function discoverVersionSourceJar(_svc, input) {
140
140
  const normalizedProjectPath = normalizeOptionalProjectPath(input.projectPath);
141
- const searchRoots = buildVersionSourceSearchRoots(normalizedProjectPath);
141
+ const searchRoots = buildVersionSourceSearchRoots({
142
+ projectPath: normalizedProjectPath,
143
+ gradleUserHome: input.gradleUserHome
144
+ });
142
145
  const searchedPaths = [];
143
146
  const candidates = [];
144
147
  const seen = new Set();
@@ -267,7 +270,8 @@ export async function probeMinecraftArtifact(svc, input) {
267
270
  }
268
271
  const versionSourceDiscovery = await svc.discoverVersionSourceJar({
269
272
  version: resolvedVersion,
270
- projectPath: input.projectPath
273
+ projectPath: input.projectPath,
274
+ gradleUserHome: input.gradleUserHome
271
275
  });
272
276
  if (!versionSourceDiscovery.selectedSourceJarPath) {
273
277
  throw createError({
@@ -311,7 +315,10 @@ export async function discoverAccessWidenerRuntimeCandidates(_svc, input) {
311
315
  const normalizedProjectPathLower = normalizedProjectPath
312
316
  ? normalizePathStyle(normalizedProjectPath).toLowerCase()
313
317
  : undefined;
314
- const searchRoots = buildVersionSourceSearchRoots(normalizedProjectPath);
318
+ const searchRoots = buildVersionSourceSearchRoots({
319
+ projectPath: normalizedProjectPath,
320
+ gradleUserHome: input.gradleUserHome
321
+ });
315
322
  const searchedPaths = [];
316
323
  const candidates = [];
317
324
  const seen = new Set();
@@ -381,7 +388,10 @@ export async function discoverAccessTransformerRuntimeCandidates(_svc, input) {
381
388
  const normalizedProjectPathLower = normalizedProjectPath
382
389
  ? normalizePathStyle(normalizedProjectPath).toLowerCase()
383
390
  : undefined;
384
- const searchRoots = buildLoaderRuntimeSearchRoots(normalizedProjectPath);
391
+ const searchRoots = buildLoaderRuntimeSearchRoots({
392
+ projectPath: normalizedProjectPath,
393
+ gradleUserHome: input.gradleUserHome
394
+ });
385
395
  const searchedPaths = [];
386
396
  const candidates = [];
387
397
  const seen = new Set();
@@ -491,6 +501,7 @@ export async function resolveAccessWidenerRuntimeArtifact(svc, input) {
491
501
  const discovery = await svc.discoverAccessWidenerRuntimeCandidates({
492
502
  version,
493
503
  projectPath: normalizedProjectPath,
504
+ gradleUserHome: input.gradleUserHome,
494
505
  requestedScope
495
506
  });
496
507
  if (!discovery.selected) {
@@ -622,6 +633,7 @@ export async function resolveAccessTransformerRuntimeArtifact(svc, input) {
622
633
  const discovery = await svc.discoverAccessTransformerRuntimeCandidates({
623
634
  version,
624
635
  projectPath: normalizedProjectPath,
636
+ gradleUserHome: input.gradleUserHome,
625
637
  requestedScope,
626
638
  atNamespace: input.atNamespace,
627
639
  loader
@@ -790,7 +802,8 @@ async function computeBinaryRemapGate(svc, input) {
790
802
  const health = await svc.mappingService.checkMappingHealth({
791
803
  version: input.version,
792
804
  requestedMapping: "mojang",
793
- sourcePriority: input.sourcePriority
805
+ sourcePriority: input.sourcePriority,
806
+ gradleUserHome: input.gradleUserHome
794
807
  });
795
808
  mojangAvailable = health.mojangMappingsAvailable;
796
809
  }
@@ -1026,7 +1039,8 @@ export async function resolveArtifact(svc, input) {
1026
1039
  scope !== "vanilla") {
1027
1040
  versionSourceDiscovery = await svc.discoverVersionSourceJar({
1028
1041
  version: resolvedVersion,
1029
- projectPath: input.projectPath
1042
+ projectPath: input.projectPath,
1043
+ gradleUserHome: input.gradleUserHome
1030
1044
  });
1031
1045
  if (versionSourceDiscovery.selectedSourceJarPath) {
1032
1046
  resolvedTarget = {
@@ -1045,6 +1059,7 @@ export async function resolveArtifact(svc, input) {
1045
1059
  version: resolvedVersion,
1046
1060
  targetKind: kind,
1047
1061
  sourcePriority: input.sourcePriority,
1062
+ gradleUserHome: input.gradleUserHome,
1048
1063
  forceBinaryRemapDisabled: dependencyOrigin
1049
1064
  });
1050
1065
  if (binaryRemapGate.warnings.length > 0) {
@@ -1137,7 +1152,8 @@ export async function resolveArtifact(svc, input) {
1137
1152
  version: resolved.version,
1138
1153
  sourceMapping: "obfuscated",
1139
1154
  targetMapping: effectiveMapping,
1140
- sourcePriority: input.sourcePriority
1155
+ sourcePriority: input.sourcePriority,
1156
+ gradleUserHome: input.gradleUserHome
1141
1157
  });
1142
1158
  additionalTransformChain.push(...mappingAvailability.transformChain);
1143
1159
  if (mappingAvailability.warnings.length > 0) {
@@ -1,4 +1,4 @@
1
- import { existsSync, readdirSync, statSync, unlinkSync } from "node:fs";
1
+ import { existsSync, readdirSync, rmSync, statSync } from "node:fs";
2
2
  import { join } from "node:path";
3
3
  import { log } from "../logger.js";
4
4
  export function hasAnyFiles(svc, artifactId) {
@@ -17,7 +17,7 @@ export function unlinkRemappedJarForArtifact(svc, artifactId) {
17
17
  const remappedJarPath = join(svc.config.cacheDir, "remapped", `${artifactId}.jar`);
18
18
  try {
19
19
  if (existsSync(remappedJarPath)) {
20
- unlinkSync(remappedJarPath);
20
+ rmSync(remappedJarPath, { recursive: true, force: true });
21
21
  }
22
22
  }
23
23
  catch {
@@ -20,6 +20,7 @@ export type RemapMembersInput = {
20
20
  mappingApplied: SourceMapping;
21
21
  requestedMapping: SourceMapping;
22
22
  sourcePriority: MappingSourcePriority | undefined;
23
+ gradleUserHome?: string;
23
24
  memberPattern: string | undefined;
24
25
  warnings: string[];
25
26
  };
@@ -3,7 +3,7 @@ export async function remapAndCountMembers(svc, input) {
3
3
  if (input.version == null) {
4
4
  return members;
5
5
  }
6
- const result = await svc.remapSignatureMembers(members, kind, input.version, input.mappingApplied, input.requestedMapping, input.sourcePriority, input.warnings);
6
+ const result = await svc.remapSignatureMembers(members, kind, input.version, input.mappingApplied, input.requestedMapping, input.sourcePriority, input.warnings, undefined, input.gradleUserHome);
7
7
  return result.members;
8
8
  };
9
9
  let constructors = await remap(input.signatureConstructors, "method");
@@ -9,6 +9,7 @@ export declare function resolveClassNameForLookup(svc: SourceService, input: {
9
9
  sourceMapping: SourceMapping;
10
10
  targetMapping: SourceMapping;
11
11
  sourcePriority: MappingSourcePriority | undefined;
12
+ gradleUserHome?: string;
12
13
  warnings: string[];
13
14
  context: string;
14
15
  }): Promise<string>;
@@ -108,7 +108,8 @@ export async function resolveClassNameForLookup(svc, input) {
108
108
  name: input.className,
109
109
  sourceMapping: input.sourceMapping,
110
110
  targetMapping: input.targetMapping,
111
- sourcePriority: input.sourcePriority
111
+ sourcePriority: input.sourcePriority,
112
+ gradleUserHome: input.gradleUserHome
112
113
  });
113
114
  if (mapped.resolved && mapped.resolvedSymbol) {
114
115
  return mapped.resolvedSymbol.name;
@@ -401,6 +402,7 @@ export async function getClassSource(svc, input) {
401
402
  sourcePriority: input.sourcePriority,
402
403
  allowDecompile: input.allowDecompile,
403
404
  projectPath: input.projectPath,
405
+ gradleUserHome: input.gradleUserHome,
404
406
  scope: input.scope,
405
407
  preferProjectVersion: input.preferProjectVersion,
406
408
  strictVersion: input.strictVersion
@@ -488,6 +490,7 @@ export async function getClassSource(svc, input) {
488
490
  sourceMapping: requestedMapping,
489
491
  targetMapping: activeMappingApplied,
490
492
  sourcePriority: input.sourcePriority,
493
+ gradleUserHome: input.gradleUserHome,
491
494
  warnings,
492
495
  context: "source lookup"
493
496
  });
@@ -499,6 +502,7 @@ export async function getClassSource(svc, input) {
499
502
  sourceMapping: requestedMapping,
500
503
  targetMapping: activeMappingApplied,
501
504
  sourcePriority: input.sourcePriority,
505
+ gradleUserHome: input.gradleUserHome,
502
506
  warnings,
503
507
  context: "source lookup"
504
508
  });
@@ -528,6 +532,7 @@ export async function getClassSource(svc, input) {
528
532
  sourceMapping: requestedMapping,
529
533
  targetMapping: activeMappingApplied,
530
534
  sourcePriority: input.sourcePriority,
535
+ gradleUserHome: input.gradleUserHome,
531
536
  warnings,
532
537
  context: "source lookup"
533
538
  });
@@ -659,6 +664,7 @@ export async function getClassMembers(svc, input) {
659
664
  sourcePriority: input.sourcePriority,
660
665
  allowDecompile: input.allowDecompile,
661
666
  projectPath: input.projectPath,
667
+ gradleUserHome: input.gradleUserHome,
662
668
  scope: input.scope,
663
669
  preferProjectVersion: input.preferProjectVersion,
664
670
  strictVersion: input.strictVersion
@@ -726,6 +732,7 @@ export async function getClassMembers(svc, input) {
726
732
  sourceMapping: requestedMapping,
727
733
  targetMapping: mappingApplied,
728
734
  sourcePriority: input.sourcePriority,
735
+ gradleUserHome: input.gradleUserHome,
729
736
  warnings,
730
737
  context: "binary lookup"
731
738
  });
@@ -775,6 +782,7 @@ export async function getClassMembers(svc, input) {
775
782
  mappingApplied,
776
783
  requestedMapping,
777
784
  sourcePriority: input.sourcePriority,
785
+ gradleUserHome: input.gradleUserHome,
778
786
  memberPattern,
779
787
  warnings
780
788
  });
@@ -1,7 +1,22 @@
1
+ import { resolveMojangTinyFile } from "../mojang-tiny-mapping-service.js";
1
2
  import type { SourceService } from "../source-service.js";
2
3
  import type { ArtifactIndexMetaRow } from "../storage/index-meta-repo.js";
3
4
  import type { ArtifactRow, ResolvedSourceArtifact } from "../types.js";
5
+ import { remapJar } from "../tiny-remapper-service.js";
6
+ import { resolveTinyRemapperJar } from "../tiny-remapper-resolver.js";
4
7
  export declare const INDEX_SCHEMA_VERSION = 1;
8
+ /**
9
+ * Injectable dependencies let remap-cache regression tests run without Java.
10
+ * Production callers use `defaultBinaryRemapDeps`; this is not a public MCP
11
+ * extension point.
12
+ */
13
+ export type BinaryRemapDeps = {
14
+ resolveTinyRemapperJar: typeof resolveTinyRemapperJar;
15
+ resolveMojangTinyFile: typeof resolveMojangTinyFile;
16
+ remapJar: typeof remapJar;
17
+ now: () => number;
18
+ randomSuffix: () => string;
19
+ };
5
20
  export type IndexRebuildReason = "force" | "missing_meta" | "schema_mismatch" | "signature_mismatch" | "already_current";
6
21
  export interface IndexedFileRecord {
7
22
  filePath: string;
@@ -57,13 +72,12 @@ export declare function ingestIfNeeded(svc: SourceService, resolved: ResolvedSou
57
72
  * binary remap, run tiny-remapper now and return the remapped jar path.
58
73
  * Otherwise return the original binaryJarPath unchanged.
59
74
  *
60
- * Cache safety: writes to a per-attempt temp file then atomic-renames into
61
- * <cacheDir>/remapped/<artifactId>.jar. A per-target inflight Promise map
62
- * collapses concurrent calls so two simultaneous resolveArtifact calls for
63
- * the same artifactId share one tiny-remapper run instead of racing on the
64
- * same output path.
75
+ * Cache safety: writes to a per-attempt `.jar` temp path, validates ZIP magic,
76
+ * then atomic-renames into <cacheDir>/remapped/<artifactId>.jar. A per-target
77
+ * inflight Promise map collapses concurrent calls so simultaneous
78
+ * resolveArtifact calls for the same artifactId share one tiny-remapper run.
65
79
  */
66
- export declare function maybeRemapBinaryForMojang(svc: SourceService, resolved: ResolvedSourceArtifact): Promise<string>;
80
+ export declare function maybeRemapBinaryForMojang(svc: SourceService, resolved: ResolvedSourceArtifact, deps?: BinaryRemapDeps): Promise<string>;
67
81
  export declare function recordRemappedJarBytesFromDisk(svc: SourceService, artifactId: string, path: string): Promise<void>;
68
82
  /**
69
83
  * Best-effort structural check that `path` is a non-empty file beginning with
@@ -73,10 +87,21 @@ export declare function recordRemappedJarBytesFromDisk(svc: SourceService, artif
73
87
  * are not (a corrupt cache hit must be evicted).
74
88
  */
75
89
  export declare function isUsableJarFile(path: string): Promise<boolean>;
90
+ export declare function buildBinaryRemapTempPath(remappedJarPath: string, input: {
91
+ pid: number;
92
+ now: number;
93
+ randomSuffix: string;
94
+ }): string;
76
95
  export declare function runBinaryRemap(svc: SourceService, input: {
77
96
  version: string;
78
97
  inputJar: string;
79
98
  remappedDir: string;
80
99
  remappedJarPath: string;
81
100
  }): Promise<string>;
101
+ export declare function runBinaryRemapWithDeps(svc: SourceService, input: {
102
+ version: string;
103
+ inputJar: string;
104
+ remappedDir: string;
105
+ remappedJarPath: string;
106
+ }, deps: BinaryRemapDeps): Promise<string>;
82
107
  export declare function loadFromSourceJar(svc: SourceService, sourceJarPath: string): Promise<IndexedFileRecord[]>;