@adhisang/minecraft-modding-mcp 5.0.0 → 6.1.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 +55 -0
- package/dist/access-widener-parser.d.ts +1 -0
- package/dist/access-widener-parser.js +6 -3
- package/dist/cache-registry.js +35 -31
- package/dist/config.js +1 -1
- package/dist/decompiler/vineflower.js +8 -2
- package/dist/entry-tools/analyze-mod-service.js +2 -1
- package/dist/entry-tools/analyze-symbol-service.js +18 -9
- package/dist/entry-tools/batch-class-members-service.d.ts +2 -0
- package/dist/entry-tools/batch-class-members-service.js +1 -0
- package/dist/entry-tools/compare-minecraft-service.js +1 -1
- package/dist/entry-tools/inspect-minecraft/handlers/class-members.js +2 -1
- package/dist/entry-tools/inspect-minecraft/handlers/file.js +26 -1
- package/dist/entry-tools/inspect-minecraft/handlers/versions.js +2 -1
- package/dist/entry-tools/inspect-minecraft/internal.js +2 -0
- package/dist/entry-tools/response-contract.d.ts +2 -0
- package/dist/entry-tools/response-contract.js +2 -2
- package/dist/entry-tools/validate-project/cases/project-summary.js +30 -12
- package/dist/entry-tools/validate-project-service.d.ts +4 -4
- package/dist/entry-tools/verify-mixin-target-service.js +7 -7
- package/dist/index.js +21 -16
- package/dist/java-process.js +1 -1
- package/dist/json-rpc-framing.d.ts +4 -0
- package/dist/json-rpc-framing.js +23 -1
- package/dist/mapping/internal-types.d.ts +17 -0
- package/dist/mapping/loaders/tiny-maven.js +46 -20
- package/dist/mapping/lookup.d.ts +12 -1
- package/dist/mapping/lookup.js +53 -1
- package/dist/mapping-service.js +37 -22
- package/dist/minecraft-explorer-service.js +2 -1
- package/dist/mixin/annotation-validators.js +21 -3
- package/dist/mixin/parsed-validator.js +1 -0
- package/dist/mixin-parser.js +71 -3
- package/dist/mod-decompile-service.d.ts +2 -0
- package/dist/mod-decompile-service.js +19 -2
- package/dist/mod-remap-service.js +6 -5
- package/dist/mojang-tiny-mapping-service.js +5 -2
- package/dist/nbt/java-nbt-codec.js +40 -16
- package/dist/nbt/json-patch.js +51 -12
- package/dist/nbt/typed-json.d.ts +1 -0
- package/dist/nbt/typed-json.js +12 -2
- package/dist/nbt/types.d.ts +2 -2
- package/dist/path-converter.js +10 -3
- package/dist/registry-service.d.ts +2 -0
- package/dist/registry-service.js +16 -1
- package/dist/repo-downloader.js +4 -3
- package/dist/source/artifact-resolver.js +1 -1
- package/dist/source/class-source/members-builder.d.ts +26 -0
- package/dist/source/class-source/members-builder.js +31 -4
- package/dist/source/class-source/snippet-builder.js +6 -0
- package/dist/source/class-source-helpers.d.ts +4 -4
- package/dist/source/class-source-helpers.js +12 -11
- package/dist/source/class-source.d.ts +19 -0
- package/dist/source/class-source.js +87 -26
- package/dist/source/file-access.js +3 -2
- package/dist/source/indexer.js +24 -7
- package/dist/source/lifecycle/mapping-helpers.js +28 -3
- package/dist/source/lifecycle/runtime-check.js +20 -6
- package/dist/source/member-pattern.d.ts +16 -0
- package/dist/source/member-pattern.js +27 -0
- package/dist/source/search.js +23 -8
- package/dist/source/validate-mixin/pipeline/resolve.js +23 -3
- package/dist/source/workspace-target.js +2 -1
- package/dist/source-resolver.js +2 -2
- package/dist/source-service.d.ts +8 -4
- package/dist/source-service.js +9 -1
- package/dist/stdio-supervisor.d.ts +2 -2
- package/dist/stdio-supervisor.js +29 -9
- package/dist/storage/db.js +24 -1
- package/dist/storage/files-repo.d.ts +3 -0
- package/dist/storage/files-repo.js +43 -37
- package/dist/storage/symbols-repo.d.ts +9 -0
- package/dist/storage/symbols-repo.js +47 -19
- package/dist/symbols/symbol-extractor.js +1 -1
- package/dist/tool-guidance.d.ts +2 -1
- package/dist/tool-guidance.js +27 -2
- package/dist/tool-schemas.d.ts +15 -4
- package/dist/tool-schemas.js +102 -97
- package/dist/warning-details.d.ts +12 -0
- package/dist/warning-details.js +17 -0
- package/dist/workspace-mapping-service.js +9 -0
- package/docs/tool-reference.md +23 -0
- package/package.json +1 -1
package/dist/nbt/json-patch.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { isDeepStrictEqual } from "node:util";
|
|
2
2
|
import { createError, ERROR_CODES } from "../errors.js";
|
|
3
|
-
import { assertValidTypedNbtDocument, validateTypedNbtDocument } from "./typed-json.js";
|
|
3
|
+
import { assertValidTypedNbtDocument, validateTypedNbtDocument, validateTypedNbtNode } from "./typed-json.js";
|
|
4
4
|
function isRecord(value) {
|
|
5
5
|
return (typeof value === "object" &&
|
|
6
6
|
value !== null &&
|
|
@@ -136,6 +136,9 @@ function parseArrayIndex(token, length, options) {
|
|
|
136
136
|
}
|
|
137
137
|
return index;
|
|
138
138
|
}
|
|
139
|
+
function isTypedNbtNode(value) {
|
|
140
|
+
return isRecord(value) && typeof value.type === "string";
|
|
141
|
+
}
|
|
139
142
|
function resolveParent(root, tokens, opIndex, path) {
|
|
140
143
|
if (tokens.length === 0) {
|
|
141
144
|
patchConflict("Operation path does not reference a child location.", {
|
|
@@ -144,6 +147,8 @@ function resolveParent(root, tokens, opIndex, path) {
|
|
|
144
147
|
});
|
|
145
148
|
}
|
|
146
149
|
let cursor = root;
|
|
150
|
+
let enclosingNode;
|
|
151
|
+
let enclosingPointer = "";
|
|
147
152
|
for (let i = 0; i < tokens.length - 1; i += 1) {
|
|
148
153
|
const token = tokens[i];
|
|
149
154
|
const pointer = `/${tokens.slice(0, i + 1).join("/")}`;
|
|
@@ -154,6 +159,10 @@ function resolveParent(root, tokens, opIndex, path) {
|
|
|
154
159
|
path: pointer
|
|
155
160
|
});
|
|
156
161
|
cursor = cursor[index];
|
|
162
|
+
if (isTypedNbtNode(cursor)) {
|
|
163
|
+
enclosingNode = cursor;
|
|
164
|
+
enclosingPointer = pointer;
|
|
165
|
+
}
|
|
157
166
|
continue;
|
|
158
167
|
}
|
|
159
168
|
if (!isRecord(cursor)) {
|
|
@@ -169,8 +178,12 @@ function resolveParent(root, tokens, opIndex, path) {
|
|
|
169
178
|
});
|
|
170
179
|
}
|
|
171
180
|
cursor = cursor[token];
|
|
181
|
+
if (isTypedNbtNode(cursor)) {
|
|
182
|
+
enclosingNode = cursor;
|
|
183
|
+
enclosingPointer = pointer;
|
|
184
|
+
}
|
|
172
185
|
}
|
|
173
|
-
return { parent: cursor, key: tokens[tokens.length - 1] };
|
|
186
|
+
return { parent: cursor, key: tokens[tokens.length - 1], enclosingNode, enclosingPointer };
|
|
174
187
|
}
|
|
175
188
|
function readValueAtPath(root, tokens, opIndex, path) {
|
|
176
189
|
if (tokens.length === 0) {
|
|
@@ -205,8 +218,10 @@ function readValueAtPath(root, tokens, opIndex, path) {
|
|
|
205
218
|
}
|
|
206
219
|
return cursor;
|
|
207
220
|
}
|
|
208
|
-
function assertTypedNbtInvariant(
|
|
209
|
-
const validation =
|
|
221
|
+
function assertTypedNbtInvariant(subtree, opIndex, path) {
|
|
222
|
+
const validation = "root" in subtree
|
|
223
|
+
? validateTypedNbtDocument(subtree.root)
|
|
224
|
+
: validateTypedNbtNode(subtree.node, subtree.pointer);
|
|
210
225
|
if (!validation.ok) {
|
|
211
226
|
patchConflict("JSON Patch operation produced invalid typed NBT JSON.", {
|
|
212
227
|
opIndex,
|
|
@@ -241,11 +256,13 @@ export function applyJsonPatch(document, patch) {
|
|
|
241
256
|
}
|
|
242
257
|
if (operation.op === "add") {
|
|
243
258
|
const nextValue = structuredClone(operation.value);
|
|
259
|
+
let subtree = { root: working };
|
|
244
260
|
if (tokens.length === 0) {
|
|
245
261
|
working = nextValue;
|
|
262
|
+
subtree = { root: working };
|
|
246
263
|
}
|
|
247
264
|
else {
|
|
248
|
-
const { parent, key } = resolveParent(working, tokens, i, operation.path);
|
|
265
|
+
const { parent, key, enclosingNode, enclosingPointer } = resolveParent(working, tokens, i, operation.path);
|
|
249
266
|
if (Array.isArray(parent)) {
|
|
250
267
|
const index = parseArrayIndex(key, parent.length, {
|
|
251
268
|
allowAppend: true,
|
|
@@ -255,7 +272,12 @@ export function applyJsonPatch(document, patch) {
|
|
|
255
272
|
parent.splice(index, 0, nextValue);
|
|
256
273
|
}
|
|
257
274
|
else if (isRecord(parent)) {
|
|
258
|
-
parent
|
|
275
|
+
Object.defineProperty(parent, key, {
|
|
276
|
+
value: nextValue,
|
|
277
|
+
enumerable: true,
|
|
278
|
+
writable: true,
|
|
279
|
+
configurable: true
|
|
280
|
+
});
|
|
259
281
|
}
|
|
260
282
|
else {
|
|
261
283
|
patchConflict("Add target parent is not a container.", {
|
|
@@ -263,9 +285,13 @@ export function applyJsonPatch(document, patch) {
|
|
|
263
285
|
jsonPointer: operation.path
|
|
264
286
|
});
|
|
265
287
|
}
|
|
288
|
+
subtree =
|
|
289
|
+
enclosingNode === undefined
|
|
290
|
+
? { root: working }
|
|
291
|
+
: { node: enclosingNode, pointer: enclosingPointer };
|
|
266
292
|
}
|
|
267
293
|
changed = true;
|
|
268
|
-
assertTypedNbtInvariant(
|
|
294
|
+
assertTypedNbtInvariant(subtree, i, operation.path);
|
|
269
295
|
continue;
|
|
270
296
|
}
|
|
271
297
|
if (operation.op === "remove") {
|
|
@@ -275,7 +301,7 @@ export function applyJsonPatch(document, patch) {
|
|
|
275
301
|
jsonPointer: operation.path
|
|
276
302
|
});
|
|
277
303
|
}
|
|
278
|
-
const { parent, key } = resolveParent(working, tokens, i, operation.path);
|
|
304
|
+
const { parent, key, enclosingNode, enclosingPointer } = resolveParent(working, tokens, i, operation.path);
|
|
279
305
|
if (Array.isArray(parent)) {
|
|
280
306
|
const index = parseArrayIndex(key, parent.length, {
|
|
281
307
|
allowAppend: false,
|
|
@@ -300,16 +326,20 @@ export function applyJsonPatch(document, patch) {
|
|
|
300
326
|
});
|
|
301
327
|
}
|
|
302
328
|
changed = true;
|
|
303
|
-
assertTypedNbtInvariant(
|
|
329
|
+
assertTypedNbtInvariant(enclosingNode === undefined
|
|
330
|
+
? { root: working }
|
|
331
|
+
: { node: enclosingNode, pointer: enclosingPointer }, i, operation.path);
|
|
304
332
|
continue;
|
|
305
333
|
}
|
|
306
334
|
if (operation.op === "replace") {
|
|
307
335
|
const nextValue = structuredClone(operation.value);
|
|
336
|
+
let subtree = { root: working };
|
|
308
337
|
if (tokens.length === 0) {
|
|
309
338
|
working = nextValue;
|
|
339
|
+
subtree = { root: working };
|
|
310
340
|
}
|
|
311
341
|
else {
|
|
312
|
-
const { parent, key } = resolveParent(working, tokens, i, operation.path);
|
|
342
|
+
const { parent, key, enclosingNode, enclosingPointer } = resolveParent(working, tokens, i, operation.path);
|
|
313
343
|
if (Array.isArray(parent)) {
|
|
314
344
|
const index = parseArrayIndex(key, parent.length, {
|
|
315
345
|
allowAppend: false,
|
|
@@ -325,7 +355,12 @@ export function applyJsonPatch(document, patch) {
|
|
|
325
355
|
jsonPointer: operation.path
|
|
326
356
|
});
|
|
327
357
|
}
|
|
328
|
-
parent
|
|
358
|
+
Object.defineProperty(parent, key, {
|
|
359
|
+
value: nextValue,
|
|
360
|
+
enumerable: true,
|
|
361
|
+
writable: true,
|
|
362
|
+
configurable: true
|
|
363
|
+
});
|
|
329
364
|
}
|
|
330
365
|
else {
|
|
331
366
|
patchConflict("Replace target parent is not a container.", {
|
|
@@ -333,9 +368,13 @@ export function applyJsonPatch(document, patch) {
|
|
|
333
368
|
jsonPointer: operation.path
|
|
334
369
|
});
|
|
335
370
|
}
|
|
371
|
+
subtree =
|
|
372
|
+
enclosingNode === undefined
|
|
373
|
+
? { root: working }
|
|
374
|
+
: { node: enclosingNode, pointer: enclosingPointer };
|
|
336
375
|
}
|
|
337
376
|
changed = true;
|
|
338
|
-
assertTypedNbtInvariant(
|
|
377
|
+
assertTypedNbtInvariant(subtree, i, operation.path);
|
|
339
378
|
continue;
|
|
340
379
|
}
|
|
341
380
|
}
|
package/dist/nbt/typed-json.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ type ValidationResult = {
|
|
|
5
5
|
ok: false;
|
|
6
6
|
issue: TypedNbtValidationIssue;
|
|
7
7
|
};
|
|
8
|
+
export declare function validateTypedNbtNode(value: unknown, pointer: string): ValidationResult;
|
|
8
9
|
export declare function validateTypedNbtDocument(value: unknown): ValidationResult;
|
|
9
10
|
export declare function assertValidTypedNbtDocument(value: unknown): asserts value is TypedNbtDocument;
|
|
10
11
|
export type { TypedNbtDocument, TypedNbtValidationIssue } from "./types.js";
|
package/dist/nbt/typed-json.js
CHANGED
|
@@ -16,6 +16,7 @@ const NODE_TYPES = [
|
|
|
16
16
|
const LIST_ELEMENT_TYPES = ["end", ...NODE_TYPES];
|
|
17
17
|
const NODE_TYPE_SET = new Set(NODE_TYPES);
|
|
18
18
|
const LIST_ELEMENT_TYPE_SET = new Set(LIST_ELEMENT_TYPES);
|
|
19
|
+
const NON_FINITE_FLOAT_SENTINELS = new Set(["NaN", "Infinity", "-Infinity"]);
|
|
19
20
|
const INT64_MIN = -9223372036854775808n;
|
|
20
21
|
const INT64_MAX = 9223372036854775807n;
|
|
21
22
|
function isRecord(value) {
|
|
@@ -119,8 +120,14 @@ function validateNode(value, pointer) {
|
|
|
119
120
|
return validateLongString(node.value, `${pointer}/value`);
|
|
120
121
|
case "float":
|
|
121
122
|
case "double":
|
|
122
|
-
if (typeof node.value
|
|
123
|
-
|
|
123
|
+
if (typeof node.value === "string") {
|
|
124
|
+
if (!NON_FINITE_FLOAT_SENTINELS.has(node.value)) {
|
|
125
|
+
return fail(`${pointer}/value`, "number-or-non-finite-sentinel", node.value);
|
|
126
|
+
}
|
|
127
|
+
return { ok: true };
|
|
128
|
+
}
|
|
129
|
+
if (typeof node.value !== "number") {
|
|
130
|
+
return fail(`${pointer}/value`, "number-or-non-finite-sentinel", node.value);
|
|
124
131
|
}
|
|
125
132
|
return { ok: true };
|
|
126
133
|
case "string":
|
|
@@ -180,6 +187,9 @@ function validateNode(value, pointer) {
|
|
|
180
187
|
return fail(`${pointer}/type`, "nbt-node-type", nodeType);
|
|
181
188
|
}
|
|
182
189
|
}
|
|
190
|
+
export function validateTypedNbtNode(value, pointer) {
|
|
191
|
+
return validateNode(value, pointer);
|
|
192
|
+
}
|
|
183
193
|
export function validateTypedNbtDocument(value) {
|
|
184
194
|
if (!isRecord(value)) {
|
|
185
195
|
return fail("", "typed-nbt-document", value);
|
package/dist/nbt/types.d.ts
CHANGED
|
@@ -15,10 +15,10 @@ export type NbtNode = {
|
|
|
15
15
|
value: string;
|
|
16
16
|
} | {
|
|
17
17
|
type: "float";
|
|
18
|
-
value: number;
|
|
18
|
+
value: number | "NaN" | "Infinity" | "-Infinity";
|
|
19
19
|
} | {
|
|
20
20
|
type: "double";
|
|
21
|
-
value: number;
|
|
21
|
+
value: number | "NaN" | "Infinity" | "-Infinity";
|
|
22
22
|
} | {
|
|
23
23
|
type: "byteArray";
|
|
24
24
|
value: number[];
|
package/dist/path-converter.js
CHANGED
|
@@ -3,7 +3,7 @@ import { createError, ERROR_CODES } from "./errors.js";
|
|
|
3
3
|
const WINDOWS_DRIVE_PATH = /^[A-Za-z]:[\\/]/;
|
|
4
4
|
const MALFORMED_WINDOWS_DRIVE_PATH = /^[A-Za-z]:(?![\\/])/;
|
|
5
5
|
const WSL_MOUNT_PATH = /^\/mnt\/[a-z](?:\/|$)/i;
|
|
6
|
-
const UNC_WSL_PATH = /^(?:\\\\wsl
|
|
6
|
+
const UNC_WSL_PATH = /^(?:\\\\(?:wsl\$|wsl\.localhost)\\|\/\/(?:wsl\$|wsl\.localhost)\/)/i;
|
|
7
7
|
function normalizeToUnixSlashes(value) {
|
|
8
8
|
return value.replace(/\\/g, "/");
|
|
9
9
|
}
|
|
@@ -12,10 +12,11 @@ function normalizeToWindowsSlashes(value) {
|
|
|
12
12
|
}
|
|
13
13
|
function parseUncWslPath(pathValue) {
|
|
14
14
|
const normalized = normalizeToUnixSlashes(pathValue);
|
|
15
|
-
|
|
15
|
+
const prefixMatch = normalized.match(/^\/\/(?:wsl\$|wsl\.localhost)\//i);
|
|
16
|
+
if (!prefixMatch) {
|
|
16
17
|
return undefined;
|
|
17
18
|
}
|
|
18
|
-
const remainder = normalized.slice(
|
|
19
|
+
const remainder = normalized.slice(prefixMatch[0].length).replace(/^\/+/, "");
|
|
19
20
|
const slashIndex = remainder.indexOf("/");
|
|
20
21
|
if (slashIndex < 0) {
|
|
21
22
|
return {
|
|
@@ -99,6 +100,12 @@ function toWslPath(pathValue, runtime, field) {
|
|
|
99
100
|
}
|
|
100
101
|
const parsed = parseUncWslPath(pathValue);
|
|
101
102
|
if (parsed) {
|
|
103
|
+
const distro = runtime.wslDistro?.trim();
|
|
104
|
+
if (!distro || parsed.distro.toLowerCase() !== distro.toLowerCase()) {
|
|
105
|
+
throwInvalidPath(pathValue, distro
|
|
106
|
+
? `UNC WSL path targets distro "${parsed.distro}", but this server runs in distro "${distro}".`
|
|
107
|
+
: `Cannot resolve UNC WSL path for distro "${parsed.distro}" without a known WSL distro name.`, runtime, field);
|
|
108
|
+
}
|
|
102
109
|
const rest = parsed.innerPath.replace(/^\/+/, "");
|
|
103
110
|
return rest ? `/${rest}` : "/";
|
|
104
111
|
}
|
|
@@ -28,9 +28,11 @@ export declare class RegistryService {
|
|
|
28
28
|
private readonly config;
|
|
29
29
|
private readonly versionService;
|
|
30
30
|
private readonly registryCache;
|
|
31
|
+
private readonly loadLocks;
|
|
31
32
|
constructor(config: Config, versionService: VersionService);
|
|
32
33
|
getRegistryData(input: GetRegistryDataInput): Promise<GetRegistryDataOutput>;
|
|
33
34
|
private loadRegistries;
|
|
35
|
+
private loadRegistriesInternal;
|
|
34
36
|
private loadExistingRegistries;
|
|
35
37
|
private readRegistryFileOrThrow;
|
|
36
38
|
private isInvalidRegistrySnapshot;
|
package/dist/registry-service.js
CHANGED
|
@@ -126,7 +126,7 @@ function runDataGen(serverJarPath, outputDir, version) {
|
|
|
126
126
|
];
|
|
127
127
|
log("info", "registry.datagen.start", { version, isLegacy, serverJarPath, outputDir });
|
|
128
128
|
const proc = spawn("java", args, {
|
|
129
|
-
stdio: ["ignore", "
|
|
129
|
+
stdio: ["ignore", "ignore", "pipe"],
|
|
130
130
|
cwd: outputDir
|
|
131
131
|
});
|
|
132
132
|
let stderr = "";
|
|
@@ -194,6 +194,7 @@ export class RegistryService {
|
|
|
194
194
|
config;
|
|
195
195
|
versionService;
|
|
196
196
|
registryCache = new Map();
|
|
197
|
+
loadLocks = new Map();
|
|
197
198
|
constructor(config, versionService) {
|
|
198
199
|
this.config = config;
|
|
199
200
|
this.versionService = versionService;
|
|
@@ -270,6 +271,20 @@ export class RegistryService {
|
|
|
270
271
|
};
|
|
271
272
|
}
|
|
272
273
|
async loadRegistries(version, warnings) {
|
|
274
|
+
const existingLock = this.loadLocks.get(version);
|
|
275
|
+
if (existingLock) {
|
|
276
|
+
return existingLock;
|
|
277
|
+
}
|
|
278
|
+
const loadPromise = this.loadRegistriesInternal(version, warnings);
|
|
279
|
+
this.loadLocks.set(version, loadPromise);
|
|
280
|
+
try {
|
|
281
|
+
return await loadPromise;
|
|
282
|
+
}
|
|
283
|
+
finally {
|
|
284
|
+
this.loadLocks.delete(version);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
async loadRegistriesInternal(version, warnings) {
|
|
273
288
|
const cached = this.registryCache.get(version);
|
|
274
289
|
if (cached) {
|
|
275
290
|
// LRU touch: move to most-recently-used so the bound evicts the true
|
package/dist/repo-downloader.js
CHANGED
|
@@ -40,7 +40,6 @@ export async function downloadToCache(url, destinationPath, opts = {}) {
|
|
|
40
40
|
const timer = setTimeout(() => timeout.abort(), timeoutMs);
|
|
41
41
|
try {
|
|
42
42
|
const response = await fetchFn(url, { signal: timeout.signal });
|
|
43
|
-
clearTimeout(timer);
|
|
44
43
|
const status = response.status;
|
|
45
44
|
if (status === 404) {
|
|
46
45
|
return { ok: false, statusCode: status };
|
|
@@ -71,7 +70,7 @@ export async function downloadToCache(url, destinationPath, opts = {}) {
|
|
|
71
70
|
}
|
|
72
71
|
else {
|
|
73
72
|
const readable = Readable.fromWeb(response.body);
|
|
74
|
-
await pipeline(readable, createWriteStream(tempPath));
|
|
73
|
+
await pipeline(readable, createWriteStream(tempPath), { signal: timeout.signal });
|
|
75
74
|
}
|
|
76
75
|
const contentLength = statSync(tempPath).size;
|
|
77
76
|
renameSync(tempPath, destinationPath);
|
|
@@ -95,13 +94,15 @@ export async function downloadToCache(url, destinationPath, opts = {}) {
|
|
|
95
94
|
}
|
|
96
95
|
}
|
|
97
96
|
catch (caughtError) {
|
|
98
|
-
clearTimeout(timer);
|
|
99
97
|
if (attempt >= maxRetries) {
|
|
100
98
|
throw caughtError instanceof Error ? caughtError : new Error(String(caughtError));
|
|
101
99
|
}
|
|
102
100
|
await sleep(retryDelay(200, attempt));
|
|
103
101
|
attempt += 1;
|
|
104
102
|
}
|
|
103
|
+
finally {
|
|
104
|
+
clearTimeout(timer);
|
|
105
|
+
}
|
|
105
106
|
}
|
|
106
107
|
}
|
|
107
108
|
export function defaultDownloadPath(cacheDir, url) {
|
|
@@ -54,7 +54,7 @@ export function hasExactVersionToken(path, version) {
|
|
|
54
54
|
// Avoid prefix false-positives like "1.21.1" matching "1.21.10".
|
|
55
55
|
const cached = VERSION_TOKEN_REGEX_CACHE.get(normalizedVersion);
|
|
56
56
|
const pattern = cached
|
|
57
|
-
?? rememberCachedRegex(VERSION_TOKEN_REGEX_CACHE, normalizedVersion, new RegExp(`(^|[^0-9a-z])${escapeRegexLiteral(normalizedVersion)}([
|
|
57
|
+
?? rememberCachedRegex(VERSION_TOKEN_REGEX_CACHE, normalizedVersion, new RegExp(`(^|[^0-9a-z])${escapeRegexLiteral(normalizedVersion)}(?![0-9a-z]|\\.[0-9])`, "i"));
|
|
58
58
|
return pattern.test(normalizedPath);
|
|
59
59
|
}
|
|
60
60
|
function inferMergedRuntimeNamespaceHint(path) {
|
|
@@ -76,3 +76,29 @@ export declare function projectMembersForWire(slice: {
|
|
|
76
76
|
fields: SignatureMember[];
|
|
77
77
|
methods: SignatureMember[];
|
|
78
78
|
}, includeInherited: boolean, keepFieldDescriptors?: boolean): WireMembersBlock;
|
|
79
|
+
/**
|
|
80
|
+
* Token-projection level for a member listing, narrowing the per-member fields
|
|
81
|
+
* an agent receives:
|
|
82
|
+
* - `full` (default): the complete {@link WireMember} shape (current behavior).
|
|
83
|
+
* - `signatures`: name + javaSignature (drops jvmDescriptor) — enough to read
|
|
84
|
+
* the API without overload descriptors.
|
|
85
|
+
* - `names`: member name only — enough for an "does this member exist?" check.
|
|
86
|
+
* The block-level hoisted `ownerFqn` and any per-member `ownerFqn` (the
|
|
87
|
+
* multi-owner / includeInherited case) are preserved in every level so members
|
|
88
|
+
* stay attributable.
|
|
89
|
+
*/
|
|
90
|
+
export type MemberProjection = "names" | "signatures" | "full";
|
|
91
|
+
export type ProjectedMember = {
|
|
92
|
+
name: string;
|
|
93
|
+
javaSignature?: string;
|
|
94
|
+
jvmDescriptor?: string;
|
|
95
|
+
ownerFqn?: string;
|
|
96
|
+
isSynthetic?: boolean;
|
|
97
|
+
};
|
|
98
|
+
export type ProjectedMembersBlock = {
|
|
99
|
+
ownerFqn?: string;
|
|
100
|
+
constructors: ProjectedMember[];
|
|
101
|
+
fields: ProjectedMember[];
|
|
102
|
+
methods: ProjectedMember[];
|
|
103
|
+
};
|
|
104
|
+
export declare function projectMembersByLevel(block: WireMembersBlock, level: MemberProjection): ProjectedMembersBlock;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { matchesMemberPattern } from "../member-pattern.js";
|
|
1
2
|
export async function remapAndCountMembers(svc, input) {
|
|
2
3
|
const remap = async (members, kind) => {
|
|
3
4
|
if (input.version == null) {
|
|
@@ -10,10 +11,10 @@ export async function remapAndCountMembers(svc, input) {
|
|
|
10
11
|
let fields = await remap(input.signatureFields, "field");
|
|
11
12
|
let methods = await remap(input.signatureMethods, "method");
|
|
12
13
|
if (input.requestedMapping !== input.mappingApplied && input.memberPattern) {
|
|
13
|
-
const
|
|
14
|
-
constructors = constructors.filter((m) => m.name
|
|
15
|
-
fields = fields.filter((m) => m.name
|
|
16
|
-
methods = methods.filter((m) => m.name
|
|
14
|
+
const pattern = input.memberPattern;
|
|
15
|
+
constructors = constructors.filter((m) => matchesMemberPattern(m.name, pattern));
|
|
16
|
+
fields = fields.filter((m) => matchesMemberPattern(m.name, pattern));
|
|
17
|
+
methods = methods.filter((m) => matchesMemberPattern(m.name, pattern));
|
|
17
18
|
}
|
|
18
19
|
const counts = {
|
|
19
20
|
constructors: constructors.length,
|
|
@@ -90,4 +91,30 @@ export function projectMembersForWire(slice, includeInherited, keepFieldDescript
|
|
|
90
91
|
methods: slice.methods.map((m) => toWire(m, true))
|
|
91
92
|
};
|
|
92
93
|
}
|
|
94
|
+
export function projectMembersByLevel(block, level) {
|
|
95
|
+
if (level === "full") {
|
|
96
|
+
return block;
|
|
97
|
+
}
|
|
98
|
+
const reduce = (m) => {
|
|
99
|
+
if (level === "names") {
|
|
100
|
+
return {
|
|
101
|
+
name: m.name,
|
|
102
|
+
...(m.ownerFqn ? { ownerFqn: m.ownerFqn } : {})
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
// "signatures": keep the readable signature, drop the JVM descriptor.
|
|
106
|
+
return {
|
|
107
|
+
name: m.name,
|
|
108
|
+
javaSignature: m.javaSignature,
|
|
109
|
+
...(m.ownerFqn ? { ownerFqn: m.ownerFqn } : {}),
|
|
110
|
+
...(m.isSynthetic ? { isSynthetic: true } : {})
|
|
111
|
+
};
|
|
112
|
+
};
|
|
113
|
+
return {
|
|
114
|
+
...(block.ownerFqn ? { ownerFqn: block.ownerFqn } : {}),
|
|
115
|
+
constructors: block.constructors.map(reduce),
|
|
116
|
+
fields: block.fields.map(reduce),
|
|
117
|
+
methods: block.methods.map(reduce)
|
|
118
|
+
};
|
|
119
|
+
}
|
|
93
120
|
//# sourceMappingURL=members-builder.js.map
|
|
@@ -53,6 +53,12 @@ export function buildClassSourceSnippet(input) {
|
|
|
53
53
|
sourceText = sliceToMaxCharsSafe(sourceText, input.maxChars);
|
|
54
54
|
charsTruncated = true;
|
|
55
55
|
truncated = true;
|
|
56
|
+
if (input.mode !== "metadata") {
|
|
57
|
+
const survivingNewlines = (sourceText.match(/\n/g) ?? []).length;
|
|
58
|
+
returnedEnd = sourceText.endsWith("\n")
|
|
59
|
+
? returnedStart + survivingNewlines - 1
|
|
60
|
+
: returnedStart + survivingNewlines;
|
|
61
|
+
}
|
|
56
62
|
}
|
|
57
63
|
let nextStartLine;
|
|
58
64
|
// Metadata mode returns a synthesized outline, not a line window into the
|
|
@@ -9,16 +9,16 @@ export declare function extractDecompiledMembers(className: string, filePath: st
|
|
|
9
9
|
fields: DecompiledMember[];
|
|
10
10
|
methods: DecompiledMember[];
|
|
11
11
|
};
|
|
12
|
-
export declare function computeLineBraceDepths(lines: string[]): number[];
|
|
12
|
+
export declare function computeLineBraceDepths(lines: string[], strippedLines?: string[]): number[];
|
|
13
13
|
export declare function computeBraceRange(lines: string[], symbols: Array<{
|
|
14
14
|
symbolKind: string;
|
|
15
15
|
symbolName: string;
|
|
16
16
|
line: number;
|
|
17
|
-
}>, simpleName: string): {
|
|
17
|
+
}>, simpleName: string, strippedLines?: string[]): {
|
|
18
18
|
declarationLine: number;
|
|
19
19
|
endLine: number;
|
|
20
20
|
} | undefined;
|
|
21
|
-
export declare function scanBraceRange(lines: string[], declarationLine: number): {
|
|
21
|
+
export declare function scanBraceRange(lines: string[], declarationLine: number, strippedLines?: string[]): {
|
|
22
22
|
declarationLine: number;
|
|
23
23
|
endLine: number;
|
|
24
24
|
};
|
|
@@ -28,7 +28,7 @@ export declare function computeNestedTypeRanges(lines: string[], symbols: Array<
|
|
|
28
28
|
}>, outerBody: {
|
|
29
29
|
declarationLine: number;
|
|
30
30
|
endLine: number;
|
|
31
|
-
}): Array<{
|
|
31
|
+
}, strippedLines?: string[]): Array<{
|
|
32
32
|
declarationLine: number;
|
|
33
33
|
endLine: number;
|
|
34
34
|
}>;
|
|
@@ -30,13 +30,14 @@ export function extractDecompiledMembers(className, filePath, content) {
|
|
|
30
30
|
const symbols = extractSymbolsFromSource(filePath, content);
|
|
31
31
|
const simpleName = className.split(/[.$]/).at(-1) ?? className;
|
|
32
32
|
const lines = content.split(/\r?\n/);
|
|
33
|
-
const
|
|
33
|
+
const stripped = stripBraceNoise(lines);
|
|
34
|
+
const body = computeBraceRange(lines, symbols, simpleName, stripped);
|
|
34
35
|
if (!body) {
|
|
35
36
|
return { constructors: [], fields: [], methods: [] };
|
|
36
37
|
}
|
|
37
|
-
const depths = computeLineBraceDepths(lines);
|
|
38
|
+
const depths = computeLineBraceDepths(lines, stripped);
|
|
38
39
|
const baseDepth = depths[body.declarationLine - 1] ?? 0;
|
|
39
|
-
const nestedRanges = computeNestedTypeRanges(lines, symbols, body);
|
|
40
|
+
const nestedRanges = computeNestedTypeRanges(lines, symbols, body, stripped);
|
|
40
41
|
const constructors = [];
|
|
41
42
|
const fields = [];
|
|
42
43
|
const methods = [];
|
|
@@ -126,8 +127,8 @@ function stripBraceNoise(lines) {
|
|
|
126
127
|
}
|
|
127
128
|
return out;
|
|
128
129
|
}
|
|
129
|
-
export function computeLineBraceDepths(lines) {
|
|
130
|
-
const stripped = stripBraceNoise(lines);
|
|
130
|
+
export function computeLineBraceDepths(lines, strippedLines) {
|
|
131
|
+
const stripped = strippedLines ?? stripBraceNoise(lines);
|
|
131
132
|
const depths = new Array(lines.length).fill(0);
|
|
132
133
|
let depth = 0;
|
|
133
134
|
for (let i = 0; i < stripped.length; i += 1) {
|
|
@@ -144,19 +145,19 @@ export function computeLineBraceDepths(lines) {
|
|
|
144
145
|
}
|
|
145
146
|
return depths;
|
|
146
147
|
}
|
|
147
|
-
export function computeBraceRange(lines, symbols, simpleName) {
|
|
148
|
+
export function computeBraceRange(lines, symbols, simpleName, strippedLines) {
|
|
148
149
|
const classSymbol = symbols.find((symbol) => (symbol.symbolKind === "class" || symbol.symbolKind === "interface"
|
|
149
150
|
|| symbol.symbolKind === "enum" || symbol.symbolKind === "record")
|
|
150
151
|
&& symbol.symbolName === simpleName);
|
|
151
152
|
if (!classSymbol) {
|
|
152
153
|
return undefined;
|
|
153
154
|
}
|
|
154
|
-
return scanBraceRange(lines, classSymbol.line);
|
|
155
|
+
return scanBraceRange(lines, classSymbol.line, strippedLines);
|
|
155
156
|
}
|
|
156
|
-
export function scanBraceRange(lines, declarationLine) {
|
|
157
|
+
export function scanBraceRange(lines, declarationLine, strippedLines) {
|
|
157
158
|
// Strip from the start of the file so multi-line block-comment state is correct
|
|
158
159
|
// by the time we reach declarationLine; indices stay aligned with `lines`.
|
|
159
|
-
const stripped = stripBraceNoise(lines);
|
|
160
|
+
const stripped = strippedLines ?? stripBraceNoise(lines);
|
|
160
161
|
let depth = 0;
|
|
161
162
|
let started = false;
|
|
162
163
|
for (let i = declarationLine - 1; i < stripped.length; i += 1) {
|
|
@@ -175,7 +176,7 @@ export function scanBraceRange(lines, declarationLine) {
|
|
|
175
176
|
}
|
|
176
177
|
return { declarationLine, endLine: lines.length };
|
|
177
178
|
}
|
|
178
|
-
export function computeNestedTypeRanges(lines, symbols, outerBody) {
|
|
179
|
+
export function computeNestedTypeRanges(lines, symbols, outerBody, strippedLines) {
|
|
179
180
|
const ranges = [];
|
|
180
181
|
for (const candidate of symbols) {
|
|
181
182
|
if (candidate.symbolKind !== "class" && candidate.symbolKind !== "interface"
|
|
@@ -188,7 +189,7 @@ export function computeNestedTypeRanges(lines, symbols, outerBody) {
|
|
|
188
189
|
if (ranges.some((range) => candidate.line >= range.declarationLine && candidate.line <= range.endLine)) {
|
|
189
190
|
continue;
|
|
190
191
|
}
|
|
191
|
-
const nestedRange = scanBraceRange(lines, candidate.line);
|
|
192
|
+
const nestedRange = scanBraceRange(lines, candidate.line, strippedLines);
|
|
192
193
|
ranges.push(nestedRange);
|
|
193
194
|
}
|
|
194
195
|
return ranges;
|
|
@@ -2,6 +2,24 @@ import { type AppError } from "../errors.js";
|
|
|
2
2
|
import type { SourceService } from "../source-service.js";
|
|
3
3
|
import type { DecompiledFallback, FindClassInput, FindClassOutput, GetClassMembersInput, GetClassMembersOutput, GetClassSourceInput, GetClassSourceOutput } from "../source-service.js";
|
|
4
4
|
import type { ArtifactProvenance, ArtifactScope, MappingSourcePriority, ResolvedSourceArtifact, SourceMapping } from "../types.js";
|
|
5
|
+
/**
|
|
6
|
+
* Unobfuscated Minecraft versions (26.1+) ship their runtime and decompiled source
|
|
7
|
+
* in deobfuscated (mojang) names directly — there is no real obfuscated namespace to
|
|
8
|
+
* map to. An artifact whose stored namespace label is "obfuscated" is therefore a
|
|
9
|
+
* mislabel for a mojang request: the bytes already carry mojang names.
|
|
10
|
+
*
|
|
11
|
+
* Without this reconciliation, requestedMapping="mojang" !== mappingApplied="obfuscated"
|
|
12
|
+
* triggers a cascade of doomed work: resolveClassNameForLookup attempts a mojang->obfuscated
|
|
13
|
+
* class remap, remapSignatureMembers tries to remap every member obfuscated->mojang and drops
|
|
14
|
+
* them all (counts.total===0), which forces a spurious decompiledFallback, disables
|
|
15
|
+
* memberPattern, and floods the response with one "Could not remap ..." warning per member.
|
|
16
|
+
*
|
|
17
|
+
* Collapsing mappingApplied to the requested mojang namespace makes the whole pipeline an
|
|
18
|
+
* identity no-op: members are returned from bytecode with correct mojang names, memberPattern
|
|
19
|
+
* applies, and the per-member warning flood disappears. This is the single highest-impact
|
|
20
|
+
* token-efficiency fix for unobfuscated versions.
|
|
21
|
+
*/
|
|
22
|
+
export declare function reconcileUnobfuscatedNamespace(version: string | undefined, requestedMapping: SourceMapping, mappingApplied: SourceMapping): SourceMapping;
|
|
5
23
|
export declare function resolveClassFilePath(svc: SourceService, artifactId: string, className: string): string | undefined;
|
|
6
24
|
export declare function resolveClassNameForLookup(svc: SourceService, input: {
|
|
7
25
|
className: string;
|
|
@@ -37,6 +55,7 @@ export declare function buildClassSourceNotFoundError(_svc: SourceService, input
|
|
|
37
55
|
export declare function buildDecompiledFallback(svc: SourceService, artifactId: string, lookupClassName: string, memberPattern: string | undefined, maxMembers: number): {
|
|
38
56
|
fallback: DecompiledFallback;
|
|
39
57
|
counts: NonNullable<GetClassMembersOutput["decompiledMemberCounts"]>;
|
|
58
|
+
truncated: boolean;
|
|
40
59
|
} | undefined;
|
|
41
60
|
export declare function findClass(svc: SourceService, input: FindClassInput): FindClassOutput;
|
|
42
61
|
export declare function getClassSource(svc: SourceService, input: GetClassSourceInput): Promise<GetClassSourceOutput>;
|