@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,679 @@
|
|
|
1
|
+
import { indexJar, inspectClass, getBytecode } from "../java-tools.js";
|
|
2
|
+
import { searchClasses } from "../search.js";
|
|
3
|
+
import { listClasses } from "../jar.js";
|
|
4
|
+
import { formatClassMembers } from "../access-flags.js";
|
|
5
|
+
import { findModById, resolveModRefSlim, listModsSlim, findModClassesForCrossModSearch, } from "../repositories/mod.js";
|
|
6
|
+
import { validateDbId, validateClassName } from "../validate.js";
|
|
7
|
+
import { assertJarPath } from "../security.js";
|
|
8
|
+
async function getModJar(dbId) {
|
|
9
|
+
const mod = await findModById(dbId);
|
|
10
|
+
if (!mod)
|
|
11
|
+
throw new Error(`Mod #${dbId} not found`);
|
|
12
|
+
assertJarPath(mod.jarPath);
|
|
13
|
+
return mod.jarPath;
|
|
14
|
+
}
|
|
15
|
+
export async function searchModClass(dbId, query) {
|
|
16
|
+
validateDbId(dbId);
|
|
17
|
+
const jarPath = await getModJar(dbId);
|
|
18
|
+
const classes = listClasses(jarPath)
|
|
19
|
+
.map((c) => c.replace(/\.class$/, ""))
|
|
20
|
+
.filter((c) => !c.includes("$") || query.includes("$")); // hide inner classes unless explicitly searched
|
|
21
|
+
return searchClasses(classes, query);
|
|
22
|
+
}
|
|
23
|
+
export async function getModClassMembers(dbId, className) {
|
|
24
|
+
validateDbId(dbId);
|
|
25
|
+
validateClassName(className);
|
|
26
|
+
const jarPath = await getModJar(dbId);
|
|
27
|
+
const internal = className.replace(/\./g, "/");
|
|
28
|
+
const info = await inspectClass(jarPath, internal);
|
|
29
|
+
return formatClassMembers(info);
|
|
30
|
+
}
|
|
31
|
+
export async function getModClassBytecode(dbId, className) {
|
|
32
|
+
validateDbId(dbId);
|
|
33
|
+
validateClassName(className);
|
|
34
|
+
const jarPath = await getModJar(dbId);
|
|
35
|
+
return getBytecode(jarPath, className.replace(/\./g, "/"));
|
|
36
|
+
}
|
|
37
|
+
export async function findModReferences(dbId, target) {
|
|
38
|
+
validateDbId(dbId);
|
|
39
|
+
const jarPath = await getModJar(dbId);
|
|
40
|
+
const index = await indexJar(jarPath);
|
|
41
|
+
return index.references[target] ?? [];
|
|
42
|
+
}
|
|
43
|
+
export async function getModInheritance(dbId, className) {
|
|
44
|
+
validateDbId(dbId);
|
|
45
|
+
validateClassName(className);
|
|
46
|
+
const jarPath = await getModJar(dbId);
|
|
47
|
+
const internal = className.replace(/\./g, "/");
|
|
48
|
+
const index = await indexJar(jarPath);
|
|
49
|
+
const classes = Object.values(index.classes);
|
|
50
|
+
const target = classes.find((c) => c.name === internal);
|
|
51
|
+
if (!target)
|
|
52
|
+
throw new Error(`Class not found: ${internal}`);
|
|
53
|
+
const subclasses = classes
|
|
54
|
+
.filter((c) => c.superName === internal)
|
|
55
|
+
.map((c) => c.name);
|
|
56
|
+
const implementors = classes
|
|
57
|
+
.filter((c) => c.interfaces.includes(internal))
|
|
58
|
+
.map((c) => c.name);
|
|
59
|
+
return {
|
|
60
|
+
className: internal,
|
|
61
|
+
superClass: target.superName,
|
|
62
|
+
interfaces: target.interfaces,
|
|
63
|
+
subclasses,
|
|
64
|
+
implementors,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
export async function diffModVersions(dbIdA, dbIdB) {
|
|
68
|
+
validateDbId(dbIdA);
|
|
69
|
+
validateDbId(dbIdB);
|
|
70
|
+
const [jarA, jarB] = await Promise.all([
|
|
71
|
+
getModJar(dbIdA),
|
|
72
|
+
getModJar(dbIdB),
|
|
73
|
+
]);
|
|
74
|
+
const [a, b] = await Promise.all([
|
|
75
|
+
findModById(dbIdA),
|
|
76
|
+
findModById(dbIdB),
|
|
77
|
+
]);
|
|
78
|
+
if (!a)
|
|
79
|
+
throw new Error(`Mod #${dbIdA} not found`);
|
|
80
|
+
if (!b)
|
|
81
|
+
throw new Error(`Mod #${dbIdB} not found`);
|
|
82
|
+
const classesA = new Set(listClasses(jarA).map((c) => c.replace(/\.class$/, "")));
|
|
83
|
+
const classesB = new Set(listClasses(jarB).map((c) => c.replace(/\.class$/, "")));
|
|
84
|
+
const added = [...classesB].filter((c) => !classesA.has(c));
|
|
85
|
+
const removed = [...classesA].filter((c) => !classesB.has(c));
|
|
86
|
+
const common = [...classesA].filter((c) => classesB.has(c));
|
|
87
|
+
return {
|
|
88
|
+
modA: { id: dbIdA, modId: a.modId, version: a.version },
|
|
89
|
+
modB: { id: dbIdB, modId: b.modId, version: b.version },
|
|
90
|
+
summary: { added: added.length, removed: removed.length, common: common.length },
|
|
91
|
+
added,
|
|
92
|
+
removed,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Find all mod classes in the DB that extend or implement a given class or interface.
|
|
97
|
+
* Uses the modClass table (superClass + interfaces columns) — requires reindex_classes to have run.
|
|
98
|
+
*
|
|
99
|
+
* target: internal slash-separated name, e.g. "net/minecraft/world/entity/Entity"
|
|
100
|
+
* or dot-separated, e.g. "net.minecraft.world.entity.Entity"
|
|
101
|
+
* modId: optional filter to a specific mod
|
|
102
|
+
*/
|
|
103
|
+
export async function findImplementors(target, modId, limit = 100, transitive = false) {
|
|
104
|
+
const internal = target.replace(/\./g, "/");
|
|
105
|
+
let modDbId;
|
|
106
|
+
if (modId !== undefined) {
|
|
107
|
+
if (typeof modId === "number") {
|
|
108
|
+
modDbId = modId;
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
const mod = await resolveModRefSlim(modId);
|
|
112
|
+
if (!mod)
|
|
113
|
+
return { error: `Mod not found: ${modId}` };
|
|
114
|
+
modDbId = mod.id;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
const where = (superClass, iface) => ({
|
|
118
|
+
...(superClass !== undefined ? { superClass } : {}),
|
|
119
|
+
...(iface !== undefined ? { interfaces: { has: iface } } : {}),
|
|
120
|
+
...(modDbId ? { modId: modDbId } : {}),
|
|
121
|
+
});
|
|
122
|
+
const fetchDirect = async (t) => {
|
|
123
|
+
const [bySuper, byIface] = await findModClassesForCrossModSearch(where(t, undefined), where(undefined, t), limit);
|
|
124
|
+
return [...bySuper, ...byIface];
|
|
125
|
+
};
|
|
126
|
+
const format = (cls) => ({
|
|
127
|
+
className: cls.className,
|
|
128
|
+
mod: cls.mod.modId,
|
|
129
|
+
modDisplay: cls.mod.displayName,
|
|
130
|
+
version: cls.mod.version,
|
|
131
|
+
});
|
|
132
|
+
if (!transitive) {
|
|
133
|
+
const direct = await fetchDirect(internal);
|
|
134
|
+
const bySuper = direct.filter(c => c.superClass === internal);
|
|
135
|
+
const byIface = direct.filter(c => c.interfaces.includes(internal));
|
|
136
|
+
return {
|
|
137
|
+
target: internal,
|
|
138
|
+
transitive: false,
|
|
139
|
+
directSubclasses: { count: bySuper.length, classes: bySuper.map(format) },
|
|
140
|
+
implementors: { count: byIface.length, classes: byIface.map(format) },
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
// BFS transitive walk
|
|
144
|
+
const visited = new Set([internal]);
|
|
145
|
+
const queue = [internal];
|
|
146
|
+
const allFound = [];
|
|
147
|
+
while (queue.length > 0 && allFound.length < limit) {
|
|
148
|
+
const current = queue.shift();
|
|
149
|
+
const found = await fetchDirect(current);
|
|
150
|
+
for (const cls of found) {
|
|
151
|
+
if (!visited.has(cls.className)) {
|
|
152
|
+
visited.add(cls.className);
|
|
153
|
+
allFound.push(cls);
|
|
154
|
+
queue.push(cls.className);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return {
|
|
159
|
+
target: internal,
|
|
160
|
+
transitive: true,
|
|
161
|
+
count: allFound.length,
|
|
162
|
+
note: allFound.length >= limit ? `Capped at ${limit}. Use filter or increase limit.` : undefined,
|
|
163
|
+
classes: allFound.map(format),
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
// ── Annotation search ──────────────────────────────────────────────────────────
|
|
167
|
+
/**
|
|
168
|
+
* Find all classes across the DB annotated with a given annotation.
|
|
169
|
+
* Annotations appear in the JAR index references map the same way class references do —
|
|
170
|
+
* any class that uses @MyAnnotation will have the annotation class in its reference list.
|
|
171
|
+
* This works without decompilation.
|
|
172
|
+
*
|
|
173
|
+
* annotation: internal slash-separated name, e.g. "net/neoforged/bus/api/SubscribeEvent"
|
|
174
|
+
* or dot-separated: "net.neoforged.bus.api.SubscribeEvent"
|
|
175
|
+
* modId: optional — limit to one mod
|
|
176
|
+
* limit: max results per mod (default 200 total)
|
|
177
|
+
*/
|
|
178
|
+
export async function findAnnotatedClasses(annotation, modId, limit = 200) {
|
|
179
|
+
const internal = annotation.replace(/\./g, "/");
|
|
180
|
+
let mods;
|
|
181
|
+
if (modId !== undefined) {
|
|
182
|
+
const mod = await resolveModRefSlim(modId);
|
|
183
|
+
if (!mod)
|
|
184
|
+
return { error: `Mod not found: ${modId}` };
|
|
185
|
+
mods = [mod];
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
mods = await listModsSlim();
|
|
189
|
+
}
|
|
190
|
+
const results = [];
|
|
191
|
+
let total = 0;
|
|
192
|
+
for (const mod of mods) {
|
|
193
|
+
if (total >= limit)
|
|
194
|
+
break;
|
|
195
|
+
try {
|
|
196
|
+
const index = await indexJar(mod.jarPath);
|
|
197
|
+
const refs = index.references[internal] ?? [];
|
|
198
|
+
if (refs.length > 0) {
|
|
199
|
+
results.push({ mod: mod.modId, modDisplay: mod.displayName, version: mod.version, classes: refs });
|
|
200
|
+
total += refs.length;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
catch {
|
|
204
|
+
// skip mods whose JARs can't be indexed
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
return {
|
|
208
|
+
annotation: internal,
|
|
209
|
+
totalMods: results.length,
|
|
210
|
+
totalClasses: total,
|
|
211
|
+
note: total >= limit ? `Capped at ${limit} total. Narrow with modId.` : undefined,
|
|
212
|
+
results,
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
// ── Registration scanner ───────────────────────────────────────────────────────
|
|
216
|
+
/** Known class paths for each registration category, covering NeoForge, Forge, Fabric. */
|
|
217
|
+
const REGISTRATION_TARGETS = {
|
|
218
|
+
deferredRegister: [
|
|
219
|
+
"net/neoforged/neoforge/registries/DeferredRegister",
|
|
220
|
+
"net/minecraftforge/registries/DeferredRegister",
|
|
221
|
+
"net/fabricmc/fabric/api/object/builder/v1/registry/FabricRegistryBuilder",
|
|
222
|
+
],
|
|
223
|
+
eventHandlers: [
|
|
224
|
+
"net/neoforged/bus/api/SubscribeEvent",
|
|
225
|
+
"net/neoforged/fml/common/EventBusSubscriber",
|
|
226
|
+
"net/minecraftforge/eventbus/api/SubscribeEvent",
|
|
227
|
+
"net/minecraftforge/fml/common/Mod$EventBusSubscriber",
|
|
228
|
+
],
|
|
229
|
+
commands: [
|
|
230
|
+
"net/minecraft/commands/Commands",
|
|
231
|
+
"net/minecraft/commands/CommandSourceStack",
|
|
232
|
+
"com/mojang/brigadier/CommandDispatcher",
|
|
233
|
+
],
|
|
234
|
+
keybindings: [
|
|
235
|
+
"net/minecraft/client/KeyMapping",
|
|
236
|
+
"com/mojang/blaze3d/platform/InputConstants",
|
|
237
|
+
],
|
|
238
|
+
network: [
|
|
239
|
+
"net/neoforged/neoforge/network/registration/PayloadRegistrar",
|
|
240
|
+
"net/neoforged/neoforge/network/event/RegisterPayloadHandlersEvent",
|
|
241
|
+
"net/minecraftforge/network/simple/SimpleChannel",
|
|
242
|
+
"net/fabricmc/fabric/api/networking/v1/PayloadTypeRegistry",
|
|
243
|
+
],
|
|
244
|
+
config: [
|
|
245
|
+
"net/neoforged/neoforge/common/ModConfigSpec",
|
|
246
|
+
"net/minecraftforge/common/ForgeConfigSpec",
|
|
247
|
+
"me/shedaniel/autoconfig/AutoConfig",
|
|
248
|
+
],
|
|
249
|
+
capabilities: [
|
|
250
|
+
"net/neoforged/neoforge/capabilities/RegisterCapabilitiesEvent",
|
|
251
|
+
"net/minecraftforge/event/AttachCapabilitiesEvent",
|
|
252
|
+
"net/fabricmc/fabric/api/lookup/v1/block/BlockApiLookup",
|
|
253
|
+
],
|
|
254
|
+
lootModifiers: [
|
|
255
|
+
"net/neoforged/neoforge/common/loot/IGlobalLootModifier",
|
|
256
|
+
"net/minecraftforge/common/loot/IGlobalLootModifier",
|
|
257
|
+
"net/fabricmc/fabric/api/loot/v2/LootTableEvents",
|
|
258
|
+
],
|
|
259
|
+
datapackRegistries: [
|
|
260
|
+
"net/neoforged/neoforge/registries/DataPackRegistriesHooks",
|
|
261
|
+
"net/neoforged/neoforge/registries/NeoForgeRegistries",
|
|
262
|
+
"net/neoforged/fml/common/registry/GameRegistry",
|
|
263
|
+
],
|
|
264
|
+
};
|
|
265
|
+
/**
|
|
266
|
+
* Scan a mod JAR's class index for registration patterns: DeferredRegister usage,
|
|
267
|
+
* event handlers, command registrars, keybindings, network payloads, config builders,
|
|
268
|
+
* capabilities, loot modifiers, and datapack registries.
|
|
269
|
+
* Uses the JAR index (no decompilation required). Results name the classes involved;
|
|
270
|
+
* use class_members or get_source to drill down into any of them.
|
|
271
|
+
*/
|
|
272
|
+
export async function scanModRegistrations(dbId) {
|
|
273
|
+
const jarPath = await getModJar(dbId);
|
|
274
|
+
const index = await indexJar(jarPath);
|
|
275
|
+
const findClasses = (targets) => {
|
|
276
|
+
const found = new Set();
|
|
277
|
+
for (const t of targets) {
|
|
278
|
+
for (const c of (index.references[t] ?? [])) {
|
|
279
|
+
found.add(c);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
// Filter out inner classes of the same name (reduce noise; keep if no parent)
|
|
283
|
+
return [...found].sort();
|
|
284
|
+
};
|
|
285
|
+
const results = {};
|
|
286
|
+
for (const [category, targets] of Object.entries(REGISTRATION_TARGETS)) {
|
|
287
|
+
const classes = findClasses(targets);
|
|
288
|
+
if (classes.length > 0) {
|
|
289
|
+
results[category] = { count: classes.length, classes };
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
const totalClasses = Object.values(results).reduce((n, r) => n + r.count, 0);
|
|
293
|
+
return {
|
|
294
|
+
mod: dbId,
|
|
295
|
+
totalMatchingClasses: totalClasses,
|
|
296
|
+
note: "Classes listed reference the registration APIs. Use mod_bytecode class_members or mod source to inspect them further.",
|
|
297
|
+
registrations: results,
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
// ── Cross-mod reference search ─────────────────────────────────────────────────
|
|
301
|
+
/**
|
|
302
|
+
* Find which mods in the DB reference a given class, method, or field.
|
|
303
|
+
* Unlike find_refs (single JAR), this scans every ingested mod's index.
|
|
304
|
+
*
|
|
305
|
+
* target: slash-separated class/method/field, e.g. "net/minecraft/world/entity/LivingEntity"
|
|
306
|
+
* or "mymod/SomeClass:myMethod:(I)V"
|
|
307
|
+
* mcVersion: optional filter
|
|
308
|
+
* loader: optional filter
|
|
309
|
+
*/
|
|
310
|
+
export async function crossModRefs(target, mcVersion, loader, limit = 500) {
|
|
311
|
+
const internal = target.replace(/\./g, "/");
|
|
312
|
+
const mods = await listModsSlim({ mcVersion, loader });
|
|
313
|
+
const results = [];
|
|
314
|
+
let total = 0;
|
|
315
|
+
for (const mod of mods) {
|
|
316
|
+
if (total >= limit)
|
|
317
|
+
break;
|
|
318
|
+
try {
|
|
319
|
+
const index = await indexJar(mod.jarPath);
|
|
320
|
+
const refs = index.references[internal] ?? [];
|
|
321
|
+
if (refs.length > 0) {
|
|
322
|
+
results.push({ mod: mod.modId, modDisplay: mod.displayName, version: mod.version, loader: mod.loader, referencingClasses: refs });
|
|
323
|
+
total += refs.length;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
catch { /* skip unindexable JARs */ }
|
|
327
|
+
}
|
|
328
|
+
return {
|
|
329
|
+
target: internal,
|
|
330
|
+
mcVersion: mcVersion ?? "(all)",
|
|
331
|
+
loader: loader ?? "(all)",
|
|
332
|
+
totalMods: results.length,
|
|
333
|
+
totalReferences: total,
|
|
334
|
+
note: total >= limit ? `Capped at ${limit} total references. Use mcVersion/loader to narrow.` : undefined,
|
|
335
|
+
results,
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
// ── Event listener search ──────────────────────────────────────────────────────
|
|
339
|
+
// Known @SubscribeEvent annotation paths across loaders
|
|
340
|
+
const SUBSCRIBE_EVENT_ANNOTATIONS = [
|
|
341
|
+
"net/neoforged/bus/api/SubscribeEvent",
|
|
342
|
+
"net/minecraftforge/eventbus/api/SubscribeEvent",
|
|
343
|
+
];
|
|
344
|
+
/**
|
|
345
|
+
* Find all @SubscribeEvent (or equivalent) methods across the DB that listen
|
|
346
|
+
* to a specific event class. Scans JAR indexes — no decompilation needed.
|
|
347
|
+
*
|
|
348
|
+
* event: internal class name of the event, e.g. "net/neoforged/neoforge/event/entity/living/LivingDeathEvent"
|
|
349
|
+
* or partial name match, e.g. "LivingDeathEvent"
|
|
350
|
+
* modId: optional — limit to one mod
|
|
351
|
+
*/
|
|
352
|
+
export async function findEventListeners(event, modId, limit = 300) {
|
|
353
|
+
const eventInternal = event.replace(/\./g, "/");
|
|
354
|
+
let mods;
|
|
355
|
+
if (modId !== undefined) {
|
|
356
|
+
const mod = await resolveModRefSlim(modId);
|
|
357
|
+
if (!mod)
|
|
358
|
+
return { error: `Mod not found: ${modId}` };
|
|
359
|
+
mods = [mod];
|
|
360
|
+
}
|
|
361
|
+
else {
|
|
362
|
+
mods = await listModsSlim();
|
|
363
|
+
}
|
|
364
|
+
const results = [];
|
|
365
|
+
let total = 0;
|
|
366
|
+
for (const mod of mods) {
|
|
367
|
+
if (total >= limit)
|
|
368
|
+
break;
|
|
369
|
+
try {
|
|
370
|
+
const index = await indexJar(mod.jarPath);
|
|
371
|
+
// Find classes that reference the event AND any @SubscribeEvent annotation
|
|
372
|
+
const eventRefs = new Set(index.references[eventInternal] ?? []);
|
|
373
|
+
// Also check partial name match across the reference keys
|
|
374
|
+
if (eventRefs.size === 0) {
|
|
375
|
+
for (const [key, classes] of Object.entries(index.references)) {
|
|
376
|
+
if (key.includes(eventInternal) || key.endsWith("/" + eventInternal)) {
|
|
377
|
+
for (const c of classes)
|
|
378
|
+
eventRefs.add(c);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
if (eventRefs.size === 0)
|
|
383
|
+
continue;
|
|
384
|
+
const subscriberClasses = new Set();
|
|
385
|
+
for (const ann of SUBSCRIBE_EVENT_ANNOTATIONS) {
|
|
386
|
+
for (const c of (index.references[ann] ?? []))
|
|
387
|
+
subscriberClasses.add(c);
|
|
388
|
+
}
|
|
389
|
+
// Intersection: classes that reference the event AND have @SubscribeEvent methods
|
|
390
|
+
const candidates = [...eventRefs].filter(c => subscriberClasses.has(c));
|
|
391
|
+
if (candidates.length === 0)
|
|
392
|
+
continue;
|
|
393
|
+
// For each candidate, find which methods take this event via inspectClass
|
|
394
|
+
const listeners = [];
|
|
395
|
+
for (const cls of candidates) {
|
|
396
|
+
try {
|
|
397
|
+
const info = await inspectClass(mod.jarPath, cls);
|
|
398
|
+
// Methods whose descriptor contains the event class
|
|
399
|
+
const matching = info.methods
|
|
400
|
+
.filter(m => m.descriptor.includes(eventInternal.replace(/\//g, "/")) ||
|
|
401
|
+
m.descriptor.includes(eventInternal.split("/").pop()))
|
|
402
|
+
.map(m => `${m.name}${m.descriptor}`);
|
|
403
|
+
if (matching.length > 0) {
|
|
404
|
+
listeners.push({ className: cls, methods: matching });
|
|
405
|
+
}
|
|
406
|
+
else {
|
|
407
|
+
// Can't narrow to method — include class anyway
|
|
408
|
+
listeners.push({ className: cls, methods: [] });
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
catch {
|
|
412
|
+
listeners.push({ className: cls, methods: [] });
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
results.push({ mod: mod.modId, modDisplay: mod.displayName, version: mod.version, listeners });
|
|
416
|
+
total += candidates.length;
|
|
417
|
+
}
|
|
418
|
+
catch { /* skip */ }
|
|
419
|
+
}
|
|
420
|
+
return {
|
|
421
|
+
event: eventInternal,
|
|
422
|
+
totalMods: results.length,
|
|
423
|
+
totalListeners: total,
|
|
424
|
+
note: total >= limit ? `Capped at ${limit}. Use modId to narrow.` : undefined,
|
|
425
|
+
results,
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
// ── Optional mod integration detection ────────────────────────────────────────
|
|
429
|
+
// Patterns that indicate conditional/optional mod integrations in bytecode
|
|
430
|
+
const OPTIONAL_INTEGRATION_PATTERNS = {
|
|
431
|
+
"modloaded_check": [
|
|
432
|
+
"net/neoforged/fml/ModList",
|
|
433
|
+
"net/minecraftforge/fml/ModList",
|
|
434
|
+
"net/fabricmc/loader/api/FabricLoader",
|
|
435
|
+
],
|
|
436
|
+
"optional_interface": [
|
|
437
|
+
"net/minecraftforge/fml/common/Optional$Interface",
|
|
438
|
+
"net/minecraftforge/fml/common/Optional$Method",
|
|
439
|
+
],
|
|
440
|
+
"services_integration": [
|
|
441
|
+
"java/util/ServiceLoader",
|
|
442
|
+
],
|
|
443
|
+
"capability_integration": [
|
|
444
|
+
"net/neoforged/neoforge/capabilities/Capabilities",
|
|
445
|
+
"net/minecraftforge/common/capabilities/ForgeCapabilities",
|
|
446
|
+
],
|
|
447
|
+
"curios_integration": [
|
|
448
|
+
"top/theillusivec4/curios/api/CuriosApi",
|
|
449
|
+
"top/theillusivec4/curios/api/SlotContext",
|
|
450
|
+
],
|
|
451
|
+
"jei_integration": [
|
|
452
|
+
"mezz/jei/api/IModPlugin",
|
|
453
|
+
"mezz/jei/api/registration/IRecipeRegistration",
|
|
454
|
+
],
|
|
455
|
+
"jade_integration": [
|
|
456
|
+
"snownee/jade/api/IWailaPlugin",
|
|
457
|
+
"snownee/jade/api/BlockAccessor",
|
|
458
|
+
],
|
|
459
|
+
"rei_integration": [
|
|
460
|
+
"me/shedaniel/rei/api/common/plugins/REIPlugin",
|
|
461
|
+
],
|
|
462
|
+
"jmapfrontiers_integration": [
|
|
463
|
+
"journeymap/client/api/IClientPlugin",
|
|
464
|
+
],
|
|
465
|
+
"waila_integration": [
|
|
466
|
+
"mcp/mobius/waila/api/IWailaPlugin",
|
|
467
|
+
],
|
|
468
|
+
"top_integration": [
|
|
469
|
+
"mcjty/theoneprobe/api/IProbeProvider",
|
|
470
|
+
],
|
|
471
|
+
"patchouli_integration": [
|
|
472
|
+
"vazkii/patchouli/api/PatchouliAPI",
|
|
473
|
+
],
|
|
474
|
+
};
|
|
475
|
+
/**
|
|
476
|
+
* Scan a mod JAR for optional/conditional mod integration patterns.
|
|
477
|
+
* Detects: ModList.isLoaded() calls, @OptionalInterface, capability hooks,
|
|
478
|
+
* and hard-coded integration checks for popular mods (JEI, Jade, REI, etc.)
|
|
479
|
+
* Works from JAR index — no decompilation needed.
|
|
480
|
+
*/
|
|
481
|
+
export async function findOptionalIntegrations(dbId) {
|
|
482
|
+
const jarPath = await getModJar(dbId);
|
|
483
|
+
const index = await indexJar(jarPath);
|
|
484
|
+
const detected = {};
|
|
485
|
+
for (const [category, targets] of Object.entries(OPTIONAL_INTEGRATION_PATTERNS)) {
|
|
486
|
+
const found = new Set();
|
|
487
|
+
for (const target of targets) {
|
|
488
|
+
for (const cls of (index.references[target] ?? []))
|
|
489
|
+
found.add(cls);
|
|
490
|
+
}
|
|
491
|
+
if (found.size > 0) {
|
|
492
|
+
detected[category] = { pattern: targets[0], classes: [...found].sort() };
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
// Also look for string constants referencing known modIds (heuristic via field refs)
|
|
496
|
+
// by scanning for classes with many external-mod package references
|
|
497
|
+
const externalPackages = new Set();
|
|
498
|
+
for (const refKey of Object.keys(index.references)) {
|
|
499
|
+
const pkg = refKey.split("/").slice(0, 3).join("/");
|
|
500
|
+
const modPkg = refKey.split("/")[0];
|
|
501
|
+
if (!["net", "com", "org", "java", "javax"].includes(modPkg)) {
|
|
502
|
+
externalPackages.add(pkg);
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
return {
|
|
506
|
+
mod: dbId,
|
|
507
|
+
totalIntegrationCategories: Object.keys(detected).length,
|
|
508
|
+
note: "Detected via bytecode reference analysis. A category being absent does not mean no integration — some mods use reflection.",
|
|
509
|
+
integrations: detected,
|
|
510
|
+
externalTopPackages: [...externalPackages].sort().slice(0, 30),
|
|
511
|
+
};
|
|
512
|
+
}
|
|
513
|
+
// ── Network payload inventory ──────────────────────────────────────────────────
|
|
514
|
+
// Classes that indicate network packet/payload registration across loaders
|
|
515
|
+
const NETWORK_PAYLOAD_TARGETS = {
|
|
516
|
+
"neoforge_payload": [
|
|
517
|
+
"net/neoforged/neoforge/network/handling/IPayloadContext",
|
|
518
|
+
"net/neoforged/neoforge/network/codec/NetworkCodecBuf",
|
|
519
|
+
"net/neoforged/neoforge/network/registration/NetworkRegistry",
|
|
520
|
+
],
|
|
521
|
+
"custom_packet_payload": [
|
|
522
|
+
"net/minecraft/network/protocol/common/custom/CustomPacketPayload",
|
|
523
|
+
],
|
|
524
|
+
"stream_codec": [
|
|
525
|
+
"net/minecraft/network/codec/StreamCodec",
|
|
526
|
+
"net/minecraft/network/codec/ByteBufCodecs",
|
|
527
|
+
],
|
|
528
|
+
"forge_channel": [
|
|
529
|
+
"net/minecraftforge/network/Channel",
|
|
530
|
+
"net/minecraftforge/network/NetworkRegistry",
|
|
531
|
+
"net/minecraftforge/network/simple/SimpleChannel",
|
|
532
|
+
],
|
|
533
|
+
"fabric_packet": [
|
|
534
|
+
"net/fabricmc/fabric/api/networking/v1/ServerPlayNetworking",
|
|
535
|
+
"net/fabricmc/fabric/api/networking/v1/PayloadTypeRegistry",
|
|
536
|
+
],
|
|
537
|
+
};
|
|
538
|
+
/**
|
|
539
|
+
* Inventory all network packet/payload classes a mod ships.
|
|
540
|
+
* Identifies classes implementing CustomPacketPayload, StreamCodec types,
|
|
541
|
+
* and loader-specific network channel patterns.
|
|
542
|
+
* No decompilation needed — works from JAR index + class inspection.
|
|
543
|
+
*/
|
|
544
|
+
export async function findNetworkPayloads(dbId) {
|
|
545
|
+
const jarPath = await getModJar(dbId);
|
|
546
|
+
const index = await indexJar(jarPath);
|
|
547
|
+
// Gather all candidate classes by pattern category
|
|
548
|
+
const byCategory = {};
|
|
549
|
+
for (const [cat, targets] of Object.entries(NETWORK_PAYLOAD_TARGETS)) {
|
|
550
|
+
const found = new Set();
|
|
551
|
+
for (const target of targets) {
|
|
552
|
+
for (const cls of (index.references[target] ?? []))
|
|
553
|
+
found.add(cls);
|
|
554
|
+
}
|
|
555
|
+
if (found.size > 0)
|
|
556
|
+
byCategory[cat] = found;
|
|
557
|
+
}
|
|
558
|
+
// Also find CustomPacketPayload implementors via inheritance index
|
|
559
|
+
const payloadImpls = [];
|
|
560
|
+
const payloadInterface = "net/minecraft/network/protocol/common/custom/CustomPacketPayload";
|
|
561
|
+
for (const [cls, info] of Object.entries(index.classes ?? {})) {
|
|
562
|
+
if ((info.interfaces ?? []).includes(payloadInterface))
|
|
563
|
+
payloadImpls.push(cls);
|
|
564
|
+
}
|
|
565
|
+
// Collect unique payload classes and inspect them for TYPE field (packet id)
|
|
566
|
+
const allCandidates = new Set([
|
|
567
|
+
...payloadImpls,
|
|
568
|
+
...Object.values(byCategory).flatMap(s => [...s]),
|
|
569
|
+
]);
|
|
570
|
+
const payloads = [];
|
|
571
|
+
for (const cls of allCandidates) {
|
|
572
|
+
// Determine which category it falls in
|
|
573
|
+
let cat = "unknown";
|
|
574
|
+
if (payloadImpls.includes(cls))
|
|
575
|
+
cat = "custom_packet_payload";
|
|
576
|
+
else {
|
|
577
|
+
for (const [c, s] of Object.entries(byCategory)) {
|
|
578
|
+
if (s.has(cls)) {
|
|
579
|
+
cat = c;
|
|
580
|
+
break;
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
try {
|
|
585
|
+
const info = await inspectClass(jarPath, cls);
|
|
586
|
+
const typeFields = info.fields.filter(f => f.name === "TYPE" || f.name === "ID" || f.name === "PACKET_ID" ||
|
|
587
|
+
f.descriptor.includes("ResourceLocation") || f.descriptor.includes("CustomPacketPayload$Type")).map(f => `${f.name}: ${f.descriptor}`);
|
|
588
|
+
const handleMethods = info.methods.filter(m => m.name === "handle" || m.name === "write" || m.name === "codec" || m.name === "type").map(m => `${m.name}${m.descriptor}`);
|
|
589
|
+
payloads.push({ className: cls, fields: typeFields, methods: handleMethods, category: cat });
|
|
590
|
+
}
|
|
591
|
+
catch {
|
|
592
|
+
payloads.push({ className: cls, fields: [], methods: [], category: cat });
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
return {
|
|
596
|
+
mod: dbId,
|
|
597
|
+
totalPayloadClasses: payloads.length,
|
|
598
|
+
note: "Payload classes identified via bytecode reference analysis. Use mod_bytecode class_members for full detail on any class.",
|
|
599
|
+
byCategory: Object.fromEntries(Object.entries(byCategory).map(([k, v]) => [k, [...v]])),
|
|
600
|
+
payloads: payloads.sort((a, b) => a.className.localeCompare(b.className)),
|
|
601
|
+
};
|
|
602
|
+
}
|
|
603
|
+
// ── Config schema extraction ───────────────────────────────────────────────────
|
|
604
|
+
const CONFIG_BUILDER_TARGETS = {
|
|
605
|
+
"neoforge_config": [
|
|
606
|
+
"net/neoforged/neoforge/common/ModConfigSpec$Builder",
|
|
607
|
+
"net/neoforged/neoforge/common/ModConfigSpec",
|
|
608
|
+
],
|
|
609
|
+
"forge_config": [
|
|
610
|
+
"net/minecraftforge/common/ForgeConfigSpec$Builder",
|
|
611
|
+
"net/minecraftforge/common/ForgeConfigSpec",
|
|
612
|
+
],
|
|
613
|
+
"cloth_config": [
|
|
614
|
+
"me/shedaniel/clothconfig2/api/ConfigBuilder",
|
|
615
|
+
"me/shedaniel/clothconfig2/api/ConfigCategory",
|
|
616
|
+
],
|
|
617
|
+
"auto_config": [
|
|
618
|
+
"me/shedaniel/autoconfig/AutoConfig",
|
|
619
|
+
"me/shedaniel/autoconfig/annotation/Config",
|
|
620
|
+
],
|
|
621
|
+
"night_config": [
|
|
622
|
+
"com/electronwill/nightconfig/core/Config",
|
|
623
|
+
"com/electronwill/nightconfig/toml/TomlFormat",
|
|
624
|
+
],
|
|
625
|
+
};
|
|
626
|
+
/**
|
|
627
|
+
* Extract the config schema from a mod's JAR by analysing which classes use
|
|
628
|
+
* config builder APIs. Works without decompilation — identifies the classes
|
|
629
|
+
* that define config keys, then uses class inspection to surface field names.
|
|
630
|
+
*
|
|
631
|
+
* For NeoForge/Forge ModConfigSpec the builder class typically has string
|
|
632
|
+
* define/defineInRange/defineEnum calls; field names are the best proxy.
|
|
633
|
+
*/
|
|
634
|
+
export async function extractConfigSchema(dbId) {
|
|
635
|
+
const jarPath = await getModJar(dbId);
|
|
636
|
+
const index = await indexJar(jarPath);
|
|
637
|
+
const detected = {};
|
|
638
|
+
for (const [system, targets] of Object.entries(CONFIG_BUILDER_TARGETS)) {
|
|
639
|
+
const found = new Set();
|
|
640
|
+
for (const t of targets) {
|
|
641
|
+
for (const cls of (index.references[t] ?? []))
|
|
642
|
+
found.add(cls);
|
|
643
|
+
}
|
|
644
|
+
if (found.size > 0)
|
|
645
|
+
detected[system] = [...found].sort();
|
|
646
|
+
}
|
|
647
|
+
if (Object.keys(detected).length === 0) {
|
|
648
|
+
return { mod: dbId, note: "No known config builder patterns found in this JAR.", configSystems: [] };
|
|
649
|
+
}
|
|
650
|
+
// Inspect each config class to surface field names as proxies for config keys
|
|
651
|
+
const schemas = [];
|
|
652
|
+
for (const [system, classes] of Object.entries(detected)) {
|
|
653
|
+
for (const cls of classes.slice(0, 10)) { // cap per system
|
|
654
|
+
try {
|
|
655
|
+
const info = await inspectClass(jarPath, cls);
|
|
656
|
+
// Heuristic: static final fields of type ConfigValue / BooleanValue / IntValue / String / etc.
|
|
657
|
+
const keyFields = info.fields.filter(f => f.descriptor.includes("ConfigValue") ||
|
|
658
|
+
f.descriptor.includes("BooleanValue") ||
|
|
659
|
+
f.descriptor.includes("IntValue") ||
|
|
660
|
+
f.descriptor.includes("DoubleValue") ||
|
|
661
|
+
f.descriptor.includes("LongValue") ||
|
|
662
|
+
f.descriptor.includes("EnumValue") ||
|
|
663
|
+
// static String fields often hold key names
|
|
664
|
+
(f.descriptor === "Ljava/lang/String;" && f.name === f.name.toUpperCase())).map(f => `${f.name}: ${f.descriptor}`);
|
|
665
|
+
schemas.push({ system, className: cls, candidateKeys: keyFields });
|
|
666
|
+
}
|
|
667
|
+
catch {
|
|
668
|
+
schemas.push({ system, className: cls, candidateKeys: [] });
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
return {
|
|
673
|
+
mod: dbId,
|
|
674
|
+
note: "Config keys inferred from builder class fields. Not all field names map 1:1 to config keys — use mod_bytecode class_members for full detail.",
|
|
675
|
+
configSystems: Object.keys(detected),
|
|
676
|
+
schemas,
|
|
677
|
+
};
|
|
678
|
+
}
|
|
679
|
+
//# sourceMappingURL=bytecode.js.map
|