@creeperhost/modlens-mcp 1.6.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/README.md +938 -0
- package/dist/access-flags.d.ts +58 -0
- package/dist/access-flags.d.ts.map +1 -0
- package/dist/access-flags.js +79 -0
- package/dist/access-flags.js.map +1 -0
- package/dist/access-flags.test.d.ts +2 -0
- package/dist/access-flags.test.d.ts.map +1 -0
- package/dist/access-flags.test.js +189 -0
- package/dist/access-flags.test.js.map +1 -0
- package/dist/batch-ingest.d.ts +2 -0
- package/dist/batch-ingest.d.ts.map +1 -0
- package/dist/batch-ingest.js +54 -0
- package/dist/batch-ingest.js.map +1 -0
- package/dist/batch-resolve-mixins.d.ts +2 -0
- package/dist/batch-resolve-mixins.d.ts.map +1 -0
- package/dist/batch-resolve-mixins.js +36 -0
- package/dist/batch-resolve-mixins.js.map +1 -0
- package/dist/cache.d.ts +16 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +36 -0
- package/dist/cache.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +1318 -0
- package/dist/cli.js.map +1 -0
- package/dist/curseforge.d.ts +53 -0
- package/dist/curseforge.d.ts.map +1 -0
- package/dist/curseforge.js +104 -0
- package/dist/curseforge.js.map +1 -0
- package/dist/db-backend.d.ts +17 -0
- package/dist/db-backend.d.ts.map +1 -0
- package/dist/db-backend.js +40 -0
- package/dist/db-backend.js.map +1 -0
- package/dist/db.d.ts +11 -0
- package/dist/db.d.ts.map +1 -0
- package/dist/db.js +55 -0
- package/dist/db.js.map +1 -0
- package/dist/embed-queue.d.ts +12 -0
- package/dist/embed-queue.d.ts.map +1 -0
- package/dist/embed-queue.js +103 -0
- package/dist/embed-queue.js.map +1 -0
- package/dist/embeddings.d.ts +21 -0
- package/dist/embeddings.d.ts.map +1 -0
- package/dist/embeddings.js +55 -0
- package/dist/embeddings.js.map +1 -0
- package/dist/env-path.d.ts +19 -0
- package/dist/env-path.d.ts.map +1 -0
- package/dist/env-path.js +40 -0
- package/dist/env-path.js.map +1 -0
- package/dist/fetch-utils.d.ts +24 -0
- package/dist/fetch-utils.d.ts.map +1 -0
- package/dist/fetch-utils.js +73 -0
- package/dist/fetch-utils.js.map +1 -0
- package/dist/fetch-utils.test.d.ts +2 -0
- package/dist/fetch-utils.test.d.ts.map +1 -0
- package/dist/fetch-utils.test.js +133 -0
- package/dist/fetch-utils.test.js.map +1 -0
- package/dist/generated/sqlite/browser.d.ts +55 -0
- package/dist/generated/sqlite/browser.d.ts.map +1 -0
- package/dist/generated/sqlite/browser.js +18 -0
- package/dist/generated/sqlite/browser.js.map +1 -0
- package/dist/generated/sqlite/client.d.ts +72 -0
- package/dist/generated/sqlite/client.d.ts.map +1 -0
- package/dist/generated/sqlite/client.js +33 -0
- package/dist/generated/sqlite/client.js.map +1 -0
- package/dist/generated/sqlite/commonInputTypes.d.ts +387 -0
- package/dist/generated/sqlite/commonInputTypes.d.ts.map +1 -0
- package/dist/generated/sqlite/commonInputTypes.js +11 -0
- package/dist/generated/sqlite/commonInputTypes.js.map +1 -0
- package/dist/generated/sqlite/enums.d.ts +2 -0
- package/dist/generated/sqlite/enums.d.ts.map +1 -0
- package/dist/generated/sqlite/enums.js +11 -0
- package/dist/generated/sqlite/enums.js.map +1 -0
- package/dist/generated/sqlite/internal/class.d.ts +226 -0
- package/dist/generated/sqlite/internal/class.d.ts.map +1 -0
- package/dist/generated/sqlite/internal/class.js +84 -0
- package/dist/generated/sqlite/internal/class.js.map +1 -0
- package/dist/generated/sqlite/internal/prismaNamespace.d.ts +1344 -0
- package/dist/generated/sqlite/internal/prismaNamespace.d.ts.map +1 -0
- package/dist/generated/sqlite/internal/prismaNamespace.js +220 -0
- package/dist/generated/sqlite/internal/prismaNamespace.js.map +1 -0
- package/dist/generated/sqlite/internal/prismaNamespaceBrowser.d.ts +204 -0
- package/dist/generated/sqlite/internal/prismaNamespaceBrowser.d.ts.map +1 -0
- package/dist/generated/sqlite/internal/prismaNamespaceBrowser.js +191 -0
- package/dist/generated/sqlite/internal/prismaNamespaceBrowser.js.map +1 -0
- package/dist/generated/sqlite/models/DocEntry.d.ts +1223 -0
- package/dist/generated/sqlite/models/DocEntry.d.ts.map +1 -0
- package/dist/generated/sqlite/models/DocEntry.js +2 -0
- package/dist/generated/sqlite/models/DocEntry.js.map +1 -0
- package/dist/generated/sqlite/models/McSourceFile.d.ts +1289 -0
- package/dist/generated/sqlite/models/McSourceFile.d.ts.map +1 -0
- package/dist/generated/sqlite/models/McSourceFile.js +2 -0
- package/dist/generated/sqlite/models/McSourceFile.js.map +1 -0
- package/dist/generated/sqlite/models/McVersion.d.ts +1315 -0
- package/dist/generated/sqlite/models/McVersion.d.ts.map +1 -0
- package/dist/generated/sqlite/models/McVersion.js +2 -0
- package/dist/generated/sqlite/models/McVersion.js.map +1 -0
- package/dist/generated/sqlite/models/McVersionDiff.d.ts +1061 -0
- package/dist/generated/sqlite/models/McVersionDiff.d.ts.map +1 -0
- package/dist/generated/sqlite/models/McVersionDiff.js +2 -0
- package/dist/generated/sqlite/models/McVersionDiff.js.map +1 -0
- package/dist/generated/sqlite/models/Mod.d.ts +2396 -0
- package/dist/generated/sqlite/models/Mod.d.ts.map +1 -0
- package/dist/generated/sqlite/models/Mod.js +2 -0
- package/dist/generated/sqlite/models/Mod.js.map +1 -0
- package/dist/generated/sqlite/models/ModClass.d.ts +1254 -0
- package/dist/generated/sqlite/models/ModClass.d.ts.map +1 -0
- package/dist/generated/sqlite/models/ModClass.js +2 -0
- package/dist/generated/sqlite/models/ModClass.js.map +1 -0
- package/dist/generated/sqlite/models/ModSourceFile.d.ts +1283 -0
- package/dist/generated/sqlite/models/ModSourceFile.d.ts.map +1 -0
- package/dist/generated/sqlite/models/ModSourceFile.js +2 -0
- package/dist/generated/sqlite/models/ModSourceFile.js.map +1 -0
- package/dist/generated/sqlite/models/ModTag.d.ts +1278 -0
- package/dist/generated/sqlite/models/ModTag.d.ts.map +1 -0
- package/dist/generated/sqlite/models/ModTag.js +2 -0
- package/dist/generated/sqlite/models/ModTag.js.map +1 -0
- package/dist/generated/sqlite/models/ModVersionDiff.d.ts +1073 -0
- package/dist/generated/sqlite/models/ModVersionDiff.d.ts.map +1 -0
- package/dist/generated/sqlite/models/ModVersionDiff.js +2 -0
- package/dist/generated/sqlite/models/ModVersionDiff.js.map +1 -0
- package/dist/generated/sqlite/models/Primer.d.ts +1319 -0
- package/dist/generated/sqlite/models/Primer.d.ts.map +1 -0
- package/dist/generated/sqlite/models/Primer.js +2 -0
- package/dist/generated/sqlite/models/Primer.js.map +1 -0
- package/dist/generated/sqlite/models.d.ts +12 -0
- package/dist/generated/sqlite/models.d.ts.map +1 -0
- package/dist/generated/sqlite/models.js +2 -0
- package/dist/generated/sqlite/models.js.map +1 -0
- package/dist/jar.d.ts +7 -0
- package/dist/jar.d.ts.map +1 -0
- package/dist/jar.js +52 -0
- package/dist/jar.js.map +1 -0
- package/dist/java-tools.d.ts +46 -0
- package/dist/java-tools.d.ts.map +1 -0
- package/dist/java-tools.js +263 -0
- package/dist/java-tools.js.map +1 -0
- package/dist/launcher.d.ts +3 -0
- package/dist/launcher.d.ts.map +1 -0
- package/dist/launcher.js +105 -0
- package/dist/launcher.js.map +1 -0
- package/dist/mappings.d.ts +95 -0
- package/dist/mappings.d.ts.map +1 -0
- package/dist/mappings.js +1094 -0
- package/dist/mappings.js.map +1 -0
- package/dist/mappings.test.d.ts +2 -0
- package/dist/mappings.test.d.ts.map +1 -0
- package/dist/mappings.test.js +161 -0
- package/dist/mappings.test.js.map +1 -0
- package/dist/minecraft.d.ts +21 -0
- package/dist/minecraft.d.ts.map +1 -0
- package/dist/minecraft.js +70 -0
- package/dist/minecraft.js.map +1 -0
- package/dist/modpacks-ch.d.ts +224 -0
- package/dist/modpacks-ch.d.ts.map +1 -0
- package/dist/modpacks-ch.js +146 -0
- package/dist/modpacks-ch.js.map +1 -0
- package/dist/modrinth.d.ts +67 -0
- package/dist/modrinth.d.ts.map +1 -0
- package/dist/modrinth.js +88 -0
- package/dist/modrinth.js.map +1 -0
- package/dist/platform-adapter.d.ts +23 -0
- package/dist/platform-adapter.d.ts.map +1 -0
- package/dist/platform-adapter.js +2 -0
- package/dist/platform-adapter.js.map +1 -0
- package/dist/platform.d.ts +47 -0
- package/dist/platform.d.ts.map +1 -0
- package/dist/platform.js +175 -0
- package/dist/platform.js.map +1 -0
- package/dist/processor.d.ts +51 -0
- package/dist/processor.d.ts.map +1 -0
- package/dist/processor.js +655 -0
- package/dist/processor.js.map +1 -0
- package/dist/processor.test.d.ts +2 -0
- package/dist/processor.test.d.ts.map +1 -0
- package/dist/processor.test.js +629 -0
- package/dist/processor.test.js.map +1 -0
- package/dist/repositories/embeddings-sqlite.d.ts +38 -0
- package/dist/repositories/embeddings-sqlite.d.ts.map +1 -0
- package/dist/repositories/embeddings-sqlite.js +208 -0
- package/dist/repositories/embeddings-sqlite.js.map +1 -0
- package/dist/repositories/embeddings.d.ts +40 -0
- package/dist/repositories/embeddings.d.ts.map +1 -0
- package/dist/repositories/embeddings.js +136 -0
- package/dist/repositories/embeddings.js.map +1 -0
- package/dist/repositories/index.d.ts +2 -0
- package/dist/repositories/index.d.ts.map +1 -0
- package/dist/repositories/index.js +15 -0
- package/dist/repositories/index.js.map +1 -0
- package/dist/repositories/mcVersion.d.ts +16 -0
- package/dist/repositories/mcVersion.d.ts.map +1 -0
- package/dist/repositories/mcVersion.js +52 -0
- package/dist/repositories/mcVersion.js.map +1 -0
- package/dist/repositories/mod.d.ts +241 -0
- package/dist/repositories/mod.d.ts.map +1 -0
- package/dist/repositories/mod.js +342 -0
- package/dist/repositories/mod.js.map +1 -0
- package/dist/repositories/packs.d.ts +44 -0
- package/dist/repositories/packs.d.ts.map +1 -0
- package/dist/repositories/packs.js +138 -0
- package/dist/repositories/packs.js.map +1 -0
- package/dist/search-adapter.d.ts +34 -0
- package/dist/search-adapter.d.ts.map +1 -0
- package/dist/search-adapter.js +195 -0
- package/dist/search-adapter.js.map +1 -0
- package/dist/search.d.ts +6 -0
- package/dist/search.d.ts.map +1 -0
- package/dist/search.js +54 -0
- package/dist/search.js.map +1 -0
- package/dist/search.test.d.ts +2 -0
- package/dist/search.test.d.ts.map +1 -0
- package/dist/search.test.js +71 -0
- package/dist/search.test.js.map +1 -0
- package/dist/security.d.ts +98 -0
- package/dist/security.d.ts.map +1 -0
- package/dist/security.js +446 -0
- package/dist/security.js.map +1 -0
- package/dist/security.test.d.ts +2 -0
- package/dist/security.test.d.ts.map +1 -0
- package/dist/security.test.js +687 -0
- package/dist/security.test.js.map +1 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +1465 -0
- package/dist/server.js.map +1 -0
- package/dist/setup.d.ts +2 -0
- package/dist/setup.d.ts.map +1 -0
- package/dist/setup.js +1113 -0
- package/dist/setup.js.map +1 -0
- package/dist/tools/bytecode.d.ts +129 -0
- package/dist/tools/bytecode.d.ts.map +1 -0
- package/dist/tools/bytecode.js +679 -0
- package/dist/tools/bytecode.js.map +1 -0
- package/dist/tools/catalog.d.ts +54 -0
- package/dist/tools/catalog.d.ts.map +1 -0
- package/dist/tools/catalog.js +249 -0
- package/dist/tools/catalog.js.map +1 -0
- package/dist/tools/compat-check.d.ts +23 -0
- package/dist/tools/compat-check.d.ts.map +1 -0
- package/dist/tools/compat-check.js +250 -0
- package/dist/tools/compat-check.js.map +1 -0
- package/dist/tools/compat-check.test.d.ts +2 -0
- package/dist/tools/compat-check.test.d.ts.map +1 -0
- package/dist/tools/compat-check.test.js +179 -0
- package/dist/tools/compat-check.test.js.map +1 -0
- package/dist/tools/diagnostics.d.ts +10 -0
- package/dist/tools/diagnostics.d.ts.map +1 -0
- package/dist/tools/diagnostics.js +133 -0
- package/dist/tools/diagnostics.js.map +1 -0
- package/dist/tools/diagnostics.test.d.ts +2 -0
- package/dist/tools/diagnostics.test.d.ts.map +1 -0
- package/dist/tools/diagnostics.test.js +168 -0
- package/dist/tools/diagnostics.test.js.map +1 -0
- package/dist/tools/docs.d.ts +19 -0
- package/dist/tools/docs.d.ts.map +1 -0
- package/dist/tools/docs.js +230 -0
- package/dist/tools/docs.js.map +1 -0
- package/dist/tools/embed-registry.d.ts +125 -0
- package/dist/tools/embed-registry.d.ts.map +1 -0
- package/dist/tools/embed-registry.js +560 -0
- package/dist/tools/embed-registry.js.map +1 -0
- package/dist/tools/gradle.d.ts +16 -0
- package/dist/tools/gradle.d.ts.map +1 -0
- package/dist/tools/gradle.js +182 -0
- package/dist/tools/gradle.js.map +1 -0
- package/dist/tools/graphify.d.ts +144 -0
- package/dist/tools/graphify.d.ts.map +1 -0
- package/dist/tools/graphify.js +798 -0
- package/dist/tools/graphify.js.map +1 -0
- package/dist/tools/graphify.test.d.ts +2 -0
- package/dist/tools/graphify.test.d.ts.map +1 -0
- package/dist/tools/graphify.test.js +39 -0
- package/dist/tools/graphify.test.js.map +1 -0
- package/dist/tools/ingest.d.ts +85 -0
- package/dist/tools/ingest.d.ts.map +1 -0
- package/dist/tools/ingest.js +477 -0
- package/dist/tools/ingest.js.map +1 -0
- package/dist/tools/ingest.test.d.ts +2 -0
- package/dist/tools/ingest.test.d.ts.map +1 -0
- package/dist/tools/ingest.test.js +340 -0
- package/dist/tools/ingest.test.js.map +1 -0
- package/dist/tools/kubejs.d.ts +37 -0
- package/dist/tools/kubejs.d.ts.map +1 -0
- package/dist/tools/kubejs.js +245 -0
- package/dist/tools/kubejs.js.map +1 -0
- package/dist/tools/mappings.d.ts +17 -0
- package/dist/tools/mappings.d.ts.map +1 -0
- package/dist/tools/mappings.js +85 -0
- package/dist/tools/mappings.js.map +1 -0
- package/dist/tools/mc-fts.d.ts +101 -0
- package/dist/tools/mc-fts.d.ts.map +1 -0
- package/dist/tools/mc-fts.js +279 -0
- package/dist/tools/mc-fts.js.map +1 -0
- package/dist/tools/mcmeta.d.ts +41 -0
- package/dist/tools/mcmeta.d.ts.map +1 -0
- package/dist/tools/mcmeta.js +365 -0
- package/dist/tools/mcmeta.js.map +1 -0
- package/dist/tools/mixin-scan.d.ts +113 -0
- package/dist/tools/mixin-scan.d.ts.map +1 -0
- package/dist/tools/mixin-scan.js +433 -0
- package/dist/tools/mixin-scan.js.map +1 -0
- package/dist/tools/mod-data.d.ts +83 -0
- package/dist/tools/mod-data.d.ts.map +1 -0
- package/dist/tools/mod-data.js +630 -0
- package/dist/tools/mod-data.js.map +1 -0
- package/dist/tools/mod-tags.d.ts +59 -0
- package/dist/tools/mod-tags.d.ts.map +1 -0
- package/dist/tools/mod-tags.js +279 -0
- package/dist/tools/mod-tags.js.map +1 -0
- package/dist/tools/mod-tags.test.d.ts +2 -0
- package/dist/tools/mod-tags.test.d.ts.map +1 -0
- package/dist/tools/mod-tags.test.js +75 -0
- package/dist/tools/mod-tags.test.js.map +1 -0
- package/dist/tools/modpacks-ch.d.ts +257 -0
- package/dist/tools/modpacks-ch.d.ts.map +1 -0
- package/dist/tools/modpacks-ch.js +516 -0
- package/dist/tools/modpacks-ch.js.map +1 -0
- package/dist/tools/packtools.d.ts +113 -0
- package/dist/tools/packtools.d.ts.map +1 -0
- package/dist/tools/packtools.js +809 -0
- package/dist/tools/packtools.js.map +1 -0
- package/dist/tools/packtools.test.d.ts +2 -0
- package/dist/tools/packtools.test.d.ts.map +1 -0
- package/dist/tools/packtools.test.js +99 -0
- package/dist/tools/packtools.test.js.map +1 -0
- package/dist/tools/platform.d.ts +68 -0
- package/dist/tools/platform.d.ts.map +1 -0
- package/dist/tools/platform.js +452 -0
- package/dist/tools/platform.js.map +1 -0
- package/dist/tools/primers.d.ts +36 -0
- package/dist/tools/primers.d.ts.map +1 -0
- package/dist/tools/primers.js +451 -0
- package/dist/tools/primers.js.map +1 -0
- package/dist/tools/reports.d.ts +19 -0
- package/dist/tools/reports.d.ts.map +1 -0
- package/dist/tools/reports.js +573 -0
- package/dist/tools/reports.js.map +1 -0
- package/dist/tools/source.d.ts +10 -0
- package/dist/tools/source.d.ts.map +1 -0
- package/dist/tools/source.js +101 -0
- package/dist/tools/source.js.map +1 -0
- package/dist/tools/vanilla-data.d.ts +127 -0
- package/dist/tools/vanilla-data.d.ts.map +1 -0
- package/dist/tools/vanilla-data.js +824 -0
- package/dist/tools/vanilla-data.js.map +1 -0
- package/dist/tools/vanilla.d.ts +171 -0
- package/dist/tools/vanilla.d.ts.map +1 -0
- package/dist/tools/vanilla.js +612 -0
- package/dist/tools/vanilla.js.map +1 -0
- package/dist/tools/version-diff.d.ts +113 -0
- package/dist/tools/version-diff.d.ts.map +1 -0
- package/dist/tools/version-diff.js +519 -0
- package/dist/tools/version-diff.js.map +1 -0
- package/dist/tools/version-diff.test.d.ts +2 -0
- package/dist/tools/version-diff.test.d.ts.map +1 -0
- package/dist/tools/version-diff.test.js +102 -0
- package/dist/tools/version-diff.test.js.map +1 -0
- package/dist/validate.d.ts +19 -0
- package/dist/validate.d.ts.map +1 -0
- package/dist/validate.js +54 -0
- package/dist/validate.js.map +1 -0
- package/dist/validate.test.d.ts +2 -0
- package/dist/validate.test.d.ts.map +1 -0
- package/dist/validate.test.js +73 -0
- package/dist/validate.test.js.map +1 -0
- package/package.json +62 -0
- package/prisma/backends/schema.sqlite.prisma +215 -0
- package/prisma/backends/template.db +0 -0
- package/prisma/schema.prisma +303 -0
- package/scripts/backup.mjs +151 -0
- package/scripts/build-template-db.mjs +38 -0
- package/scripts/check-atm10.mjs +27 -0
- package/scripts/check-sync.mjs +13 -0
- package/scripts/create-fts-index.mjs +50 -0
- package/scripts/enable-pgvector.mjs +90 -0
- package/scripts/enable-sqlite-vec.mjs +158 -0
- package/scripts/migrate-backend.mjs +188 -0
- package/scripts/migrate-embed-provenance.mjs +80 -0
- package/scripts/migrate-metadata-source.mjs +62 -0
- package/scripts/migrate-mod-source-files.mjs +67 -0
- package/scripts/post-update.mjs +77 -0
- package/scripts/verify-atm10.mjs +63 -0
|
@@ -0,0 +1,824 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP tools for browsing vanilla Minecraft data/asset files via misode/mcmeta.
|
|
3
|
+
*
|
|
4
|
+
* Covers: tags, recipes, loot tables, lang, blockstates, models, biomes,
|
|
5
|
+
* damage types, enchantments — all cached to ~/.modlens-cache/mcmeta/.
|
|
6
|
+
*
|
|
7
|
+
* Data paths follow Minecraft's pack structure:
|
|
8
|
+
* data branch → data/minecraft/<type>/<id>.json
|
|
9
|
+
* assets-json → assets/minecraft/<type>/<id>.json
|
|
10
|
+
*/
|
|
11
|
+
import { readFile, writeFile, readdir } from "fs/promises";
|
|
12
|
+
import { join, resolve } from "path";
|
|
13
|
+
import { CACHE_ROOT, exists, ensureDir } from "../cache.js";
|
|
14
|
+
import { resolveModRef } from "../repositories/mod.js";
|
|
15
|
+
import { searchSource } from "./source.js";
|
|
16
|
+
const RAW_BASE = "https://raw.githubusercontent.com/misode/mcmeta";
|
|
17
|
+
const MCMETA_CACHE = join(CACHE_ROOT, "mcmeta");
|
|
18
|
+
/** Minecraft resource-location component: [a-zA-Z0-9_.+-] */
|
|
19
|
+
const SAFE_COMPONENT = /^[a-zA-Z0-9_.+-]+$/;
|
|
20
|
+
function validateMcComponent(value, label) {
|
|
21
|
+
if (!SAFE_COMPONENT.test(value)) {
|
|
22
|
+
throw new Error(`Invalid ${label}: "${value}" — only alphanumeric, dot, underscore, hyphen, and plus are allowed`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
// ── Internal helpers ─────────────────────────────────────────────────────────────────
|
|
26
|
+
function mcmetaCachePath(version, branch, filePath) {
|
|
27
|
+
const result = resolve(join(MCMETA_CACHE, version, branch, filePath));
|
|
28
|
+
const resolvedCache = resolve(MCMETA_CACHE);
|
|
29
|
+
if (!result.startsWith(resolvedCache + require("path").sep) && result !== resolvedCache) {
|
|
30
|
+
throw new Error(`Path traversal rejected in mcmeta cache: ${filePath}`);
|
|
31
|
+
}
|
|
32
|
+
return result;
|
|
33
|
+
}
|
|
34
|
+
async function fetchMcmetaJson(ref, filePath) {
|
|
35
|
+
const url = `${RAW_BASE}/${ref}/${filePath}`;
|
|
36
|
+
const [version, branch] = ref.includes("-")
|
|
37
|
+
? ref.split(/-(.+)/)
|
|
38
|
+
: ["_latest", ref];
|
|
39
|
+
const cachePath = mcmetaCachePath(version, branch, filePath);
|
|
40
|
+
if (await exists(cachePath)) {
|
|
41
|
+
return JSON.parse((await readFile(cachePath)).toString("utf8"));
|
|
42
|
+
}
|
|
43
|
+
const res = await fetch(url);
|
|
44
|
+
if (!res.ok)
|
|
45
|
+
throw new Error(`mcmeta fetch failed: ${res.status} — ${url}`);
|
|
46
|
+
const text = await res.text();
|
|
47
|
+
await ensureDir(cachePath);
|
|
48
|
+
await writeFile(cachePath, Buffer.from(text));
|
|
49
|
+
return JSON.parse(text);
|
|
50
|
+
}
|
|
51
|
+
function versionRef(version, branch) {
|
|
52
|
+
return version ? `${version}-${branch}` : branch;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* List files/dirs in a mcmeta branch directory via the GitHub Contents API.
|
|
56
|
+
* Result is cached as _dir_index.json inside the matching cache directory.
|
|
57
|
+
*/
|
|
58
|
+
async function listMcmetaDir(version, branch, path) {
|
|
59
|
+
const cacheDir = mcmetaCachePath(version, branch, path);
|
|
60
|
+
const indexPath = join(cacheDir, "_dir_index.json");
|
|
61
|
+
if (await exists(indexPath)) {
|
|
62
|
+
return JSON.parse((await readFile(indexPath)).toString("utf8"));
|
|
63
|
+
}
|
|
64
|
+
// Try local cache dir listing first (may already be populated by prior fetches)
|
|
65
|
+
if (await exists(cacheDir)) {
|
|
66
|
+
try {
|
|
67
|
+
const dirEntries = await readdir(cacheDir, { withFileTypes: true });
|
|
68
|
+
const entries = dirEntries
|
|
69
|
+
.filter(e => e.name !== "_dir_index.json")
|
|
70
|
+
.map(e => ({ name: e.name, type: e.isDirectory() ? "dir" : "file" }));
|
|
71
|
+
if (entries.length > 0) {
|
|
72
|
+
await writeFile(indexPath, JSON.stringify(entries, null, 2));
|
|
73
|
+
return entries;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
catch { /* fall through to API */ }
|
|
77
|
+
}
|
|
78
|
+
const apiUrl = `https://api.github.com/repos/misode/mcmeta/contents/${path}?ref=${version}-${branch}`;
|
|
79
|
+
const res = await fetch(apiUrl, { headers: { Accept: "application/vnd.github.v3+json" } });
|
|
80
|
+
if (!res.ok)
|
|
81
|
+
throw new Error(`GitHub contents API ${res.status} — ${apiUrl}`);
|
|
82
|
+
const items = await res.json();
|
|
83
|
+
const entries = items.map(i => ({ name: i.name, type: i.type === "dir" ? "dir" : "file" }));
|
|
84
|
+
await ensureDir(indexPath);
|
|
85
|
+
await writeFile(indexPath, JSON.stringify(entries, null, 2));
|
|
86
|
+
return entries;
|
|
87
|
+
}
|
|
88
|
+
// ── Tags ──────────────────────────────────────────────────────────────────────
|
|
89
|
+
/**
|
|
90
|
+
* Browse vanilla MC tags.
|
|
91
|
+
* - No registry: list all tag registries (block, item, entity_type, …)
|
|
92
|
+
* - registry only: list all tag IDs in that registry
|
|
93
|
+
* - registry + tagId: return the full tag JSON (values array)
|
|
94
|
+
*/
|
|
95
|
+
export async function getMcTags(version, registry, tagId, namespace = "minecraft") {
|
|
96
|
+
const v = version ?? "26.1.2";
|
|
97
|
+
const branch = "data";
|
|
98
|
+
validateMcComponent(namespace, "namespace");
|
|
99
|
+
if (registry)
|
|
100
|
+
validateMcComponent(registry, "registry");
|
|
101
|
+
if (tagId)
|
|
102
|
+
validateMcComponent(tagId.replace(/\//g, "_"), "tagId");
|
|
103
|
+
if (registry && tagId) {
|
|
104
|
+
const path = `data/${namespace}/tags/${registry}/${tagId}.json`;
|
|
105
|
+
try {
|
|
106
|
+
const data = await fetchMcmetaJson(versionRef(v, branch), path);
|
|
107
|
+
return { version: v, namespace, registry, tagId, ...data };
|
|
108
|
+
}
|
|
109
|
+
catch (err) {
|
|
110
|
+
return { version: v, namespace, registry, tagId, error: String(err) };
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
if (registry) {
|
|
114
|
+
const path = `data/${namespace}/tags/${registry}`;
|
|
115
|
+
try {
|
|
116
|
+
const entries = await listMcmetaDir(v, branch, path);
|
|
117
|
+
const tags = entries
|
|
118
|
+
.filter(e => e.type === "file" && e.name.endsWith(".json"))
|
|
119
|
+
.map(e => e.name.replace(".json", ""));
|
|
120
|
+
return { version: v, namespace, registry, count: tags.length, tags };
|
|
121
|
+
}
|
|
122
|
+
catch (err) {
|
|
123
|
+
return { version: v, namespace, registry, error: String(err) };
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
// List all tag registries
|
|
127
|
+
const path = `data/${namespace}/tags`;
|
|
128
|
+
try {
|
|
129
|
+
const entries = await listMcmetaDir(v, branch, path);
|
|
130
|
+
const registries = entries.filter(e => e.type === "dir").map(e => e.name);
|
|
131
|
+
return { version: v, namespace, registries };
|
|
132
|
+
}
|
|
133
|
+
catch (err) {
|
|
134
|
+
return { version: v, namespace, error: String(err) };
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Reverse tag lookup: find every tag in a registry whose values list contains
|
|
139
|
+
* the given entry (e.g. "minecraft:iron_ore" in the "block" registry).
|
|
140
|
+
*/
|
|
141
|
+
export async function findTagsForEntry(entry, registry, version, namespace = "minecraft") {
|
|
142
|
+
const v = version ?? "26.1.2";
|
|
143
|
+
const normalizedEntry = entry.includes(":") ? entry : `minecraft:${entry}`;
|
|
144
|
+
const listResult = await getMcTags(v, registry, undefined, namespace);
|
|
145
|
+
const tagIds = listResult.tags ?? [];
|
|
146
|
+
const results = await Promise.all(tagIds.map(async (tagId) => {
|
|
147
|
+
try {
|
|
148
|
+
const data = await fetchMcmetaJson(versionRef(v, "data"), `data/${namespace}/tags/${registry}/${tagId}.json`);
|
|
149
|
+
const values = (data.values ?? []);
|
|
150
|
+
const flat = values.map(val => typeof val === "string" ? val : val.id ?? "");
|
|
151
|
+
if (flat.some(e => e === normalizedEntry || e === entry)) {
|
|
152
|
+
return { tag: `#${namespace}:${tagId}`, values };
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
catch { /* skip broken tags */ }
|
|
156
|
+
return null;
|
|
157
|
+
}));
|
|
158
|
+
const matchingTags = results.filter((r) => r !== null);
|
|
159
|
+
return { version: v, namespace, registry, entry: normalizedEntry, found: matchingTags.length, tags: matchingTags };
|
|
160
|
+
}
|
|
161
|
+
// ── Recipes ───────────────────────────────────────────────────────────────────
|
|
162
|
+
/**
|
|
163
|
+
* List vanilla recipes, optionally filtered by recipe type or output item.
|
|
164
|
+
* When filtering by outputItem or type, each recipe file is loaded (results cached).
|
|
165
|
+
*/
|
|
166
|
+
export async function listRecipes(version, type, outputItem) {
|
|
167
|
+
const v = version ?? "26.1.2";
|
|
168
|
+
try {
|
|
169
|
+
const entries = await listMcmetaDir(v, "data", "data/minecraft/recipe");
|
|
170
|
+
const ids = entries
|
|
171
|
+
.filter(e => e.type === "file" && e.name.endsWith(".json"))
|
|
172
|
+
.map(e => e.name.replace(".json", ""));
|
|
173
|
+
if (!type && !outputItem) {
|
|
174
|
+
return { version: v, count: ids.length, recipes: ids };
|
|
175
|
+
}
|
|
176
|
+
const fetched = await Promise.all(ids.map(async (id) => {
|
|
177
|
+
try {
|
|
178
|
+
const data = await fetchMcmetaJson(versionRef(v, "data"), `data/minecraft/recipe/${id}.json`);
|
|
179
|
+
const rType = (data.type ?? "").replace("minecraft:", "");
|
|
180
|
+
if (type && !rType.includes(type))
|
|
181
|
+
return null;
|
|
182
|
+
if (outputItem) {
|
|
183
|
+
const resultStr = JSON.stringify(data.result ?? data.output ?? "");
|
|
184
|
+
if (!resultStr.includes(outputItem.replace("minecraft:", "")))
|
|
185
|
+
return null;
|
|
186
|
+
}
|
|
187
|
+
return { id, recipeType: rType, result: data.result ?? data.output };
|
|
188
|
+
}
|
|
189
|
+
catch {
|
|
190
|
+
return null;
|
|
191
|
+
}
|
|
192
|
+
}));
|
|
193
|
+
const results = fetched.filter((r) => r !== null);
|
|
194
|
+
return { version: v, count: results.length, recipes: results };
|
|
195
|
+
}
|
|
196
|
+
catch (err) {
|
|
197
|
+
return { version: v, error: String(err) };
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Get the full JSON for a specific vanilla recipe by its ID (e.g. "crafting_table").
|
|
202
|
+
*/
|
|
203
|
+
export async function getRecipe(version, recipeId) {
|
|
204
|
+
if (!recipeId)
|
|
205
|
+
return { error: "recipeId is required" };
|
|
206
|
+
const v = version ?? "26.1.2";
|
|
207
|
+
const id = recipeId.replace("minecraft:", "");
|
|
208
|
+
try {
|
|
209
|
+
const data = await fetchMcmetaJson(versionRef(v, "data"), `data/minecraft/recipe/${id}.json`);
|
|
210
|
+
return { version: v, id, data };
|
|
211
|
+
}
|
|
212
|
+
catch (err) {
|
|
213
|
+
return { version: v, id, error: String(err) };
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
// ── Loot Tables ───────────────────────────────────────────────────────────────
|
|
217
|
+
/**
|
|
218
|
+
* List vanilla loot tables.
|
|
219
|
+
* - No category: list top-level categories (blocks, entities, chests, gameplay, …)
|
|
220
|
+
* - category given: list all tables in that category
|
|
221
|
+
*/
|
|
222
|
+
export async function listLootTables(version, category) {
|
|
223
|
+
const v = version ?? "26.1.2";
|
|
224
|
+
const basePath = "data/minecraft/loot_table";
|
|
225
|
+
if (category) {
|
|
226
|
+
try {
|
|
227
|
+
const entries = await listMcmetaDir(v, "data", `${basePath}/${category}`);
|
|
228
|
+
const tables = entries
|
|
229
|
+
.filter(e => e.type === "file" && e.name.endsWith(".json"))
|
|
230
|
+
.map(e => `${category}/${e.name.replace(".json", "")}`);
|
|
231
|
+
return { version: v, category, count: tables.length, lootTables: tables };
|
|
232
|
+
}
|
|
233
|
+
catch (err) {
|
|
234
|
+
return { version: v, category, error: String(err) };
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
try {
|
|
238
|
+
const entries = await listMcmetaDir(v, "data", basePath);
|
|
239
|
+
const dirs = entries.filter(e => e.type === "dir").map(e => e.name);
|
|
240
|
+
const files = entries
|
|
241
|
+
.filter(e => e.type === "file" && e.name.endsWith(".json"))
|
|
242
|
+
.map(e => e.name.replace(".json", ""));
|
|
243
|
+
return { version: v, categories: dirs, rootLootTables: files };
|
|
244
|
+
}
|
|
245
|
+
catch (err) {
|
|
246
|
+
return { version: v, error: String(err) };
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Get the full JSON for a specific vanilla loot table.
|
|
251
|
+
* path examples: "blocks/iron_ore", "chests/dungeon", "entities/creeper"
|
|
252
|
+
*/
|
|
253
|
+
export async function getLootTable(version, path) {
|
|
254
|
+
if (!path)
|
|
255
|
+
return { error: "path is required" };
|
|
256
|
+
const v = version ?? "26.1.2";
|
|
257
|
+
const fullPath = path.startsWith("data/")
|
|
258
|
+
? path
|
|
259
|
+
: `data/minecraft/loot_table/${path}${path.endsWith(".json") ? "" : ".json"}`;
|
|
260
|
+
try {
|
|
261
|
+
const data = await fetchMcmetaJson(versionRef(v, "data"), fullPath);
|
|
262
|
+
return { version: v, path, data };
|
|
263
|
+
}
|
|
264
|
+
catch (err) {
|
|
265
|
+
return { version: v, path, error: String(err) };
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
// ── Lang / Translations ───────────────────────────────────────────────────────
|
|
269
|
+
/**
|
|
270
|
+
* Search vanilla en_us.json translation keys/values.
|
|
271
|
+
* filter: substring matched against both key and value (case-insensitive).
|
|
272
|
+
* limit: max number of results to return (default 100).
|
|
273
|
+
*/
|
|
274
|
+
export async function getLangEntries(version, filter, limit = 100) {
|
|
275
|
+
const v = version ?? "26.1.2";
|
|
276
|
+
try {
|
|
277
|
+
const lang = await fetchMcmetaJson(versionRef(v, "assets-json"), "assets/minecraft/lang/en_us.json");
|
|
278
|
+
if (!filter) {
|
|
279
|
+
const entries = Object.entries(lang);
|
|
280
|
+
return {
|
|
281
|
+
version: v,
|
|
282
|
+
total: entries.length,
|
|
283
|
+
shown: Math.min(entries.length, limit),
|
|
284
|
+
entries: Object.fromEntries(entries.slice(0, limit)),
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
const lower = filter.toLowerCase();
|
|
288
|
+
const matched = Object.entries(lang).filter(([k, val]) => k.toLowerCase().includes(lower) || val.toLowerCase().includes(lower));
|
|
289
|
+
return {
|
|
290
|
+
version: v,
|
|
291
|
+
filter,
|
|
292
|
+
count: matched.length,
|
|
293
|
+
entries: Object.fromEntries(matched.slice(0, limit)),
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
catch (err) {
|
|
297
|
+
return { version: v, error: String(err) };
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
// ── Blockstates & Models ──────────────────────────────────────────────────────
|
|
301
|
+
/**
|
|
302
|
+
* Get the blockstate JSON for a vanilla block (variant → model path mappings).
|
|
303
|
+
* block: e.g. "stone", "oak_door", "minecraft:grass_block"
|
|
304
|
+
*/
|
|
305
|
+
export async function getBlockstate(version, block) {
|
|
306
|
+
if (!block)
|
|
307
|
+
return { error: "block is required" };
|
|
308
|
+
const v = version ?? "26.1.2";
|
|
309
|
+
const id = block.replace("minecraft:", "");
|
|
310
|
+
try {
|
|
311
|
+
const data = await fetchMcmetaJson(versionRef(v, "assets-json"), `assets/minecraft/blockstates/${id}.json`);
|
|
312
|
+
return { version: v, block: id, data };
|
|
313
|
+
}
|
|
314
|
+
catch (err) {
|
|
315
|
+
return { version: v, block: id, error: String(err) };
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Get a vanilla model JSON and follow its parent chain.
|
|
320
|
+
* modelPath: e.g. "block/stone", "item/iron_sword", "block/cube_all"
|
|
321
|
+
* resolveParents: if true (default), recursively fetches parent models and merges texture keys.
|
|
322
|
+
*/
|
|
323
|
+
export async function getMcModel(version, modelPath, resolveParents = true) {
|
|
324
|
+
if (!modelPath)
|
|
325
|
+
return { error: "modelPath is required" };
|
|
326
|
+
const v = version ?? "26.1.2";
|
|
327
|
+
const normalPath = modelPath.endsWith(".json") ? modelPath : `${modelPath}.json`;
|
|
328
|
+
const fullPath = normalPath.startsWith("assets/") ? normalPath : `assets/minecraft/models/${normalPath}`;
|
|
329
|
+
try {
|
|
330
|
+
const root = await fetchMcmetaJson(versionRef(v, "assets-json"), fullPath);
|
|
331
|
+
if (!resolveParents || !root.parent) {
|
|
332
|
+
return { version: v, modelPath, data: root };
|
|
333
|
+
}
|
|
334
|
+
// Walk parent chain, collecting texture overrides
|
|
335
|
+
const chain = [{ path: fullPath, data: root }];
|
|
336
|
+
let current = root;
|
|
337
|
+
const seen = new Set([fullPath]);
|
|
338
|
+
while (current.parent) {
|
|
339
|
+
const parentPath = current.parent.includes(":")
|
|
340
|
+
? `assets/${current.parent.replace(":", "/models/")}.json`
|
|
341
|
+
: `assets/minecraft/models/${current.parent}.json`;
|
|
342
|
+
if (seen.has(parentPath))
|
|
343
|
+
break;
|
|
344
|
+
seen.add(parentPath);
|
|
345
|
+
try {
|
|
346
|
+
const parentData = await fetchMcmetaJson(versionRef(v, "assets-json"), parentPath);
|
|
347
|
+
chain.push({ path: parentPath, data: parentData });
|
|
348
|
+
current = parentData;
|
|
349
|
+
}
|
|
350
|
+
catch {
|
|
351
|
+
break;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
// Merge: child textures win; collect all unique elements
|
|
355
|
+
const mergedTextures = {};
|
|
356
|
+
for (const { data } of [...chain].reverse()) {
|
|
357
|
+
Object.assign(mergedTextures, data.textures ?? {});
|
|
358
|
+
}
|
|
359
|
+
return {
|
|
360
|
+
version: v,
|
|
361
|
+
modelPath,
|
|
362
|
+
data: root,
|
|
363
|
+
parentChain: chain.slice(1).map(c => c.path),
|
|
364
|
+
mergedTextures,
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
catch (err) {
|
|
368
|
+
return { version: v, modelPath, error: String(err) };
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
// ── Biomes ────────────────────────────────────────────────────────────────────
|
|
372
|
+
/**
|
|
373
|
+
* List all vanilla biomes for a MC version.
|
|
374
|
+
*/
|
|
375
|
+
export async function listBiomes(version) {
|
|
376
|
+
const v = version ?? "26.1.2";
|
|
377
|
+
try {
|
|
378
|
+
const entries = await listMcmetaDir(v, "data", "data/minecraft/worldgen/biome");
|
|
379
|
+
const biomes = entries
|
|
380
|
+
.filter(e => e.type === "file" && e.name.endsWith(".json"))
|
|
381
|
+
.map(e => `minecraft:${e.name.replace(".json", "")}`);
|
|
382
|
+
return { version: v, count: biomes.length, biomes };
|
|
383
|
+
}
|
|
384
|
+
catch (err) {
|
|
385
|
+
return { version: v, error: String(err) };
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
/**
|
|
389
|
+
* Get the full worldgen biome JSON for a specific biome.
|
|
390
|
+
* biomeId: e.g. "minecraft:desert", "badlands", "deep_dark"
|
|
391
|
+
*/
|
|
392
|
+
export async function getBiome(version, biomeId) {
|
|
393
|
+
if (!biomeId)
|
|
394
|
+
return { error: "biomeId is required" };
|
|
395
|
+
const v = version ?? "26.1.2";
|
|
396
|
+
const id = biomeId.replace("minecraft:", "");
|
|
397
|
+
try {
|
|
398
|
+
const data = await fetchMcmetaJson(versionRef(v, "data"), `data/minecraft/worldgen/biome/${id}.json`);
|
|
399
|
+
return { version: v, biome: `minecraft:${id}`, data };
|
|
400
|
+
}
|
|
401
|
+
catch (err) {
|
|
402
|
+
return { version: v, biome: id, error: String(err) };
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
// ── Damage Types ──────────────────────────────────────────────────────────────
|
|
406
|
+
/**
|
|
407
|
+
* List all vanilla damage types with their full JSON definitions.
|
|
408
|
+
* Returns the complete map: id → { message_id, scaling, exhaustion, effects?, death_message_type? }
|
|
409
|
+
*/
|
|
410
|
+
export async function listDamageTypes(version) {
|
|
411
|
+
const v = version ?? "26.1.2";
|
|
412
|
+
try {
|
|
413
|
+
const entries = await listMcmetaDir(v, "data", "data/minecraft/damage_type");
|
|
414
|
+
const ids = entries
|
|
415
|
+
.filter(e => e.type === "file" && e.name.endsWith(".json"))
|
|
416
|
+
.map(e => e.name.replace(".json", ""));
|
|
417
|
+
const damageTypes = {};
|
|
418
|
+
await Promise.all(ids.map(async (id) => {
|
|
419
|
+
try {
|
|
420
|
+
damageTypes[`minecraft:${id}`] = await fetchMcmetaJson(versionRef(v, "data"), `data/minecraft/damage_type/${id}.json`);
|
|
421
|
+
}
|
|
422
|
+
catch { /* skip */ }
|
|
423
|
+
}));
|
|
424
|
+
return { version: v, count: ids.length, damageTypes };
|
|
425
|
+
}
|
|
426
|
+
catch (err) {
|
|
427
|
+
return { version: v, error: String(err) };
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
// ── Enchantments ──────────────────────────────────────────────────────────────
|
|
431
|
+
/**
|
|
432
|
+
* List all vanilla enchantments for a MC version.
|
|
433
|
+
*/
|
|
434
|
+
export async function listEnchantments(version) {
|
|
435
|
+
const v = version ?? "26.1.2";
|
|
436
|
+
try {
|
|
437
|
+
const entries = await listMcmetaDir(v, "data", "data/minecraft/enchantment");
|
|
438
|
+
const enchantments = entries
|
|
439
|
+
.filter(e => e.type === "file" && e.name.endsWith(".json"))
|
|
440
|
+
.map(e => `minecraft:${e.name.replace(".json", "")}`);
|
|
441
|
+
return { version: v, count: enchantments.length, enchantments };
|
|
442
|
+
}
|
|
443
|
+
catch (err) {
|
|
444
|
+
return { version: v, error: String(err) };
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
/**
|
|
448
|
+
* Get the full JSON definition of a vanilla enchantment.
|
|
449
|
+
* id: e.g. "minecraft:sharpness", "looting", "protection"
|
|
450
|
+
*/
|
|
451
|
+
export async function getEnchantment(version, id) {
|
|
452
|
+
if (!id)
|
|
453
|
+
return { error: "id is required" };
|
|
454
|
+
const v = version ?? "26.1.2";
|
|
455
|
+
const normalId = id.replace("minecraft:", "");
|
|
456
|
+
try {
|
|
457
|
+
const data = await fetchMcmetaJson(versionRef(v, "data"), `data/minecraft/enchantment/${normalId}.json`);
|
|
458
|
+
return { version: v, enchantment: `minecraft:${normalId}`, data };
|
|
459
|
+
}
|
|
460
|
+
catch (err) {
|
|
461
|
+
return { version: v, enchantment: id, error: String(err) };
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
// ── Advancements ──────────────────────────────────────────────────────────────
|
|
465
|
+
/**
|
|
466
|
+
* List vanilla advancements, optionally filtered by category tab.
|
|
467
|
+
* category: e.g. "story", "nether", "end", "adventure", "husbandry"
|
|
468
|
+
*/
|
|
469
|
+
export async function listAdvancements(version, category) {
|
|
470
|
+
const v = version ?? "26.1.2";
|
|
471
|
+
const basePath = "data/minecraft/advancement";
|
|
472
|
+
if (category) {
|
|
473
|
+
try {
|
|
474
|
+
const entries = await listMcmetaDir(v, "data", `${basePath}/${category}`);
|
|
475
|
+
const advancements = entries
|
|
476
|
+
.filter(e => e.type === "file" && e.name.endsWith(".json"))
|
|
477
|
+
.map(e => `${category}/${e.name.replace(".json", "")}`);
|
|
478
|
+
return { version: v, category, count: advancements.length, advancements };
|
|
479
|
+
}
|
|
480
|
+
catch (err) {
|
|
481
|
+
return { version: v, category, error: String(err) };
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
try {
|
|
485
|
+
const entries = await listMcmetaDir(v, "data", basePath);
|
|
486
|
+
const dirs = entries.filter(e => e.type === "dir").map(e => e.name);
|
|
487
|
+
const files = entries
|
|
488
|
+
.filter(e => e.type === "file" && e.name.endsWith(".json"))
|
|
489
|
+
.map(e => e.name.replace(".json", ""));
|
|
490
|
+
return { version: v, categories: dirs, rootAdvancements: files };
|
|
491
|
+
}
|
|
492
|
+
catch (err) {
|
|
493
|
+
return { version: v, error: String(err) };
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
/**
|
|
497
|
+
* Get the full JSON for a specific vanilla advancement.
|
|
498
|
+
* id: e.g. "story/mine_stone", "nether/root", "adventure/kill_a_mob"
|
|
499
|
+
*/
|
|
500
|
+
export async function getAdvancement(version, id) {
|
|
501
|
+
if (!id)
|
|
502
|
+
return { error: "id is required" };
|
|
503
|
+
const v = version ?? "26.1.2";
|
|
504
|
+
const fullPath = id.startsWith("data/")
|
|
505
|
+
? id
|
|
506
|
+
: `data/minecraft/advancement/${id}${id.endsWith(".json") ? "" : ".json"}`;
|
|
507
|
+
try {
|
|
508
|
+
const data = await fetchMcmetaJson(versionRef(v, "data"), fullPath);
|
|
509
|
+
return { version: v, id, data };
|
|
510
|
+
}
|
|
511
|
+
catch (err) {
|
|
512
|
+
return { version: v, id, error: String(err) };
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
// ── Reverse recipe lookup ─────────────────────────────────────────────────────
|
|
516
|
+
/**
|
|
517
|
+
* Find all recipes whose result/output contains the given item.
|
|
518
|
+
* item: e.g. "iron_ingot", "minecraft:iron_ingot"
|
|
519
|
+
*/
|
|
520
|
+
export async function findRecipesForItem(item, version) {
|
|
521
|
+
const v = version ?? "26.1.2";
|
|
522
|
+
return listRecipes(v, undefined, item);
|
|
523
|
+
}
|
|
524
|
+
/**
|
|
525
|
+
* Resolve the full model JSON inheritance chain for a block or item model.
|
|
526
|
+
* Follows parent references until a model with no parent (or a builtin) is reached.
|
|
527
|
+
* modelPath: e.g. "block/stone", "item/diamond_sword"
|
|
528
|
+
*/
|
|
529
|
+
export async function getModelTree(modelPath, version) {
|
|
530
|
+
const v = version ?? "26.1.2";
|
|
531
|
+
const chain = [];
|
|
532
|
+
const visited = new Set();
|
|
533
|
+
let current = modelPath.replace(/^minecraft:/, "");
|
|
534
|
+
// Normalise — add block/ prefix if bare id with no slash
|
|
535
|
+
if (!current.includes("/"))
|
|
536
|
+
current = `block/${current}`;
|
|
537
|
+
while (current && !visited.has(current)) {
|
|
538
|
+
visited.add(current);
|
|
539
|
+
const filePath = `assets/minecraft/models/${current}.json`;
|
|
540
|
+
try {
|
|
541
|
+
const data = await fetchMcmetaJson(versionRef(v, "assets-json"), filePath);
|
|
542
|
+
chain.push({ path: current, data });
|
|
543
|
+
const parent = data.parent?.replace(/^minecraft:/, "");
|
|
544
|
+
if (!parent || parent.startsWith("builtin/"))
|
|
545
|
+
break;
|
|
546
|
+
current = parent;
|
|
547
|
+
}
|
|
548
|
+
catch {
|
|
549
|
+
chain.push({ path: current, data: { parent: undefined }, });
|
|
550
|
+
break;
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
// Merge textures up the chain (child wins)
|
|
554
|
+
const mergedTextures = {};
|
|
555
|
+
for (const node of [...chain].reverse()) {
|
|
556
|
+
Object.assign(mergedTextures, node.data.textures ?? {});
|
|
557
|
+
}
|
|
558
|
+
return { version: v, modelPath, chainLength: chain.length, mergedTextures, chain };
|
|
559
|
+
}
|
|
560
|
+
// ── Worldgen: structures ──────────────────────────────────────────────────────
|
|
561
|
+
/**
|
|
562
|
+
* List all vanilla worldgen structures.
|
|
563
|
+
* category: optionally scope to a sub-folder (most structures are flat, so usually omit)
|
|
564
|
+
*/
|
|
565
|
+
export async function listStructures(version) {
|
|
566
|
+
const v = version ?? "26.1.2";
|
|
567
|
+
const basePath = "data/minecraft/worldgen/structure";
|
|
568
|
+
try {
|
|
569
|
+
const entries = await listMcmetaDir(v, "data", basePath);
|
|
570
|
+
const structures = entries
|
|
571
|
+
.filter(e => e.type === "file" && e.name.endsWith(".json"))
|
|
572
|
+
.map(e => `minecraft:${e.name.replace(".json", "")}`);
|
|
573
|
+
return { version: v, count: structures.length, structures };
|
|
574
|
+
}
|
|
575
|
+
catch (err) {
|
|
576
|
+
return { version: v, error: String(err) };
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
/**
|
|
580
|
+
* Get the full JSON for a vanilla worldgen structure.
|
|
581
|
+
* id: e.g. "minecraft:village_plains", "bastion_remnant"
|
|
582
|
+
*/
|
|
583
|
+
export async function getStructureData(id, version) {
|
|
584
|
+
const v = version ?? "26.1.2";
|
|
585
|
+
const normalId = id.replace("minecraft:", "");
|
|
586
|
+
try {
|
|
587
|
+
const data = await fetchMcmetaJson(versionRef(v, "data"), `data/minecraft/worldgen/structure/${normalId}.json`);
|
|
588
|
+
return { version: v, structure: `minecraft:${normalId}`, data };
|
|
589
|
+
}
|
|
590
|
+
catch (err) {
|
|
591
|
+
return { version: v, structure: id, error: String(err) };
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
// ── Particles ─────────────────────────────────────────────────────────────────
|
|
595
|
+
/**
|
|
596
|
+
* List all vanilla particle types registered in the particle registry.
|
|
597
|
+
* Returns both the registry entry names and the particle description JSONs
|
|
598
|
+
* where available (assets/minecraft/particles/<id>.json).
|
|
599
|
+
*/
|
|
600
|
+
export async function getMcParticles(version) {
|
|
601
|
+
const v = version ?? "26.1.2";
|
|
602
|
+
// Particle descriptions live in assets branch
|
|
603
|
+
const basePath = "assets/minecraft/particles";
|
|
604
|
+
try {
|
|
605
|
+
const entries = await listMcmetaDir(v, "assets-json", basePath);
|
|
606
|
+
const particles = entries
|
|
607
|
+
.filter(e => e.type === "file" && e.name.endsWith(".json"))
|
|
608
|
+
.map(e => `minecraft:${e.name.replace(".json", "")}`);
|
|
609
|
+
return { version: v, count: particles.length, particles };
|
|
610
|
+
}
|
|
611
|
+
catch {
|
|
612
|
+
// Fall back to registry summary from mc-registries-style data
|
|
613
|
+
return { version: v, note: "Particle description files not available for this version. Use get_mc_registry_entries with registry=particle_type.", particles: [] };
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
/**
|
|
617
|
+
* Get the description JSON for a specific particle type.
|
|
618
|
+
* id: e.g. "minecraft:dust", "explosion"
|
|
619
|
+
*/
|
|
620
|
+
export async function getParticleData(id, version) {
|
|
621
|
+
const v = version ?? "26.1.2";
|
|
622
|
+
const normalId = id.replace("minecraft:", "");
|
|
623
|
+
try {
|
|
624
|
+
const data = await fetchMcmetaJson(versionRef(v, "assets-json"), `assets/minecraft/particles/${normalId}.json`);
|
|
625
|
+
return { version: v, particle: `minecraft:${normalId}`, data };
|
|
626
|
+
}
|
|
627
|
+
catch (err) {
|
|
628
|
+
return { version: v, particle: id, error: String(err) };
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
// ── Entity attributes ─────────────────────────────────────────────────────────
|
|
632
|
+
/**
|
|
633
|
+
* Get default attributes for a vanilla or modded entity type.
|
|
634
|
+
*
|
|
635
|
+
* Vanilla: reads from data/minecraft/attribute/<entity>.json, falls back to a
|
|
636
|
+
* built-in defaults table for the most common entity types.
|
|
637
|
+
* Modded: provide modId to search the mod's decompiled source for
|
|
638
|
+
* `createAttributes()` / `registerAttributes()` in the entity class.
|
|
639
|
+
*
|
|
640
|
+
* entity: e.g. "player", "zombie", "mymod:my_creature" — omit to list all vanilla attribute files
|
|
641
|
+
* modId: DB modId string or numeric id (required for modded entities)
|
|
642
|
+
*/
|
|
643
|
+
export async function getEntityAttributes(entity, version, modId) {
|
|
644
|
+
const v = version ?? "26.1.2";
|
|
645
|
+
// ── Modded entity lookup ──────────────────────────────────────────────────
|
|
646
|
+
if (modId !== undefined || (entity && entity.includes(":") && !entity.startsWith("minecraft:"))) {
|
|
647
|
+
// Resolve mod record
|
|
648
|
+
const mod = modId !== undefined
|
|
649
|
+
? await resolveModRef(String(modId))
|
|
650
|
+
: null;
|
|
651
|
+
if (!mod && modId !== undefined) {
|
|
652
|
+
return { error: `Mod not found: ${modId}` };
|
|
653
|
+
}
|
|
654
|
+
if (!entity) {
|
|
655
|
+
return { error: "entity is required when looking up modded attributes." };
|
|
656
|
+
}
|
|
657
|
+
// If not decompiled, we can't search source
|
|
658
|
+
if (!mod?.decompiled) {
|
|
659
|
+
return {
|
|
660
|
+
entity,
|
|
661
|
+
mod: mod?.modId ?? "unknown",
|
|
662
|
+
note: "Mod is not decompiled. Run decompile_mod first, then retry.",
|
|
663
|
+
attributes: [],
|
|
664
|
+
};
|
|
665
|
+
}
|
|
666
|
+
// Entity name heuristic: "mymod:my_creature" or just "my_creature"
|
|
667
|
+
const entityName = entity.includes(":") ? entity.split(":")[1] : entity;
|
|
668
|
+
// Convert snake_case to PascalCase class name heuristic
|
|
669
|
+
const className = entityName.split("_").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join("");
|
|
670
|
+
// Search decompiled source for createAttributes / registerAttributes in the entity class
|
|
671
|
+
const [createHits, registerHits] = await Promise.all([
|
|
672
|
+
searchSource(`createAttributes`, mod.id, false, 30),
|
|
673
|
+
searchSource(`registerAttributes`, mod.id, false, 20),
|
|
674
|
+
]);
|
|
675
|
+
// Filter hits to those in files matching the entity class name
|
|
676
|
+
const classFilter = (hit) => hit.file.toLowerCase().includes(entityName.replace(/_/g, "").toLowerCase()) ||
|
|
677
|
+
hit.file.toLowerCase().includes(className.toLowerCase());
|
|
678
|
+
const relevant = [
|
|
679
|
+
...createHits.filter(classFilter),
|
|
680
|
+
...registerHits.filter(classFilter),
|
|
681
|
+
];
|
|
682
|
+
if (relevant.length === 0) {
|
|
683
|
+
// Fallback: return all createAttributes hits from the mod
|
|
684
|
+
return {
|
|
685
|
+
entity,
|
|
686
|
+
mod: mod.modId,
|
|
687
|
+
entityClass: className,
|
|
688
|
+
note: `No createAttributes() / registerAttributes() found for class '${className}'. Showing all attribute-related hits in mod — narrow by class name manually.`,
|
|
689
|
+
allAttributeHits: [...createHits.slice(0, 20), ...registerHits.slice(0, 10)].map(h => ({
|
|
690
|
+
file: h.file, line: h.line, text: h.text,
|
|
691
|
+
})),
|
|
692
|
+
};
|
|
693
|
+
}
|
|
694
|
+
return {
|
|
695
|
+
entity,
|
|
696
|
+
mod: mod.modId,
|
|
697
|
+
entityClass: className,
|
|
698
|
+
source: "decompiled source search",
|
|
699
|
+
note: "These are raw source snippets from createAttributes()/registerAttributes(). Attribute base values are in the method body.",
|
|
700
|
+
attributeHits: relevant.map(h => ({ file: h.file, line: h.line, text: h.text })),
|
|
701
|
+
};
|
|
702
|
+
}
|
|
703
|
+
// ── Vanilla entity lookup ─────────────────────────────────────────────────
|
|
704
|
+
const basePath = "data/minecraft/attribute";
|
|
705
|
+
if (entity) {
|
|
706
|
+
const normalId = entity.replace("minecraft:", "");
|
|
707
|
+
// Try direct attribute file for entity
|
|
708
|
+
try {
|
|
709
|
+
const data = await fetchMcmetaJson(versionRef(v, "data"), `data/minecraft/attribute/${normalId}.json`);
|
|
710
|
+
return { version: v, entity: `minecraft:${normalId}`, source: "mcmeta data pack", data };
|
|
711
|
+
}
|
|
712
|
+
catch { /* fall through to known defaults table */ }
|
|
713
|
+
// Known defaults table (most useful for modding)
|
|
714
|
+
const KNOWN_DEFAULTS = {
|
|
715
|
+
player: [
|
|
716
|
+
{ id: "minecraft:max_health", base: 20 },
|
|
717
|
+
{ id: "minecraft:movement_speed", base: 0.1 },
|
|
718
|
+
{ id: "minecraft:attack_damage", base: 1 },
|
|
719
|
+
{ id: "minecraft:attack_speed", base: 4 },
|
|
720
|
+
{ id: "minecraft:armor", base: 0 },
|
|
721
|
+
{ id: "minecraft:armor_toughness", base: 0 },
|
|
722
|
+
{ id: "minecraft:knockback_resistance", base: 0 },
|
|
723
|
+
{ id: "minecraft:luck", base: 0 },
|
|
724
|
+
{ id: "minecraft:max_absorption", base: 0 },
|
|
725
|
+
{ id: "minecraft:follow_range", base: 32 },
|
|
726
|
+
{ id: "minecraft:safe_fall_distance", base: 3 },
|
|
727
|
+
{ id: "minecraft:fall_damage_multiplier", base: 1 },
|
|
728
|
+
{ id: "minecraft:jump_strength", base: 0.42 },
|
|
729
|
+
{ id: "minecraft:scale", base: 1 },
|
|
730
|
+
],
|
|
731
|
+
zombie: [
|
|
732
|
+
{ id: "minecraft:max_health", base: 20 },
|
|
733
|
+
{ id: "minecraft:movement_speed", base: 0.23 },
|
|
734
|
+
{ id: "minecraft:attack_damage", base: 3 },
|
|
735
|
+
{ id: "minecraft:follow_range", base: 35 },
|
|
736
|
+
{ id: "minecraft:armor", base: 0 },
|
|
737
|
+
{ id: "minecraft:knockback_resistance", base: 0 },
|
|
738
|
+
{ id: "minecraft:spawn_reinforcements_chance", base: 0.1 },
|
|
739
|
+
],
|
|
740
|
+
skeleton: [
|
|
741
|
+
{ id: "minecraft:max_health", base: 20 },
|
|
742
|
+
{ id: "minecraft:movement_speed", base: 0.25 },
|
|
743
|
+
{ id: "minecraft:attack_damage", base: 2 },
|
|
744
|
+
{ id: "minecraft:follow_range", base: 35 },
|
|
745
|
+
{ id: "minecraft:armor", base: 0 },
|
|
746
|
+
],
|
|
747
|
+
creeper: [
|
|
748
|
+
{ id: "minecraft:max_health", base: 20 },
|
|
749
|
+
{ id: "minecraft:movement_speed", base: 0.25 },
|
|
750
|
+
{ id: "minecraft:follow_range", base: 16 },
|
|
751
|
+
],
|
|
752
|
+
spider: [
|
|
753
|
+
{ id: "minecraft:max_health", base: 16 },
|
|
754
|
+
{ id: "minecraft:movement_speed", base: 0.3 },
|
|
755
|
+
{ id: "minecraft:attack_damage", base: 2 },
|
|
756
|
+
{ id: "minecraft:follow_range", base: 16 },
|
|
757
|
+
{ id: "minecraft:armor", base: 0 },
|
|
758
|
+
],
|
|
759
|
+
enderman: [
|
|
760
|
+
{ id: "minecraft:max_health", base: 40 },
|
|
761
|
+
{ id: "minecraft:movement_speed", base: 0.3 },
|
|
762
|
+
{ id: "minecraft:attack_damage", base: 7 },
|
|
763
|
+
{ id: "minecraft:follow_range", base: 64 },
|
|
764
|
+
{ id: "minecraft:armor", base: 0 },
|
|
765
|
+
],
|
|
766
|
+
blaze: [
|
|
767
|
+
{ id: "minecraft:max_health", base: 20 },
|
|
768
|
+
{ id: "minecraft:movement_speed", base: 0.23 },
|
|
769
|
+
{ id: "minecraft:attack_damage", base: 6 },
|
|
770
|
+
{ id: "minecraft:follow_range", base: 48 },
|
|
771
|
+
],
|
|
772
|
+
witch: [
|
|
773
|
+
{ id: "minecraft:max_health", base: 26 },
|
|
774
|
+
{ id: "minecraft:movement_speed", base: 0.25 },
|
|
775
|
+
{ id: "minecraft:follow_range", base: 16 },
|
|
776
|
+
],
|
|
777
|
+
villager: [
|
|
778
|
+
{ id: "minecraft:max_health", base: 20 },
|
|
779
|
+
{ id: "minecraft:movement_speed", base: 0.5 },
|
|
780
|
+
{ id: "minecraft:follow_range", base: 48 },
|
|
781
|
+
],
|
|
782
|
+
iron_golem: [
|
|
783
|
+
{ id: "minecraft:max_health", base: 100 },
|
|
784
|
+
{ id: "minecraft:movement_speed", base: 0.25 },
|
|
785
|
+
{ id: "minecraft:attack_damage", base: 15 },
|
|
786
|
+
{ id: "minecraft:knockback_resistance", base: 1 },
|
|
787
|
+
],
|
|
788
|
+
wolf: [
|
|
789
|
+
{ id: "minecraft:max_health", base: 8 },
|
|
790
|
+
{ id: "minecraft:movement_speed", base: 0.3 },
|
|
791
|
+
{ id: "minecraft:attack_damage", base: 4 },
|
|
792
|
+
{ id: "minecraft:follow_range", base: 16 },
|
|
793
|
+
{ id: "minecraft:armor", base: 0 },
|
|
794
|
+
],
|
|
795
|
+
};
|
|
796
|
+
const key = normalId.split("/").pop();
|
|
797
|
+
if (KNOWN_DEFAULTS[key]) {
|
|
798
|
+
return { version: v, entity: `minecraft:${normalId}`, source: "built-in defaults", attributes: KNOWN_DEFAULTS[key] };
|
|
799
|
+
}
|
|
800
|
+
return {
|
|
801
|
+
version: v,
|
|
802
|
+
entity: `minecraft:${normalId}`,
|
|
803
|
+
note: "No attribute data file found for this entity. For modded entities pass the modId parameter and ensure the mod is decompiled.",
|
|
804
|
+
tip: "Use get_mc_class_members on the entity class to see createAttributes() declarations.",
|
|
805
|
+
attributes: [],
|
|
806
|
+
};
|
|
807
|
+
}
|
|
808
|
+
// List all vanilla attribute files
|
|
809
|
+
try {
|
|
810
|
+
const entries = await listMcmetaDir(v, "data", basePath);
|
|
811
|
+
const attrs = entries
|
|
812
|
+
.filter(e => e.type === "file" && e.name.endsWith(".json"))
|
|
813
|
+
.map(e => `minecraft:${e.name.replace(".json", "")}`);
|
|
814
|
+
return { version: v, source: "mcmeta data pack", count: attrs.length, attributes: attrs };
|
|
815
|
+
}
|
|
816
|
+
catch {
|
|
817
|
+
return {
|
|
818
|
+
version: v,
|
|
819
|
+
note: "Attribute data folder not present in this version. Use get_mc_class_members on net/minecraft/world/entity/ai/attributes/Attributes to see all registered attributes.",
|
|
820
|
+
knownEntities: Object.keys({ player: 1, zombie: 1, skeleton: 1, creeper: 1, spider: 1, enderman: 1, blaze: 1, witch: 1, villager: 1, iron_golem: 1, wolf: 1 }),
|
|
821
|
+
};
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
//# sourceMappingURL=vanilla-data.js.map
|