@adhisang/minecraft-modding-mcp 3.1.1 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +24 -0
- package/README.md +20 -8
- package/dist/access-transformer-parser.d.ts +17 -0
- package/dist/access-transformer-parser.js +97 -0
- package/dist/concurrency.d.ts +1 -0
- package/dist/concurrency.js +24 -0
- package/dist/decompiler/vineflower.js +22 -21
- package/dist/entry-tools/analyze-mod-service.d.ts +4 -4
- package/dist/entry-tools/analyze-symbol-service.d.ts +20 -20
- package/dist/entry-tools/inspect-minecraft-service.d.ts +148 -148
- package/dist/entry-tools/validate-project-service.d.ts +153 -16
- package/dist/entry-tools/validate-project-service.js +360 -23
- package/dist/gradle-paths.d.ts +4 -0
- package/dist/gradle-paths.js +57 -0
- package/dist/index.js +65 -13
- package/dist/mapping-pipeline-service.d.ts +3 -1
- package/dist/mapping-pipeline-service.js +16 -1
- package/dist/mapping-service.d.ts +4 -0
- package/dist/mapping-service.js +155 -60
- package/dist/minecraft-explorer-service.d.ts +13 -0
- package/dist/minecraft-explorer-service.js +8 -4
- package/dist/mixin-validator.d.ts +33 -2
- package/dist/mixin-validator.js +197 -11
- package/dist/mod-analyzer.d.ts +1 -0
- package/dist/mod-analyzer.js +17 -1
- package/dist/mod-decompile-service.js +4 -4
- package/dist/mod-remap-service.js +1 -54
- package/dist/mod-search-service.d.ts +1 -0
- package/dist/mod-search-service.js +84 -51
- package/dist/response-utils.d.ts +35 -0
- package/dist/response-utils.js +113 -0
- package/dist/source-jar-reader.d.ts +16 -0
- package/dist/source-jar-reader.js +103 -1
- package/dist/source-resolver.js +9 -10
- package/dist/source-service.d.ts +22 -2
- package/dist/source-service.js +914 -105
- package/dist/tool-contract-manifest.js +8 -6
- package/dist/types.d.ts +17 -0
- package/dist/workspace-mapping-service.d.ts +13 -0
- package/dist/workspace-mapping-service.js +146 -14
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,30 @@ All notable changes to this project are documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project aims to follow [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [3.2.0] - 2026-04-12
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- `resolve-artifact`, `find-mapping`, `resolve-method-mapping-exact`, `resolve-workspace-symbol`, and `check-symbol-exists` now accept an optional `compact` parameter (default `false`). When `true`, empty arrays, null values, and empty objects are stripped from the top-level response. For `resolve-artifact`, compact mode additionally omits `provenance`, `artifactContents`, `sampleEntries`, `adjacentSourceCandidates`, `binaryJarPath`, `coordinate`, `repoUrl`, and `resolvedSourceJarPath`. For mapping tools, compact mode omits the redundant `candidates` array when the result is a single full-confidence exact-match resolution.
|
|
12
|
+
- `validate-access-widener` now accepts `projectPath`, `scope`, and `preferProjectVersion` for runtime-aware validation against Loom runtime jars, and reports additive `provenance`, `resolvedInRuntime`, and `resolvedRuntimeAccess` evidence for matched class, method, and field entries.
|
|
13
|
+
- Added `validate-access-transformer` for Forge / NeoForge Access Transformer validation, including `atNamespace` support, workspace-driven namespace inference, runtime artifact provenance, and per-entry runtime access evidence.
|
|
14
|
+
- `analyze-mod-jar` now surfaces packaged Access Transformer paths alongside mod metadata.
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
- `validate-access-widener` and `validate-access-transformer` runtime-aware method validation now preserves remapped JVM descriptors through exact and fallback member remaps, eliminating false-negative `method not found` results for entries whose descriptors reference remapped Minecraft classes such as `Level#setBlock(BlockPos, BlockState, int)`.
|
|
18
|
+
- `validate-access-widener` runtime-aware `scope: "merged"` now prefers explicit `*merged-intermediary*` / `*merged-mojang*` runtime jars over ambiguous `minecraft-merged.jar` candidates, and Loom tiny lookup now scans workspace and Gradle user-home caches so runtime validation can resolve descriptor remaps in normal Loom setups.
|
|
19
|
+
- `validate-project` now forwards workspace runtime context to discovered Access Widener validation so project-summary runs can use the same runtime-aware behavior as direct `validate-access-widener` calls.
|
|
20
|
+
- `validate-project` now discovers Access Transformer files, supports `task="access-transformer"`, and can include those validations in workspace summaries when `discover` requests them.
|
|
21
|
+
- `resolve-artifact`, `get-class-source`, `get-class-members`, and `inspect-minecraft` source/class-member flows now treat unobfuscated releases such as `26.1+` as native `mojang` runtime namespaces for both version and versioned-coordinate targets, avoiding false `ERR_MAPPING_NOT_APPLIED` failures and wrong-version Loom source-jar approximations when no exact source jar is present.
|
|
22
|
+
- `validate-mixin` tool-health diagnostics now treat unobfuscated `mojang` runtime names as available, while still flagging `intermediary`/`yarn` as unavailable on `26.1+`.
|
|
23
|
+
- `validate-project task="project-summary"` now pre-resolves `preferProjectVersion=true` consistently across discovered Access Widener and workspace Mixin checks, and returns a blocked summary with version-agnostic recovery guidance when discovered validators need a version but neither the request nor `gradle.properties` can supply one.
|
|
24
|
+
- `check-symbol-exists` and `analyze-symbol task="exists"` now fall back to unobfuscated runtime bytecode for `mojang`/runtime-name existence checks on `26.1+`, preserve the original `mapping_unavailable` result when the runtime JAR cannot be resolved, and return a targeted warning when callers provide only a short class name.
|
|
25
|
+
- Loom tiny mapping discovery no longer performs unbounded directory traversal when the version-specific cache directory is absent, preventing excessive memory consumption on systems with large Gradle caches.
|
|
26
|
+
- Mapping graph cache eviction after lifecycle scans now correctly releases all cached entries for a version instead of silently skipping them.
|
|
27
|
+
|
|
28
|
+
### Performance
|
|
29
|
+
- `resolve-artifact` source-jar detection and mapping tiny-jar loading now reuse a single ZIP walk per jar probe instead of reopening and fully enumerating matching archives on each helper call.
|
|
30
|
+
- `search-mod-source`, `decompile-mod-jar`, `get-mod-class-source`, `validate-project` workspace discovery, and workspace mapping detection now avoid synchronous hot-path file/glob reads and use bounded concurrent text reads where safe, reducing event-loop stalls on larger decompiled outputs and multi-module workspaces.
|
|
31
|
+
|
|
8
32
|
## [3.1.1] - 2026-03-21
|
|
9
33
|
|
|
10
34
|
### Fixed
|
package/README.md
CHANGED
|
@@ -20,8 +20,8 @@
|
|
|
20
20
|
- **Source Exploration**: browse and search decompiled Minecraft source code with line-level precision and cursor-paginated file listing
|
|
21
21
|
- **Multi-Mapping Conversion**: translate class, field, and method names between `obfuscated`, `mojang`, `intermediary`, and `yarn`
|
|
22
22
|
- **Version Comparison**: diff class signatures and registry entries between Minecraft versions
|
|
23
|
-
- **Mod JAR Analysis**: extract metadata, dependencies, entrypoints,
|
|
24
|
-
- **Mixin and Access
|
|
23
|
+
- **Mod JAR Analysis**: extract metadata, dependencies, entrypoints, Mixin configs, and packaged Access Transformer paths from Fabric, Forge, and NeoForge mod JARs
|
|
24
|
+
- **Mixin, Access Widener, and Access Transformer Validation**: validate source, `.accesswidener`, and Forge/NeoForge access transformer files against a target Minecraft version
|
|
25
25
|
- **NBT Round-Trip**: decode NBT binary to typed JSON, apply RFC 6902 patches, and encode it back to NBT
|
|
26
26
|
- **Registry Data and Runtime Metrics**: query generated registry snapshots and inspect cache and latency counters
|
|
27
27
|
- **MCP Resources**: expose versions, class source, artifact metadata, and mappings through URI-based resources
|
|
@@ -136,7 +136,7 @@ All six return `result.summary` first, and can include `summary.nextActions` whe
|
|
|
136
136
|
| `analyze-symbol` | symbol existence checks, mapping conversion, lifecycle tracing, and workspace symbol resolution |
|
|
137
137
|
| `compare-minecraft` | version-pair diffs, class diffs, registry diffs, and migration-oriented overviews |
|
|
138
138
|
| `analyze-mod` | mod metadata, decompile/search flows, class source, and safe remap preview/apply |
|
|
139
|
-
| `validate-project` | workspace summaries plus direct Mixin and Access
|
|
139
|
+
| `validate-project` | workspace summaries plus direct Mixin, Access Widener, and Access Transformer validation |
|
|
140
140
|
| `manage-cache` | cache inventory, verification, and preview/apply cleanup workflows |
|
|
141
141
|
|
|
142
142
|
### Workflow Notes
|
|
@@ -147,7 +147,10 @@ Keep only the high-frequency notes here. For the full pitfall list, exact contra
|
|
|
147
147
|
- If you do not already have an artifact, prefer `subject.kind="workspace"` for `inspect-minecraft` instead of guessing artifact details. When artifact context is the only missing input, a retryable `suggestedCall` preserves the requested task.
|
|
148
148
|
- `trace-symbol-lifecycle` expects `Class.method` in `symbol`. Keep exact overload matching in the separate `descriptor` field.
|
|
149
149
|
- Workspace inspection can still confirm vanilla classes when source coverage is partial, and `inspect-minecraft task="list-files"` reports a partial result with follow-up guidance when that happens.
|
|
150
|
+
- `check-symbol-exists` and `analyze-symbol task="exists"` now validate `mojang` lookups on unobfuscated releases such as `26.1+` against runtime bytecode when no mapping graph exists, preserve the original `mapping_unavailable` result if the runtime JAR itself cannot be resolved, and return a targeted warning when callers provide only a short class name.
|
|
150
151
|
- `analyze-mod` and `validate-project` still require structured `subject` objects and canonical `include` groups, but stale string-subject or domain-include payloads now return `ERR_INVALID_INPUT` with a retryable `suggestedCall`.
|
|
152
|
+
- `validate-project task="project-summary"` now pre-resolves `preferProjectVersion=true` consistently across discovered Access Widener, Access Transformer, and Mixin checks, and blocks with version-agnostic recovery guidance when discovered validators need a version but neither the request nor `gradle.properties` can supply one.
|
|
153
|
+
- Local source-jar probing, decompiled mod source reads, and workspace discovery now avoid synchronous hot-path ZIP/file scans and use bounded concurrent reads where safe, so cold `resolve-artifact`, `analyze-mod`, and `validate-project` workflows stay more responsive on larger jars and multi-module workspaces.
|
|
151
154
|
|
|
152
155
|
### Inspect Minecraft source from a version
|
|
153
156
|
|
|
@@ -216,7 +219,7 @@ Keep only the high-frequency notes here. For the full pitfall list, exact contra
|
|
|
216
219
|
"subject": {
|
|
217
220
|
"kind": "workspace",
|
|
218
221
|
"projectPath": "/workspace/modid",
|
|
219
|
-
"discover": ["mixins", "access-wideners"]
|
|
222
|
+
"discover": ["mixins", "access-wideners", "access-transformers"]
|
|
220
223
|
},
|
|
221
224
|
"preferProjectVersion": true,
|
|
222
225
|
"preferProjectMapping": true
|
|
@@ -224,6 +227,8 @@ Keep only the high-frequency notes here. For the full pitfall list, exact contra
|
|
|
224
227
|
}
|
|
225
228
|
```
|
|
226
229
|
|
|
230
|
+
Workspace summaries still default to discovering mixins and access wideners. Add `"access-transformers"` to `subject.discover` when you want Access Transformer files included in the summary run.
|
|
231
|
+
|
|
227
232
|
## Documentation
|
|
228
233
|
|
|
229
234
|
- [Detailed example requests](docs/examples.md) for copyable payloads and common workflows
|
|
@@ -243,7 +248,7 @@ Start with these top-level workflow tools unless you already know the exact spec
|
|
|
243
248
|
| `analyze-symbol` | Handle symbol existence checks, namespace mapping, lifecycle tracing, workspace symbol resolution, and API overviews |
|
|
244
249
|
| `compare-minecraft` | Compare version pairs, class diffs, registry diffs, and migration-oriented summaries |
|
|
245
250
|
| `analyze-mod` | Summarize mod metadata, decompile and search mod code, inspect class source, and preview or apply remaps |
|
|
246
|
-
| `validate-project` | Summarize workspaces and run direct Mixin or Access
|
|
251
|
+
| `validate-project` | Summarize workspaces and run direct Mixin, Access Widener, or Access Transformer validation |
|
|
247
252
|
| `manage-cache` | List, verify, and preview or apply cache cleanup and rebuild operations |
|
|
248
253
|
<!-- END GENERATED TOOL TABLE: v3-entry-tools -->
|
|
249
254
|
|
|
@@ -265,6 +270,8 @@ Tools for browsing Minecraft versions, resolving source artifacts, and reading o
|
|
|
265
270
|
| `index-artifact` | Rebuild indexed metadata for an existing artifact |
|
|
266
271
|
<!-- END GENERATED TOOL TABLE: source-exploration -->
|
|
267
272
|
|
|
273
|
+
For unobfuscated releases such as `26.1+`, `mapping="mojang"` now uses the runtime/decompile path directly for version and versioned-coordinate targets and skips Loom source-jar discovery entirely, while `intermediary` and `yarn` still fall back to `obfuscated` with a warning.
|
|
274
|
+
|
|
268
275
|
### Version Comparison & Symbol Tracking
|
|
269
276
|
|
|
270
277
|
Tools for comparing class and registry changes across Minecraft versions and tracing symbol existence over time.
|
|
@@ -291,6 +298,8 @@ Tools for converting symbol names between namespaces and checking symbol existen
|
|
|
291
298
|
| `check-symbol-exists` | Check whether a class, field, or method exists in a namespace |
|
|
292
299
|
<!-- END GENERATED TOOL TABLE: mapping-symbols -->
|
|
293
300
|
|
|
301
|
+
`resolve-artifact`, `find-mapping`, `resolve-method-mapping-exact`, `resolve-workspace-symbol`, and `check-symbol-exists` accept an optional `compact` parameter (default `false`). When `true`, empty arrays, null values, and empty objects are stripped from the top-level response to reduce token overhead. For `resolve-artifact`, compact mode additionally omits diagnostic fields (`provenance`, `artifactContents`, `sampleEntries`, `adjacentSourceCandidates`, `binaryJarPath`, `coordinate`, `repoUrl`, `resolvedSourceJarPath`), returning only the essential fields needed for downstream tool calls. For mapping tools, compact mode omits the redundant `candidates` array when the result is a single full-confidence exact-match resolution (`resolved=true`, `resolvedSymbol` present, `candidates.length=1`, `candidateCount=1`, `!candidatesTruncated`, `matchKind="exact"`, `confidence` missing or `1`).
|
|
302
|
+
|
|
294
303
|
### NBT Utilities
|
|
295
304
|
|
|
296
305
|
Tools for decoding, patching, and encoding Java Edition NBT binary data using a typed JSON representation.
|
|
@@ -310,7 +319,7 @@ Tools for extracting metadata from mod JARs, decompiling mod source, searching m
|
|
|
310
319
|
<!-- BEGIN GENERATED TOOL TABLE: mod-analysis -->
|
|
311
320
|
| Tool | Purpose |
|
|
312
321
|
| --- | --- |
|
|
313
|
-
| `analyze-mod-jar` | Extract mod metadata, dependencies, entrypoints,
|
|
322
|
+
| `analyze-mod-jar` | Extract mod metadata, dependencies, entrypoints, mixin config info, and packaged access transformer paths from a JAR |
|
|
314
323
|
| `decompile-mod-jar` | Decompile a mod JAR and optionally return one class source |
|
|
315
324
|
| `get-mod-class-source` | Read one class source from the decompiled mod cache |
|
|
316
325
|
| `search-mod-source` | Search decompiled mod source by class, method, field, or content |
|
|
@@ -319,13 +328,16 @@ Tools for extracting metadata from mod JARs, decompiling mod source, searching m
|
|
|
319
328
|
|
|
320
329
|
### Validation
|
|
321
330
|
|
|
322
|
-
Tools for validating Mixin source and Access
|
|
331
|
+
Tools for validating Mixin source, Access Widener files, and Forge/NeoForge Access Transformer files against a target Minecraft version.
|
|
332
|
+
`validate-access-widener` keeps vanilla bytecode validation by default, and now also supports runtime-aware validation through `projectPath`, `scope`, and `preferProjectVersion`, returning runtime `provenance` plus per-entry `resolvedRuntimeAccess` evidence when that mode is used. Runtime-aware method validation now preserves remapped JVM descriptors across namespace changes, searches Loom tiny mappings from workspace and Gradle user-home caches, and prefers explicit `*merged-intermediary*` / `*merged-mojang*` jars over ambiguous `minecraft-merged.jar` candidates.
|
|
333
|
+
`validate-access-transformer` infers `atNamespace` from Forge or NeoForge workspace context when `projectPath` is provided, validates packaged or inline AT content, and uses loader/runtime artifacts for `scope="loader"` instead of treating loader as a merged-only alias.
|
|
323
334
|
|
|
324
335
|
<!-- BEGIN GENERATED TOOL TABLE: validation -->
|
|
325
336
|
| Tool | Purpose |
|
|
326
337
|
| --- | --- |
|
|
327
338
|
| `validate-mixin` | Validate Mixin source against a target Minecraft version |
|
|
328
|
-
| `validate-access-widener` | Validate Access Widener content against a target Minecraft version |
|
|
339
|
+
| `validate-access-widener` | Validate Access Widener content against a target Minecraft version, optionally using runtime-aware Loom artifacts |
|
|
340
|
+
| `validate-access-transformer` | Validate Access Transformer content against a target Minecraft version, optionally using Forge/NeoForge runtime artifacts |
|
|
329
341
|
<!-- END GENERATED TOOL TABLE: validation -->
|
|
330
342
|
|
|
331
343
|
### Registry & Diagnostics
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export type AccessTransformerAccessAction = "public" | "protected" | "package-private" | "private";
|
|
2
|
+
export type AccessTransformerFinalAction = "add" | "remove";
|
|
3
|
+
export type AccessTransformerEntry = {
|
|
4
|
+
line: number;
|
|
5
|
+
targetKind: "class" | "field" | "method";
|
|
6
|
+
owner: string;
|
|
7
|
+
target: string;
|
|
8
|
+
name?: string;
|
|
9
|
+
descriptor?: string;
|
|
10
|
+
accessAction: AccessTransformerAccessAction;
|
|
11
|
+
finalAction?: AccessTransformerFinalAction;
|
|
12
|
+
};
|
|
13
|
+
export type ParsedAccessTransformer = {
|
|
14
|
+
entries: AccessTransformerEntry[];
|
|
15
|
+
parseWarnings: string[];
|
|
16
|
+
};
|
|
17
|
+
export declare function parseAccessTransformer(content: string): ParsedAccessTransformer;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
function parseAccessDeclaration(raw) {
|
|
2
|
+
const match = raw.match(/^(public|protected|default|private)([+-]f)?$/i);
|
|
3
|
+
if (!match) {
|
|
4
|
+
return undefined;
|
|
5
|
+
}
|
|
6
|
+
const finalModifier = match[2]?.toLowerCase();
|
|
7
|
+
return {
|
|
8
|
+
accessAction: match[1]?.toLowerCase() === "default"
|
|
9
|
+
? "package-private"
|
|
10
|
+
: match[1]?.toLowerCase(),
|
|
11
|
+
...(finalModifier === "+f" ? { finalAction: "add" } : {}),
|
|
12
|
+
...(finalModifier === "-f" ? { finalAction: "remove" } : {})
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
function splitMemberToken(tokens) {
|
|
16
|
+
if (tokens.length === 0) {
|
|
17
|
+
return { targetKind: "class" };
|
|
18
|
+
}
|
|
19
|
+
if (tokens.length === 1) {
|
|
20
|
+
const token = tokens[0] ?? "";
|
|
21
|
+
const descriptorStart = token.indexOf("(");
|
|
22
|
+
if (descriptorStart >= 0) {
|
|
23
|
+
return {
|
|
24
|
+
targetKind: "method",
|
|
25
|
+
name: token.slice(0, descriptorStart),
|
|
26
|
+
descriptor: token.slice(descriptorStart)
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
return {
|
|
30
|
+
targetKind: "field",
|
|
31
|
+
name: token
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
return {
|
|
35
|
+
targetKind: "method",
|
|
36
|
+
name: tokens[0],
|
|
37
|
+
descriptor: tokens.slice(1).join("")
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
export function parseAccessTransformer(content) {
|
|
41
|
+
const entries = [];
|
|
42
|
+
const parseWarnings = [];
|
|
43
|
+
const lines = content.split(/\r?\n/);
|
|
44
|
+
for (let index = 0; index < lines.length; index++) {
|
|
45
|
+
const lineNumber = index + 1;
|
|
46
|
+
const rawLine = (lines[index] ?? "").trim();
|
|
47
|
+
if (!rawLine || rawLine.startsWith("#")) {
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
const withoutComment = rawLine.replace(/\s+#.*$/, "").trim();
|
|
51
|
+
if (!withoutComment) {
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
const parts = withoutComment.split(/\s+/);
|
|
55
|
+
if (parts.length < 2) {
|
|
56
|
+
parseWarnings.push(`Line ${lineNumber}: Incomplete access transformer entry "${withoutComment}".`);
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
const declaration = parseAccessDeclaration(parts[0] ?? "");
|
|
60
|
+
if (!declaration) {
|
|
61
|
+
parseWarnings.push(`Line ${lineNumber}: Unsupported access declaration "${parts[0]}".`);
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
const owner = parts[1] ?? "";
|
|
65
|
+
if (!owner) {
|
|
66
|
+
parseWarnings.push(`Line ${lineNumber}: Incomplete access transformer entry "${withoutComment}".`);
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
const member = splitMemberToken(parts.slice(2));
|
|
70
|
+
if (member.targetKind === "method" && (!member.name || !member.descriptor)) {
|
|
71
|
+
parseWarnings.push(`Line ${lineNumber}: Method entry requires a method name and JVM descriptor.`);
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
if ((member.targetKind === "field" || member.targetKind === "method") && !member.name) {
|
|
75
|
+
parseWarnings.push(`Line ${lineNumber}: Member entry requires a target name.`);
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
const target = member.targetKind === "class"
|
|
79
|
+
? owner
|
|
80
|
+
: `${owner}#${member.name}${member.descriptor ?? ""}`;
|
|
81
|
+
entries.push({
|
|
82
|
+
line: lineNumber,
|
|
83
|
+
owner,
|
|
84
|
+
target,
|
|
85
|
+
targetKind: member.targetKind,
|
|
86
|
+
...(member.name ? { name: member.name } : {}),
|
|
87
|
+
...(member.descriptor ? { descriptor: member.descriptor } : {}),
|
|
88
|
+
accessAction: declaration.accessAction,
|
|
89
|
+
...(declaration.finalAction ? { finalAction: declaration.finalAction } : {})
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
return {
|
|
93
|
+
entries,
|
|
94
|
+
parseWarnings
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=access-transformer-parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function mapWithConcurrencyLimit<T, R>(items: readonly T[], limit: number, mapper: (item: T, index: number) => Promise<R>): Promise<R[]>;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export async function mapWithConcurrencyLimit(items, limit, mapper) {
|
|
2
|
+
if (!Number.isInteger(limit) || limit < 1) {
|
|
3
|
+
throw new Error("limit must be a positive integer");
|
|
4
|
+
}
|
|
5
|
+
if (items.length === 0) {
|
|
6
|
+
return [];
|
|
7
|
+
}
|
|
8
|
+
const results = new Array(items.length);
|
|
9
|
+
let nextIndex = 0;
|
|
10
|
+
const workerCount = Math.min(limit, items.length);
|
|
11
|
+
const worker = async () => {
|
|
12
|
+
while (true) {
|
|
13
|
+
const currentIndex = nextIndex;
|
|
14
|
+
nextIndex += 1;
|
|
15
|
+
if (currentIndex >= items.length) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
results[currentIndex] = await mapper(items[currentIndex], currentIndex);
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
await Promise.all(Array.from({ length: workerCount }, async () => worker()));
|
|
22
|
+
return results;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=concurrency.js.map
|
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
import { access, constants } from "node:fs/promises";
|
|
2
|
-
import { mkdirSync,
|
|
1
|
+
import { access, constants, mkdir, readFile, readdir, stat } from "node:fs/promises";
|
|
2
|
+
import { mkdirSync, rmSync } from "node:fs";
|
|
3
3
|
import { createHash } from "node:crypto";
|
|
4
4
|
import { basename, join, relative, sep } from "node:path";
|
|
5
|
+
import { mapWithConcurrencyLimit } from "../concurrency.js";
|
|
5
6
|
import { createError, ERROR_CODES, isAppError } from "../errors.js";
|
|
6
7
|
import { assertJavaAvailable, runJavaProcess } from "../java-process.js";
|
|
7
8
|
import { log } from "../logger.js";
|
|
8
9
|
const DEFAULT_TIMEOUT_MS = 120_000;
|
|
10
|
+
const DECOMPILED_JAVA_READ_CONCURRENCY = 8;
|
|
9
11
|
const VINEFLOWER_FLAG_PROFILES = [
|
|
10
12
|
{ label: "default", flags: ["-din=1", "-rbr=1", "-dgs=1"] },
|
|
11
13
|
{ label: "relaxed", flags: ["-din=1", "-rbr=0", "-dgs=0"] },
|
|
@@ -47,14 +49,14 @@ async function assertVineflowerAvailable(vineflowerJarPath) {
|
|
|
47
49
|
});
|
|
48
50
|
}
|
|
49
51
|
}
|
|
50
|
-
function
|
|
52
|
+
async function collectJavaFilesRecursive(baseDir, currentDir = "") {
|
|
51
53
|
const absoluteBase = currentDir ? join(baseDir, currentDir) : baseDir;
|
|
52
|
-
const entries =
|
|
54
|
+
const entries = await readdir(absoluteBase, { withFileTypes: true });
|
|
53
55
|
const result = [];
|
|
54
56
|
for (const entry of entries) {
|
|
55
57
|
const next = currentDir ? join(currentDir, entry.name) : entry.name;
|
|
56
58
|
if (entry.isDirectory()) {
|
|
57
|
-
result.push(...
|
|
59
|
+
result.push(...await collectJavaFilesRecursive(baseDir, next));
|
|
58
60
|
continue;
|
|
59
61
|
}
|
|
60
62
|
if (entry.isFile() && entry.name.endsWith(".java")) {
|
|
@@ -66,23 +68,21 @@ function collectJavaFilesSync(baseDir, currentDir = "") {
|
|
|
66
68
|
async function collectJavaFiles(baseDir) {
|
|
67
69
|
try {
|
|
68
70
|
const fastGlobModule = (await import("fast-glob"));
|
|
69
|
-
const
|
|
70
|
-
if (typeof
|
|
71
|
-
return
|
|
71
|
+
const glob = fastGlobModule.default?.glob;
|
|
72
|
+
if (typeof glob === "function") {
|
|
73
|
+
return (await glob("**/*.java", { cwd: baseDir, onlyFiles: true }))
|
|
74
|
+
.sort((left, right) => left.localeCompare(right));
|
|
72
75
|
}
|
|
73
76
|
}
|
|
74
77
|
catch {
|
|
75
78
|
// optional dependency: fallback to recursive traversal
|
|
76
79
|
}
|
|
77
|
-
return
|
|
80
|
+
return (await collectJavaFilesRecursive(baseDir))
|
|
81
|
+
.map((candidate) => candidate.split(sep).join("/"))
|
|
82
|
+
.sort((left, right) => left.localeCompare(right));
|
|
78
83
|
}
|
|
79
84
|
function readFileTreeText(filePath) {
|
|
80
|
-
return
|
|
81
|
-
import("node:fs/promises")
|
|
82
|
-
.then((fs) => fs.readFile(filePath, "utf8"))
|
|
83
|
-
.then(resolve)
|
|
84
|
-
.catch(reject);
|
|
85
|
-
});
|
|
85
|
+
return readFile(filePath, "utf8");
|
|
86
86
|
}
|
|
87
87
|
function decompileOutputDir(cacheDir, binaryJarPath, signature) {
|
|
88
88
|
const digest = createHash("sha256").update(binaryJarPath).update(signature).digest("hex");
|
|
@@ -130,17 +130,18 @@ export async function decompileBinaryJar(binaryJarPath, cacheDir, options) {
|
|
|
130
130
|
const signature = options.signature ?? basename(normalizedBinaryJarPath);
|
|
131
131
|
const outputDir = decompileOutputDir(cacheDir, normalizedBinaryJarPath, signature).replace(/[/\\]$/, "");
|
|
132
132
|
try {
|
|
133
|
-
|
|
134
|
-
|
|
133
|
+
await mkdir(outputDir, { recursive: true });
|
|
134
|
+
const outputDirStats = await stat(outputDir).catch(() => undefined);
|
|
135
|
+
if (outputDirStats) {
|
|
135
136
|
const existingJavaFiles = await collectJavaFiles(outputDir);
|
|
136
137
|
if (existingJavaFiles.length > 0) {
|
|
137
|
-
const results = await
|
|
138
|
+
const results = await mapWithConcurrencyLimit(existingJavaFiles, DECOMPILED_JAVA_READ_CONCURRENCY, async (candidate) => {
|
|
138
139
|
const abs = join(outputDir, candidate);
|
|
139
140
|
return {
|
|
140
141
|
filePath: normalizeOutputPath(outputDir, abs),
|
|
141
142
|
content: await readFileTreeText(abs)
|
|
142
143
|
};
|
|
143
|
-
})
|
|
144
|
+
});
|
|
144
145
|
emitDecompileLog("decompile.done", {
|
|
145
146
|
durationMs: Date.now() - startedAt,
|
|
146
147
|
artifactIdCandidate: options.artifactIdCandidate,
|
|
@@ -181,13 +182,13 @@ export async function decompileBinaryJar(binaryJarPath, cacheDir, options) {
|
|
|
181
182
|
}
|
|
182
183
|
});
|
|
183
184
|
}
|
|
184
|
-
const javaFiles = await
|
|
185
|
+
const javaFiles = await mapWithConcurrencyLimit(javaFileNames, DECOMPILED_JAVA_READ_CONCURRENCY, async (candidate) => {
|
|
185
186
|
const abs = join(outputDir, candidate);
|
|
186
187
|
return {
|
|
187
188
|
filePath: normalizeOutputPath(outputDir, abs),
|
|
188
189
|
content: await readFileTreeText(abs)
|
|
189
190
|
};
|
|
190
|
-
})
|
|
191
|
+
});
|
|
191
192
|
emitDecompileLog("decompile.done", {
|
|
192
193
|
durationMs: Date.now() - startedAt,
|
|
193
194
|
artifactIdCandidate: options.artifactIdCandidate,
|
|
@@ -75,7 +75,7 @@ export declare const analyzeModSchema: z.ZodEffects<z.ZodObject<{
|
|
|
75
75
|
include: z.ZodOptional<z.ZodArray<z.ZodEnum<[string, ...string[]]>, "many">>;
|
|
76
76
|
}, "strip", z.ZodTypeAny, {
|
|
77
77
|
limit: number;
|
|
78
|
-
searchType: "class" | "
|
|
78
|
+
searchType: "class" | "field" | "method" | "all" | "content";
|
|
79
79
|
task: "search" | "remap" | "summary" | "class-source" | "decompile";
|
|
80
80
|
subject: {
|
|
81
81
|
kind: "jar";
|
|
@@ -111,7 +111,7 @@ export declare const analyzeModSchema: z.ZodEffects<z.ZodObject<{
|
|
|
111
111
|
targetMapping?: "mojang" | "yarn" | undefined;
|
|
112
112
|
outputJar?: string | undefined;
|
|
113
113
|
query?: string | undefined;
|
|
114
|
-
searchType?: "class" | "
|
|
114
|
+
searchType?: "class" | "field" | "method" | "all" | "content" | undefined;
|
|
115
115
|
detail?: "full" | "summary" | "standard" | undefined;
|
|
116
116
|
include?: string[] | undefined;
|
|
117
117
|
includeFiles?: boolean | undefined;
|
|
@@ -119,7 +119,7 @@ export declare const analyzeModSchema: z.ZodEffects<z.ZodObject<{
|
|
|
119
119
|
executionMode?: "preview" | "apply" | undefined;
|
|
120
120
|
}>, {
|
|
121
121
|
limit: number;
|
|
122
|
-
searchType: "class" | "
|
|
122
|
+
searchType: "class" | "field" | "method" | "all" | "content";
|
|
123
123
|
task: "search" | "remap" | "summary" | "class-source" | "decompile";
|
|
124
124
|
subject: {
|
|
125
125
|
kind: "jar";
|
|
@@ -155,7 +155,7 @@ export declare const analyzeModSchema: z.ZodEffects<z.ZodObject<{
|
|
|
155
155
|
targetMapping?: "mojang" | "yarn" | undefined;
|
|
156
156
|
outputJar?: string | undefined;
|
|
157
157
|
query?: string | undefined;
|
|
158
|
-
searchType?: "class" | "
|
|
158
|
+
searchType?: "class" | "field" | "method" | "all" | "content" | undefined;
|
|
159
159
|
detail?: "full" | "summary" | "standard" | undefined;
|
|
160
160
|
include?: string[] | undefined;
|
|
161
161
|
includeFiles?: boolean | undefined;
|
|
@@ -8,15 +8,15 @@ export declare const analyzeSymbolShape: {
|
|
|
8
8
|
owner: z.ZodOptional<z.ZodString>;
|
|
9
9
|
descriptor: z.ZodOptional<z.ZodString>;
|
|
10
10
|
}, "strip", z.ZodTypeAny, {
|
|
11
|
-
kind: "symbol" | "class" | "method" | "field";
|
|
12
11
|
name: string;
|
|
13
|
-
|
|
12
|
+
kind: "symbol" | "class" | "field" | "method";
|
|
14
13
|
descriptor?: string | undefined;
|
|
14
|
+
owner?: string | undefined;
|
|
15
15
|
}, {
|
|
16
|
-
kind: "symbol" | "class" | "method" | "field";
|
|
17
16
|
name: string;
|
|
18
|
-
|
|
17
|
+
kind: "symbol" | "class" | "field" | "method";
|
|
19
18
|
descriptor?: string | undefined;
|
|
19
|
+
owner?: string | undefined;
|
|
20
20
|
}>;
|
|
21
21
|
version: z.ZodOptional<z.ZodString>;
|
|
22
22
|
sourceMapping: z.ZodOptional<z.ZodEnum<["obfuscated", "mojang", "intermediary", "yarn"]>>;
|
|
@@ -39,15 +39,15 @@ export declare const analyzeSymbolSchema: z.ZodEffects<z.ZodObject<{
|
|
|
39
39
|
owner: z.ZodOptional<z.ZodString>;
|
|
40
40
|
descriptor: z.ZodOptional<z.ZodString>;
|
|
41
41
|
}, "strip", z.ZodTypeAny, {
|
|
42
|
-
kind: "symbol" | "class" | "method" | "field";
|
|
43
42
|
name: string;
|
|
44
|
-
|
|
43
|
+
kind: "symbol" | "class" | "field" | "method";
|
|
45
44
|
descriptor?: string | undefined;
|
|
45
|
+
owner?: string | undefined;
|
|
46
46
|
}, {
|
|
47
|
-
kind: "symbol" | "class" | "method" | "field";
|
|
48
47
|
name: string;
|
|
49
|
-
|
|
48
|
+
kind: "symbol" | "class" | "field" | "method";
|
|
50
49
|
descriptor?: string | undefined;
|
|
50
|
+
owner?: string | undefined;
|
|
51
51
|
}>;
|
|
52
52
|
version: z.ZodOptional<z.ZodString>;
|
|
53
53
|
sourceMapping: z.ZodOptional<z.ZodEnum<["obfuscated", "mojang", "intermediary", "yarn"]>>;
|
|
@@ -65,10 +65,10 @@ export declare const analyzeSymbolSchema: z.ZodEffects<z.ZodObject<{
|
|
|
65
65
|
nameMode: "auto" | "fqcn";
|
|
66
66
|
task: "map" | "workspace" | "exists" | "exact-map" | "lifecycle" | "api-overview";
|
|
67
67
|
subject: {
|
|
68
|
-
kind: "symbol" | "class" | "method" | "field";
|
|
69
68
|
name: string;
|
|
70
|
-
|
|
69
|
+
kind: "symbol" | "class" | "field" | "method";
|
|
71
70
|
descriptor?: string | undefined;
|
|
71
|
+
owner?: string | undefined;
|
|
72
72
|
};
|
|
73
73
|
signatureMode: "exact" | "name-only";
|
|
74
74
|
maxCandidates: number;
|
|
@@ -79,15 +79,15 @@ export declare const analyzeSymbolSchema: z.ZodEffects<z.ZodObject<{
|
|
|
79
79
|
classNameMapping?: "intermediary" | "mojang" | "yarn" | "obfuscated" | undefined;
|
|
80
80
|
detail?: "full" | "summary" | "standard" | undefined;
|
|
81
81
|
include?: string[] | undefined;
|
|
82
|
-
includeKinds?: ("class" | "
|
|
82
|
+
includeKinds?: ("class" | "field" | "method")[] | undefined;
|
|
83
83
|
maxRows?: number | undefined;
|
|
84
84
|
}, {
|
|
85
85
|
task: "map" | "workspace" | "exists" | "exact-map" | "lifecycle" | "api-overview";
|
|
86
86
|
subject: {
|
|
87
|
-
kind: "symbol" | "class" | "method" | "field";
|
|
88
87
|
name: string;
|
|
89
|
-
|
|
88
|
+
kind: "symbol" | "class" | "field" | "method";
|
|
90
89
|
descriptor?: string | undefined;
|
|
90
|
+
owner?: string | undefined;
|
|
91
91
|
};
|
|
92
92
|
projectPath?: string | undefined;
|
|
93
93
|
version?: string | undefined;
|
|
@@ -98,17 +98,17 @@ export declare const analyzeSymbolSchema: z.ZodEffects<z.ZodObject<{
|
|
|
98
98
|
detail?: "full" | "summary" | "standard" | undefined;
|
|
99
99
|
include?: string[] | undefined;
|
|
100
100
|
signatureMode?: "exact" | "name-only" | undefined;
|
|
101
|
-
includeKinds?: ("class" | "
|
|
101
|
+
includeKinds?: ("class" | "field" | "method")[] | undefined;
|
|
102
102
|
maxRows?: number | undefined;
|
|
103
103
|
maxCandidates?: number | undefined;
|
|
104
104
|
}>, {
|
|
105
105
|
nameMode: "auto" | "fqcn";
|
|
106
106
|
task: "map" | "workspace" | "exists" | "exact-map" | "lifecycle" | "api-overview";
|
|
107
107
|
subject: {
|
|
108
|
-
kind: "symbol" | "class" | "method" | "field";
|
|
109
108
|
name: string;
|
|
110
|
-
|
|
109
|
+
kind: "symbol" | "class" | "field" | "method";
|
|
111
110
|
descriptor?: string | undefined;
|
|
111
|
+
owner?: string | undefined;
|
|
112
112
|
};
|
|
113
113
|
signatureMode: "exact" | "name-only";
|
|
114
114
|
maxCandidates: number;
|
|
@@ -119,15 +119,15 @@ export declare const analyzeSymbolSchema: z.ZodEffects<z.ZodObject<{
|
|
|
119
119
|
classNameMapping?: "intermediary" | "mojang" | "yarn" | "obfuscated" | undefined;
|
|
120
120
|
detail?: "full" | "summary" | "standard" | undefined;
|
|
121
121
|
include?: string[] | undefined;
|
|
122
|
-
includeKinds?: ("class" | "
|
|
122
|
+
includeKinds?: ("class" | "field" | "method")[] | undefined;
|
|
123
123
|
maxRows?: number | undefined;
|
|
124
124
|
}, {
|
|
125
125
|
task: "map" | "workspace" | "exists" | "exact-map" | "lifecycle" | "api-overview";
|
|
126
126
|
subject: {
|
|
127
|
-
kind: "symbol" | "class" | "method" | "field";
|
|
128
127
|
name: string;
|
|
129
|
-
|
|
128
|
+
kind: "symbol" | "class" | "field" | "method";
|
|
130
129
|
descriptor?: string | undefined;
|
|
130
|
+
owner?: string | undefined;
|
|
131
131
|
};
|
|
132
132
|
projectPath?: string | undefined;
|
|
133
133
|
version?: string | undefined;
|
|
@@ -138,7 +138,7 @@ export declare const analyzeSymbolSchema: z.ZodEffects<z.ZodObject<{
|
|
|
138
138
|
detail?: "full" | "summary" | "standard" | undefined;
|
|
139
139
|
include?: string[] | undefined;
|
|
140
140
|
signatureMode?: "exact" | "name-only" | undefined;
|
|
141
|
-
includeKinds?: ("class" | "
|
|
141
|
+
includeKinds?: ("class" | "field" | "method")[] | undefined;
|
|
142
142
|
maxRows?: number | undefined;
|
|
143
143
|
maxCandidates?: number | undefined;
|
|
144
144
|
}>;
|