@adhisang/minecraft-modding-mcp 4.2.1 → 6.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +69 -0
- package/README.md +5 -4
- package/dist/access-transformer-parser.d.ts +2 -1
- package/dist/access-transformer-parser.js +10 -1
- package/dist/access-widener-parser.d.ts +1 -0
- package/dist/access-widener-parser.js +6 -3
- package/dist/build-suggested-call.js +31 -10
- package/dist/cache-registry.d.ts +8 -0
- package/dist/cache-registry.js +50 -32
- package/dist/config.d.ts +1 -0
- package/dist/config.js +4 -0
- package/dist/decompiler/vineflower.js +8 -2
- package/dist/entry-tools/analyze-mod-service.d.ts +24 -24
- package/dist/entry-tools/analyze-mod-service.js +2 -1
- package/dist/entry-tools/analyze-symbol-service.d.ts +57 -22
- package/dist/entry-tools/analyze-symbol-service.js +139 -34
- package/dist/entry-tools/batch-class-members-service.d.ts +3 -1
- package/dist/entry-tools/batch-class-members-service.js +6 -6
- package/dist/entry-tools/batch-class-source-service.d.ts +3 -1
- package/dist/entry-tools/batch-class-source-service.js +5 -6
- package/dist/entry-tools/batch-mappings-service.d.ts +3 -1
- package/dist/entry-tools/batch-mappings-service.js +4 -5
- package/dist/entry-tools/batch-symbol-exists-service.d.ts +3 -1
- package/dist/entry-tools/batch-symbol-exists-service.js +4 -5
- package/dist/entry-tools/compare-minecraft-service.d.ts +10 -10
- package/dist/entry-tools/compare-minecraft-service.js +1 -1
- package/dist/entry-tools/entry-tool-schema.d.ts +1 -1
- package/dist/entry-tools/inspect-minecraft/handlers/artifact.js +1 -3
- package/dist/entry-tools/inspect-minecraft/handlers/class-members.js +4 -3
- package/dist/entry-tools/inspect-minecraft/handlers/class-overview.js +0 -5
- package/dist/entry-tools/inspect-minecraft/handlers/class-source.js +0 -1
- package/dist/entry-tools/inspect-minecraft/handlers/file.js +26 -2
- package/dist/entry-tools/inspect-minecraft/handlers/list-files.js +0 -1
- package/dist/entry-tools/inspect-minecraft/handlers/search.d.ts +8 -0
- package/dist/entry-tools/inspect-minecraft/handlers/search.js +13 -2
- package/dist/entry-tools/inspect-minecraft/handlers/versions.d.ts +1 -1
- package/dist/entry-tools/inspect-minecraft/handlers/versions.js +3 -2
- package/dist/entry-tools/inspect-minecraft/internal.d.ts +90 -89
- package/dist/entry-tools/inspect-minecraft/internal.js +32 -14
- package/dist/entry-tools/inspect-minecraft-service.d.ts +218 -218
- package/dist/entry-tools/inspect-minecraft-service.js +1 -1
- package/dist/entry-tools/manage-cache-service.d.ts +4 -4
- package/dist/entry-tools/response-contract.d.ts +3 -1
- package/dist/entry-tools/response-contract.js +3 -2
- package/dist/entry-tools/validate-project/cases/mixin.js +2 -1
- package/dist/entry-tools/validate-project/cases/project-summary.js +5 -3
- package/dist/entry-tools/validate-project-service.d.ts +42 -36
- package/dist/entry-tools/validate-project-service.js +3 -1
- package/dist/entry-tools/verify-mixin-target-service.d.ts +30 -0
- package/dist/entry-tools/verify-mixin-target-service.js +120 -29
- package/dist/error-mapping.d.ts +51 -0
- package/dist/error-mapping.js +157 -2
- package/dist/index.js +147 -76
- 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 +24 -0
- package/dist/mapping/loaders/tiny-maven.js +46 -20
- package/dist/mapping/lookup.d.ts +13 -2
- package/dist/mapping/lookup.js +60 -2
- package/dist/mapping/types.d.ts +6 -0
- package/dist/mapping-service.d.ts +26 -2
- package/dist/mapping-service.js +212 -55
- package/dist/mcp-helpers.d.ts +1 -0
- package/dist/mcp-helpers.js +25 -3
- package/dist/minecraft-explorer-service.js +24 -11
- package/dist/mixin/access-validators.js +33 -1
- package/dist/mixin/annotation-validators.js +73 -9
- package/dist/mixin/parsed-validator.js +3 -2
- package/dist/mixin-parser.js +119 -7
- package/dist/mod-decompile-service.d.ts +2 -0
- package/dist/mod-decompile-service.js +21 -3
- package/dist/mod-remap-service.d.ts +20 -1
- package/dist/mod-remap-service.js +101 -45
- package/dist/mojang-tiny-mapping-service.js +5 -2
- package/dist/nbt/java-nbt-codec.js +46 -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/observability.d.ts +6 -0
- package/dist/observability.js +10 -0
- package/dist/page-cursor.d.ts +17 -0
- package/dist/page-cursor.js +0 -0
- package/dist/path-converter.js +10 -3
- package/dist/registry-service.d.ts +2 -0
- package/dist/registry-service.js +41 -2
- package/dist/repo-downloader.js +4 -3
- package/dist/resources.js +45 -9
- package/dist/response-utils.d.ts +29 -4
- package/dist/response-utils.js +95 -5
- package/dist/source/access-validate.js +12 -2
- package/dist/source/artifact-resolver.js +2 -2
- package/dist/source/class-source/members-builder.d.ts +44 -1
- package/dist/source/class-source/members-builder.js +51 -4
- package/dist/source/class-source/snippet-builder.d.ts +13 -0
- package/dist/source/class-source/snippet-builder.js +54 -4
- package/dist/source/class-source-helpers.d.ts +4 -4
- package/dist/source/class-source-helpers.js +77 -20
- package/dist/source/class-source.d.ts +19 -0
- package/dist/source/class-source.js +144 -40
- package/dist/source/file-access.js +3 -2
- package/dist/source/indexer.js +29 -9
- package/dist/source/lifecycle/mapping-helpers.js +28 -3
- package/dist/source/lifecycle/runtime-check.js +20 -6
- package/dist/source/search.d.ts +1 -1
- package/dist/source/search.js +117 -30
- package/dist/source/validate-mixin/pipeline/resolve.js +23 -3
- package/dist/source/validate-mixin.d.ts +2 -1
- package/dist/source/validate-mixin.js +45 -4
- package/dist/source/workspace-target.js +17 -10
- package/dist/source-jar-reader.d.ts +54 -1
- package/dist/source-jar-reader.js +121 -10
- package/dist/source-resolver.js +2 -2
- package/dist/source-service.d.ts +28 -7
- 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 +15 -0
- package/dist/storage/files-repo.js +67 -37
- package/dist/storage/symbols-repo.d.ts +15 -0
- package/dist/storage/symbols-repo.js +53 -19
- package/dist/symbols/symbol-extractor.js +35 -6
- package/dist/text-truncate.d.ts +7 -0
- package/dist/text-truncate.js +21 -0
- package/dist/tiny-remapper-resolver.js +10 -0
- package/dist/tool-contract-manifest.js +2 -2
- package/dist/tool-execution-gate.js +12 -1
- package/dist/tool-guidance.d.ts +6 -2
- package/dist/tool-guidance.js +63 -13
- package/dist/tool-schemas.d.ts +561 -496
- package/dist/tool-schemas.js +175 -139
- package/dist/types.d.ts +1 -0
- package/dist/version-diff-service.js +5 -0
- package/dist/version-service.d.ts +3 -0
- package/dist/version-service.js +18 -2
- package/dist/warning-details.d.ts +30 -0
- package/dist/warning-details.js +80 -0
- package/dist/workspace-mapping-service.js +9 -0
- package/docs/README-ja.md +2 -2
- package/docs/examples.md +26 -2
- package/docs/tool-reference.md +22 -17
- package/package.json +1 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { randomBytes } from "node:crypto";
|
|
1
2
|
import { existsSync } from "node:fs";
|
|
2
|
-
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
3
|
+
import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
|
|
3
4
|
import { dirname, join } from "node:path";
|
|
4
5
|
import { createError, ERROR_CODES } from "./errors.js";
|
|
5
6
|
import { log } from "./logger.js";
|
|
@@ -339,7 +340,9 @@ export async function resolveMojangTinyFile(version, config, deps = {}) {
|
|
|
339
340
|
const membersByOwner = normalizeMemberMappings(parsedMappings, classMap, warnings);
|
|
340
341
|
const tinyContent = renderTinyV2(classMap, membersByOwner);
|
|
341
342
|
await mkdir(dirname(cachedTiny), { recursive: true });
|
|
342
|
-
|
|
343
|
+
const tempTiny = `${cachedTiny}.${randomBytes(4).toString("hex")}.tmp`;
|
|
344
|
+
await writeFile(tempTiny, tinyContent, "utf8");
|
|
345
|
+
await rename(tempTiny, cachedTiny);
|
|
343
346
|
log("info", "mojang-tiny.generated", {
|
|
344
347
|
version,
|
|
345
348
|
path: cachedTiny,
|
|
@@ -98,6 +98,9 @@ function decodeMutf8(buffer, start, end) {
|
|
|
98
98
|
throw parseError("Truncated MUTF-8 2-byte sequence.", { offset: i - 1 });
|
|
99
99
|
}
|
|
100
100
|
const b1 = buffer[i++];
|
|
101
|
+
if ((b1 & 0xc0) !== 0x80) {
|
|
102
|
+
throw parseError("Invalid MUTF-8 continuation byte.", { byte: b1, offset: i - 1 });
|
|
103
|
+
}
|
|
101
104
|
codeUnits.push(((b0 & 0x1f) << 6) | (b1 & 0x3f));
|
|
102
105
|
}
|
|
103
106
|
else if ((b0 & 0xf0) === 0xe0) {
|
|
@@ -106,6 +109,9 @@ function decodeMutf8(buffer, start, end) {
|
|
|
106
109
|
}
|
|
107
110
|
const b1 = buffer[i++];
|
|
108
111
|
const b2 = buffer[i++];
|
|
112
|
+
if ((b1 & 0xc0) !== 0x80 || (b2 & 0xc0) !== 0x80) {
|
|
113
|
+
throw parseError("Invalid MUTF-8 continuation byte.", { offset: i - 2 });
|
|
114
|
+
}
|
|
109
115
|
codeUnits.push(((b0 & 0x0f) << 12) | ((b1 & 0x3f) << 6) | (b2 & 0x3f));
|
|
110
116
|
}
|
|
111
117
|
else {
|
|
@@ -219,12 +225,12 @@ class NbtWriter {
|
|
|
219
225
|
}
|
|
220
226
|
writeFloat32(value) {
|
|
221
227
|
const chunk = Buffer.allocUnsafe(4);
|
|
222
|
-
chunk.writeFloatBE(value, 0);
|
|
228
|
+
chunk.writeFloatBE(Number(value), 0);
|
|
223
229
|
this.chunks.push(chunk);
|
|
224
230
|
}
|
|
225
231
|
writeFloat64(value) {
|
|
226
232
|
const chunk = Buffer.allocUnsafe(8);
|
|
227
|
-
chunk.writeDoubleBE(value, 0);
|
|
233
|
+
chunk.writeDoubleBE(Number(value), 0);
|
|
228
234
|
this.chunks.push(chunk);
|
|
229
235
|
}
|
|
230
236
|
writeInt64(value) {
|
|
@@ -232,6 +238,27 @@ class NbtWriter {
|
|
|
232
238
|
chunk.writeBigInt64BE(value, 0);
|
|
233
239
|
this.chunks.push(chunk);
|
|
234
240
|
}
|
|
241
|
+
writeInt8Array(values) {
|
|
242
|
+
const chunk = Buffer.allocUnsafe(values.length);
|
|
243
|
+
for (let i = 0; i < values.length; i += 1) {
|
|
244
|
+
chunk.writeInt8(values[i], i);
|
|
245
|
+
}
|
|
246
|
+
this.chunks.push(chunk);
|
|
247
|
+
}
|
|
248
|
+
writeInt32Array(values) {
|
|
249
|
+
const chunk = Buffer.allocUnsafe(values.length * 4);
|
|
250
|
+
for (let i = 0; i < values.length; i += 1) {
|
|
251
|
+
chunk.writeInt32BE(values[i], i * 4);
|
|
252
|
+
}
|
|
253
|
+
this.chunks.push(chunk);
|
|
254
|
+
}
|
|
255
|
+
writeInt64Array(values) {
|
|
256
|
+
const chunk = Buffer.allocUnsafe(values.length * 8);
|
|
257
|
+
for (let i = 0; i < values.length; i += 1) {
|
|
258
|
+
chunk.writeBigInt64BE(values[i], i * 8);
|
|
259
|
+
}
|
|
260
|
+
this.chunks.push(chunk);
|
|
261
|
+
}
|
|
235
262
|
writeString(value) {
|
|
236
263
|
const encoded = encodeMutf8(value);
|
|
237
264
|
if (encoded.length > 0xffff) {
|
|
@@ -255,10 +282,14 @@ function readPayload(reader, tagId, pointer) {
|
|
|
255
282
|
return { type: "int", value: reader.readInt32() };
|
|
256
283
|
case "long":
|
|
257
284
|
return { type: "long", value: reader.readInt64().toString() };
|
|
258
|
-
case "float":
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
285
|
+
case "float": {
|
|
286
|
+
const value = reader.readFloat32();
|
|
287
|
+
return { type: "float", value: Number.isFinite(value) ? value : Number.isNaN(value) ? "NaN" : value > 0 ? "Infinity" : "-Infinity" };
|
|
288
|
+
}
|
|
289
|
+
case "double": {
|
|
290
|
+
const value = reader.readFloat64();
|
|
291
|
+
return { type: "double", value: Number.isFinite(value) ? value : Number.isNaN(value) ? "NaN" : value > 0 ? "Infinity" : "-Infinity" };
|
|
292
|
+
}
|
|
262
293
|
case "byteArray": {
|
|
263
294
|
const length = reader.readInt32();
|
|
264
295
|
if (length < 0) {
|
|
@@ -309,7 +340,12 @@ function readPayload(reader, tagId, pointer) {
|
|
|
309
340
|
break;
|
|
310
341
|
}
|
|
311
342
|
const childName = reader.readString();
|
|
312
|
-
value
|
|
343
|
+
Object.defineProperty(value, childName, {
|
|
344
|
+
value: readPayload(reader, childType, `${pointer}/value/${childName}`),
|
|
345
|
+
enumerable: true,
|
|
346
|
+
writable: true,
|
|
347
|
+
configurable: true
|
|
348
|
+
});
|
|
313
349
|
}
|
|
314
350
|
return { type: "compound", value };
|
|
315
351
|
}
|
|
@@ -374,9 +410,7 @@ function writePayload(writer, node, pointer) {
|
|
|
374
410
|
return;
|
|
375
411
|
case "byteArray":
|
|
376
412
|
writer.writeInt32(node.value.length);
|
|
377
|
-
|
|
378
|
-
writer.writeInt8(value);
|
|
379
|
-
}
|
|
413
|
+
writer.writeInt8Array(node.value);
|
|
380
414
|
return;
|
|
381
415
|
case "string":
|
|
382
416
|
writer.writeString(node.value);
|
|
@@ -398,15 +432,11 @@ function writePayload(writer, node, pointer) {
|
|
|
398
432
|
return;
|
|
399
433
|
case "intArray":
|
|
400
434
|
writer.writeInt32(node.value.length);
|
|
401
|
-
|
|
402
|
-
writer.writeInt32(value);
|
|
403
|
-
}
|
|
435
|
+
writer.writeInt32Array(node.value);
|
|
404
436
|
return;
|
|
405
437
|
case "longArray":
|
|
406
438
|
writer.writeInt32(node.value.length);
|
|
407
|
-
|
|
408
|
-
writer.writeInt64(BigInt(value));
|
|
409
|
-
}
|
|
439
|
+
writer.writeInt64Array(node.value.map((value) => BigInt(value)));
|
|
410
440
|
return;
|
|
411
441
|
default:
|
|
412
442
|
throw encodeError("Unsupported typed NBT node for encoding.", {
|
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/observability.d.ts
CHANGED
|
@@ -40,6 +40,8 @@ export interface RuntimeMetricSnapshot {
|
|
|
40
40
|
search_rows_scanned: number;
|
|
41
41
|
search_rows_returned: number;
|
|
42
42
|
search_indexed_zero_shortcircuit_count: number;
|
|
43
|
+
search_like_prefilter_count: number;
|
|
44
|
+
search_scan_truncated_count: number;
|
|
43
45
|
reindex_count: number;
|
|
44
46
|
reindex_skip_count: number;
|
|
45
47
|
cache_evictions: number;
|
|
@@ -74,6 +76,8 @@ export declare class RuntimeMetrics {
|
|
|
74
76
|
private searchRowsScanned;
|
|
75
77
|
private searchRowsReturned;
|
|
76
78
|
private searchIndexedZeroShortcircuitCount;
|
|
79
|
+
private searchLikePrefilterCount;
|
|
80
|
+
private searchScanTruncatedCount;
|
|
77
81
|
private reindexCount;
|
|
78
82
|
private reindexSkipCount;
|
|
79
83
|
private cacheEvictions;
|
|
@@ -102,6 +106,8 @@ export declare class RuntimeMetrics {
|
|
|
102
106
|
recordSearchRowsScanned(count: number): void;
|
|
103
107
|
recordSearchRowsReturned(count: number): void;
|
|
104
108
|
recordSearchIndexedZeroShortcircuit(): void;
|
|
109
|
+
recordSearchLikePrefilter(): void;
|
|
110
|
+
recordSearchScanTruncated(): void;
|
|
105
111
|
recordReindex(): void;
|
|
106
112
|
recordReindexSkip(): void;
|
|
107
113
|
recordCacheEviction(count?: number): void;
|
package/dist/observability.js
CHANGED
|
@@ -26,6 +26,8 @@ export class RuntimeMetrics {
|
|
|
26
26
|
searchRowsScanned = 0;
|
|
27
27
|
searchRowsReturned = 0;
|
|
28
28
|
searchIndexedZeroShortcircuitCount = 0;
|
|
29
|
+
searchLikePrefilterCount = 0;
|
|
30
|
+
searchScanTruncatedCount = 0;
|
|
29
31
|
reindexCount = 0;
|
|
30
32
|
reindexSkipCount = 0;
|
|
31
33
|
cacheEvictions = 0;
|
|
@@ -128,6 +130,12 @@ export class RuntimeMetrics {
|
|
|
128
130
|
recordSearchIndexedZeroShortcircuit() {
|
|
129
131
|
this.searchIndexedZeroShortcircuitCount += 1;
|
|
130
132
|
}
|
|
133
|
+
recordSearchLikePrefilter() {
|
|
134
|
+
this.searchLikePrefilterCount += 1;
|
|
135
|
+
}
|
|
136
|
+
recordSearchScanTruncated() {
|
|
137
|
+
this.searchScanTruncatedCount += 1;
|
|
138
|
+
}
|
|
131
139
|
recordReindex() {
|
|
132
140
|
this.reindexCount += 1;
|
|
133
141
|
}
|
|
@@ -192,6 +200,8 @@ export class RuntimeMetrics {
|
|
|
192
200
|
search_rows_scanned: this.searchRowsScanned,
|
|
193
201
|
search_rows_returned: this.searchRowsReturned,
|
|
194
202
|
search_indexed_zero_shortcircuit_count: this.searchIndexedZeroShortcircuitCount,
|
|
203
|
+
search_like_prefilter_count: this.searchLikePrefilterCount,
|
|
204
|
+
search_scan_truncated_count: this.searchScanTruncatedCount,
|
|
195
205
|
reindex_count: this.reindexCount,
|
|
196
206
|
reindex_skip_count: this.reindexSkipCount,
|
|
197
207
|
cache_evictions: this.cacheEvictions,
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export type OffsetCursor = {
|
|
2
|
+
offset: number;
|
|
3
|
+
contextKey: string;
|
|
4
|
+
};
|
|
5
|
+
/** Build a stable context key from the query parameters a cursor belongs to. */
|
|
6
|
+
export declare function buildPageContextKey(parts: Array<string | number | boolean | undefined>): string;
|
|
7
|
+
export declare function encodeOffsetCursor(offset: number, contextKey: string): string;
|
|
8
|
+
export declare function decodeOffsetCursor(cursor: string | undefined): OffsetCursor | undefined;
|
|
9
|
+
/**
|
|
10
|
+
* Resolve the starting offset from an incoming cursor. Returns offset 0 when the
|
|
11
|
+
* cursor is absent, malformed, or belongs to a different query context, and
|
|
12
|
+
* reports whether a provided cursor was ignored.
|
|
13
|
+
*/
|
|
14
|
+
export declare function resolveCursorOffset(cursor: string | undefined, contextKey: string): {
|
|
15
|
+
offset: number;
|
|
16
|
+
cursorIgnored: boolean;
|
|
17
|
+
};
|
|
Binary file
|
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
|
@@ -82,6 +82,25 @@ function parseRegistrySnapshot(raw, version, registryFile) {
|
|
|
82
82
|
details: { version, registryFile }
|
|
83
83
|
});
|
|
84
84
|
}
|
|
85
|
+
// Every registry value must carry an `entries` object. Without this guard a
|
|
86
|
+
// malformed value slips through and later throws a raw TypeError on `.entries`
|
|
87
|
+
// (registry-service.ts:268/305, version-diff-service.ts), bypassing the
|
|
88
|
+
// corrupt-snapshot regeneration path that keys off REGISTRY_GENERATION_FAILED.
|
|
89
|
+
for (const [registryName, value] of Object.entries(parsed)) {
|
|
90
|
+
const entries = value?.entries;
|
|
91
|
+
if (typeof value !== "object" ||
|
|
92
|
+
value === null ||
|
|
93
|
+
Array.isArray(value) ||
|
|
94
|
+
typeof entries !== "object" ||
|
|
95
|
+
entries === null ||
|
|
96
|
+
Array.isArray(entries)) {
|
|
97
|
+
throw createError({
|
|
98
|
+
code: ERROR_CODES.REGISTRY_GENERATION_FAILED,
|
|
99
|
+
message: `registries.json for version "${version}" has a malformed "${registryName}" registry (missing entries).`,
|
|
100
|
+
details: { version, registryFile, registryName }
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
}
|
|
85
104
|
return parsed;
|
|
86
105
|
}
|
|
87
106
|
function runDataGen(serverJarPath, outputDir, version) {
|
|
@@ -107,7 +126,7 @@ function runDataGen(serverJarPath, outputDir, version) {
|
|
|
107
126
|
];
|
|
108
127
|
log("info", "registry.datagen.start", { version, isLegacy, serverJarPath, outputDir });
|
|
109
128
|
const proc = spawn("java", args, {
|
|
110
|
-
stdio: ["ignore", "
|
|
129
|
+
stdio: ["ignore", "ignore", "pipe"],
|
|
111
130
|
cwd: outputDir
|
|
112
131
|
});
|
|
113
132
|
let stderr = "";
|
|
@@ -175,6 +194,7 @@ export class RegistryService {
|
|
|
175
194
|
config;
|
|
176
195
|
versionService;
|
|
177
196
|
registryCache = new Map();
|
|
197
|
+
loadLocks = new Map();
|
|
178
198
|
constructor(config, versionService) {
|
|
179
199
|
this.config = config;
|
|
180
200
|
this.versionService = versionService;
|
|
@@ -251,9 +271,28 @@ export class RegistryService {
|
|
|
251
271
|
};
|
|
252
272
|
}
|
|
253
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) {
|
|
254
288
|
const cached = this.registryCache.get(version);
|
|
255
|
-
if (cached)
|
|
289
|
+
if (cached) {
|
|
290
|
+
// LRU touch: move to most-recently-used so the bound evicts the true
|
|
291
|
+
// coldest entry (Map preserves insertion order).
|
|
292
|
+
this.registryCache.delete(version);
|
|
293
|
+
this.registryCache.set(version, cached);
|
|
256
294
|
return cached;
|
|
295
|
+
}
|
|
257
296
|
const registryDir = join(this.config.cacheDir, "registries", version);
|
|
258
297
|
const cachedRegistries = this.loadExistingRegistries(registryDir, version, warnings);
|
|
259
298
|
if (cachedRegistries) {
|
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) {
|