@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.
Files changed (382) hide show
  1. package/README.md +938 -0
  2. package/dist/access-flags.d.ts +58 -0
  3. package/dist/access-flags.d.ts.map +1 -0
  4. package/dist/access-flags.js +79 -0
  5. package/dist/access-flags.js.map +1 -0
  6. package/dist/access-flags.test.d.ts +2 -0
  7. package/dist/access-flags.test.d.ts.map +1 -0
  8. package/dist/access-flags.test.js +189 -0
  9. package/dist/access-flags.test.js.map +1 -0
  10. package/dist/batch-ingest.d.ts +2 -0
  11. package/dist/batch-ingest.d.ts.map +1 -0
  12. package/dist/batch-ingest.js +54 -0
  13. package/dist/batch-ingest.js.map +1 -0
  14. package/dist/batch-resolve-mixins.d.ts +2 -0
  15. package/dist/batch-resolve-mixins.d.ts.map +1 -0
  16. package/dist/batch-resolve-mixins.js +36 -0
  17. package/dist/batch-resolve-mixins.js.map +1 -0
  18. package/dist/cache.d.ts +16 -0
  19. package/dist/cache.d.ts.map +1 -0
  20. package/dist/cache.js +36 -0
  21. package/dist/cache.js.map +1 -0
  22. package/dist/cli.d.ts +2 -0
  23. package/dist/cli.d.ts.map +1 -0
  24. package/dist/cli.js +1318 -0
  25. package/dist/cli.js.map +1 -0
  26. package/dist/curseforge.d.ts +53 -0
  27. package/dist/curseforge.d.ts.map +1 -0
  28. package/dist/curseforge.js +104 -0
  29. package/dist/curseforge.js.map +1 -0
  30. package/dist/db-backend.d.ts +17 -0
  31. package/dist/db-backend.d.ts.map +1 -0
  32. package/dist/db-backend.js +40 -0
  33. package/dist/db-backend.js.map +1 -0
  34. package/dist/db.d.ts +11 -0
  35. package/dist/db.d.ts.map +1 -0
  36. package/dist/db.js +55 -0
  37. package/dist/db.js.map +1 -0
  38. package/dist/embed-queue.d.ts +12 -0
  39. package/dist/embed-queue.d.ts.map +1 -0
  40. package/dist/embed-queue.js +103 -0
  41. package/dist/embed-queue.js.map +1 -0
  42. package/dist/embeddings.d.ts +21 -0
  43. package/dist/embeddings.d.ts.map +1 -0
  44. package/dist/embeddings.js +55 -0
  45. package/dist/embeddings.js.map +1 -0
  46. package/dist/env-path.d.ts +19 -0
  47. package/dist/env-path.d.ts.map +1 -0
  48. package/dist/env-path.js +40 -0
  49. package/dist/env-path.js.map +1 -0
  50. package/dist/fetch-utils.d.ts +24 -0
  51. package/dist/fetch-utils.d.ts.map +1 -0
  52. package/dist/fetch-utils.js +73 -0
  53. package/dist/fetch-utils.js.map +1 -0
  54. package/dist/fetch-utils.test.d.ts +2 -0
  55. package/dist/fetch-utils.test.d.ts.map +1 -0
  56. package/dist/fetch-utils.test.js +133 -0
  57. package/dist/fetch-utils.test.js.map +1 -0
  58. package/dist/generated/sqlite/browser.d.ts +55 -0
  59. package/dist/generated/sqlite/browser.d.ts.map +1 -0
  60. package/dist/generated/sqlite/browser.js +18 -0
  61. package/dist/generated/sqlite/browser.js.map +1 -0
  62. package/dist/generated/sqlite/client.d.ts +72 -0
  63. package/dist/generated/sqlite/client.d.ts.map +1 -0
  64. package/dist/generated/sqlite/client.js +33 -0
  65. package/dist/generated/sqlite/client.js.map +1 -0
  66. package/dist/generated/sqlite/commonInputTypes.d.ts +387 -0
  67. package/dist/generated/sqlite/commonInputTypes.d.ts.map +1 -0
  68. package/dist/generated/sqlite/commonInputTypes.js +11 -0
  69. package/dist/generated/sqlite/commonInputTypes.js.map +1 -0
  70. package/dist/generated/sqlite/enums.d.ts +2 -0
  71. package/dist/generated/sqlite/enums.d.ts.map +1 -0
  72. package/dist/generated/sqlite/enums.js +11 -0
  73. package/dist/generated/sqlite/enums.js.map +1 -0
  74. package/dist/generated/sqlite/internal/class.d.ts +226 -0
  75. package/dist/generated/sqlite/internal/class.d.ts.map +1 -0
  76. package/dist/generated/sqlite/internal/class.js +84 -0
  77. package/dist/generated/sqlite/internal/class.js.map +1 -0
  78. package/dist/generated/sqlite/internal/prismaNamespace.d.ts +1344 -0
  79. package/dist/generated/sqlite/internal/prismaNamespace.d.ts.map +1 -0
  80. package/dist/generated/sqlite/internal/prismaNamespace.js +220 -0
  81. package/dist/generated/sqlite/internal/prismaNamespace.js.map +1 -0
  82. package/dist/generated/sqlite/internal/prismaNamespaceBrowser.d.ts +204 -0
  83. package/dist/generated/sqlite/internal/prismaNamespaceBrowser.d.ts.map +1 -0
  84. package/dist/generated/sqlite/internal/prismaNamespaceBrowser.js +191 -0
  85. package/dist/generated/sqlite/internal/prismaNamespaceBrowser.js.map +1 -0
  86. package/dist/generated/sqlite/models/DocEntry.d.ts +1223 -0
  87. package/dist/generated/sqlite/models/DocEntry.d.ts.map +1 -0
  88. package/dist/generated/sqlite/models/DocEntry.js +2 -0
  89. package/dist/generated/sqlite/models/DocEntry.js.map +1 -0
  90. package/dist/generated/sqlite/models/McSourceFile.d.ts +1289 -0
  91. package/dist/generated/sqlite/models/McSourceFile.d.ts.map +1 -0
  92. package/dist/generated/sqlite/models/McSourceFile.js +2 -0
  93. package/dist/generated/sqlite/models/McSourceFile.js.map +1 -0
  94. package/dist/generated/sqlite/models/McVersion.d.ts +1315 -0
  95. package/dist/generated/sqlite/models/McVersion.d.ts.map +1 -0
  96. package/dist/generated/sqlite/models/McVersion.js +2 -0
  97. package/dist/generated/sqlite/models/McVersion.js.map +1 -0
  98. package/dist/generated/sqlite/models/McVersionDiff.d.ts +1061 -0
  99. package/dist/generated/sqlite/models/McVersionDiff.d.ts.map +1 -0
  100. package/dist/generated/sqlite/models/McVersionDiff.js +2 -0
  101. package/dist/generated/sqlite/models/McVersionDiff.js.map +1 -0
  102. package/dist/generated/sqlite/models/Mod.d.ts +2396 -0
  103. package/dist/generated/sqlite/models/Mod.d.ts.map +1 -0
  104. package/dist/generated/sqlite/models/Mod.js +2 -0
  105. package/dist/generated/sqlite/models/Mod.js.map +1 -0
  106. package/dist/generated/sqlite/models/ModClass.d.ts +1254 -0
  107. package/dist/generated/sqlite/models/ModClass.d.ts.map +1 -0
  108. package/dist/generated/sqlite/models/ModClass.js +2 -0
  109. package/dist/generated/sqlite/models/ModClass.js.map +1 -0
  110. package/dist/generated/sqlite/models/ModSourceFile.d.ts +1283 -0
  111. package/dist/generated/sqlite/models/ModSourceFile.d.ts.map +1 -0
  112. package/dist/generated/sqlite/models/ModSourceFile.js +2 -0
  113. package/dist/generated/sqlite/models/ModSourceFile.js.map +1 -0
  114. package/dist/generated/sqlite/models/ModTag.d.ts +1278 -0
  115. package/dist/generated/sqlite/models/ModTag.d.ts.map +1 -0
  116. package/dist/generated/sqlite/models/ModTag.js +2 -0
  117. package/dist/generated/sqlite/models/ModTag.js.map +1 -0
  118. package/dist/generated/sqlite/models/ModVersionDiff.d.ts +1073 -0
  119. package/dist/generated/sqlite/models/ModVersionDiff.d.ts.map +1 -0
  120. package/dist/generated/sqlite/models/ModVersionDiff.js +2 -0
  121. package/dist/generated/sqlite/models/ModVersionDiff.js.map +1 -0
  122. package/dist/generated/sqlite/models/Primer.d.ts +1319 -0
  123. package/dist/generated/sqlite/models/Primer.d.ts.map +1 -0
  124. package/dist/generated/sqlite/models/Primer.js +2 -0
  125. package/dist/generated/sqlite/models/Primer.js.map +1 -0
  126. package/dist/generated/sqlite/models.d.ts +12 -0
  127. package/dist/generated/sqlite/models.d.ts.map +1 -0
  128. package/dist/generated/sqlite/models.js +2 -0
  129. package/dist/generated/sqlite/models.js.map +1 -0
  130. package/dist/jar.d.ts +7 -0
  131. package/dist/jar.d.ts.map +1 -0
  132. package/dist/jar.js +52 -0
  133. package/dist/jar.js.map +1 -0
  134. package/dist/java-tools.d.ts +46 -0
  135. package/dist/java-tools.d.ts.map +1 -0
  136. package/dist/java-tools.js +263 -0
  137. package/dist/java-tools.js.map +1 -0
  138. package/dist/launcher.d.ts +3 -0
  139. package/dist/launcher.d.ts.map +1 -0
  140. package/dist/launcher.js +105 -0
  141. package/dist/launcher.js.map +1 -0
  142. package/dist/mappings.d.ts +95 -0
  143. package/dist/mappings.d.ts.map +1 -0
  144. package/dist/mappings.js +1094 -0
  145. package/dist/mappings.js.map +1 -0
  146. package/dist/mappings.test.d.ts +2 -0
  147. package/dist/mappings.test.d.ts.map +1 -0
  148. package/dist/mappings.test.js +161 -0
  149. package/dist/mappings.test.js.map +1 -0
  150. package/dist/minecraft.d.ts +21 -0
  151. package/dist/minecraft.d.ts.map +1 -0
  152. package/dist/minecraft.js +70 -0
  153. package/dist/minecraft.js.map +1 -0
  154. package/dist/modpacks-ch.d.ts +224 -0
  155. package/dist/modpacks-ch.d.ts.map +1 -0
  156. package/dist/modpacks-ch.js +146 -0
  157. package/dist/modpacks-ch.js.map +1 -0
  158. package/dist/modrinth.d.ts +67 -0
  159. package/dist/modrinth.d.ts.map +1 -0
  160. package/dist/modrinth.js +88 -0
  161. package/dist/modrinth.js.map +1 -0
  162. package/dist/platform-adapter.d.ts +23 -0
  163. package/dist/platform-adapter.d.ts.map +1 -0
  164. package/dist/platform-adapter.js +2 -0
  165. package/dist/platform-adapter.js.map +1 -0
  166. package/dist/platform.d.ts +47 -0
  167. package/dist/platform.d.ts.map +1 -0
  168. package/dist/platform.js +175 -0
  169. package/dist/platform.js.map +1 -0
  170. package/dist/processor.d.ts +51 -0
  171. package/dist/processor.d.ts.map +1 -0
  172. package/dist/processor.js +655 -0
  173. package/dist/processor.js.map +1 -0
  174. package/dist/processor.test.d.ts +2 -0
  175. package/dist/processor.test.d.ts.map +1 -0
  176. package/dist/processor.test.js +629 -0
  177. package/dist/processor.test.js.map +1 -0
  178. package/dist/repositories/embeddings-sqlite.d.ts +38 -0
  179. package/dist/repositories/embeddings-sqlite.d.ts.map +1 -0
  180. package/dist/repositories/embeddings-sqlite.js +208 -0
  181. package/dist/repositories/embeddings-sqlite.js.map +1 -0
  182. package/dist/repositories/embeddings.d.ts +40 -0
  183. package/dist/repositories/embeddings.d.ts.map +1 -0
  184. package/dist/repositories/embeddings.js +136 -0
  185. package/dist/repositories/embeddings.js.map +1 -0
  186. package/dist/repositories/index.d.ts +2 -0
  187. package/dist/repositories/index.d.ts.map +1 -0
  188. package/dist/repositories/index.js +15 -0
  189. package/dist/repositories/index.js.map +1 -0
  190. package/dist/repositories/mcVersion.d.ts +16 -0
  191. package/dist/repositories/mcVersion.d.ts.map +1 -0
  192. package/dist/repositories/mcVersion.js +52 -0
  193. package/dist/repositories/mcVersion.js.map +1 -0
  194. package/dist/repositories/mod.d.ts +241 -0
  195. package/dist/repositories/mod.d.ts.map +1 -0
  196. package/dist/repositories/mod.js +342 -0
  197. package/dist/repositories/mod.js.map +1 -0
  198. package/dist/repositories/packs.d.ts +44 -0
  199. package/dist/repositories/packs.d.ts.map +1 -0
  200. package/dist/repositories/packs.js +138 -0
  201. package/dist/repositories/packs.js.map +1 -0
  202. package/dist/search-adapter.d.ts +34 -0
  203. package/dist/search-adapter.d.ts.map +1 -0
  204. package/dist/search-adapter.js +195 -0
  205. package/dist/search-adapter.js.map +1 -0
  206. package/dist/search.d.ts +6 -0
  207. package/dist/search.d.ts.map +1 -0
  208. package/dist/search.js +54 -0
  209. package/dist/search.js.map +1 -0
  210. package/dist/search.test.d.ts +2 -0
  211. package/dist/search.test.d.ts.map +1 -0
  212. package/dist/search.test.js +71 -0
  213. package/dist/search.test.js.map +1 -0
  214. package/dist/security.d.ts +98 -0
  215. package/dist/security.d.ts.map +1 -0
  216. package/dist/security.js +446 -0
  217. package/dist/security.js.map +1 -0
  218. package/dist/security.test.d.ts +2 -0
  219. package/dist/security.test.d.ts.map +1 -0
  220. package/dist/security.test.js +687 -0
  221. package/dist/security.test.js.map +1 -0
  222. package/dist/server.d.ts +2 -0
  223. package/dist/server.d.ts.map +1 -0
  224. package/dist/server.js +1465 -0
  225. package/dist/server.js.map +1 -0
  226. package/dist/setup.d.ts +2 -0
  227. package/dist/setup.d.ts.map +1 -0
  228. package/dist/setup.js +1113 -0
  229. package/dist/setup.js.map +1 -0
  230. package/dist/tools/bytecode.d.ts +129 -0
  231. package/dist/tools/bytecode.d.ts.map +1 -0
  232. package/dist/tools/bytecode.js +679 -0
  233. package/dist/tools/bytecode.js.map +1 -0
  234. package/dist/tools/catalog.d.ts +54 -0
  235. package/dist/tools/catalog.d.ts.map +1 -0
  236. package/dist/tools/catalog.js +249 -0
  237. package/dist/tools/catalog.js.map +1 -0
  238. package/dist/tools/compat-check.d.ts +23 -0
  239. package/dist/tools/compat-check.d.ts.map +1 -0
  240. package/dist/tools/compat-check.js +250 -0
  241. package/dist/tools/compat-check.js.map +1 -0
  242. package/dist/tools/compat-check.test.d.ts +2 -0
  243. package/dist/tools/compat-check.test.d.ts.map +1 -0
  244. package/dist/tools/compat-check.test.js +179 -0
  245. package/dist/tools/compat-check.test.js.map +1 -0
  246. package/dist/tools/diagnostics.d.ts +10 -0
  247. package/dist/tools/diagnostics.d.ts.map +1 -0
  248. package/dist/tools/diagnostics.js +133 -0
  249. package/dist/tools/diagnostics.js.map +1 -0
  250. package/dist/tools/diagnostics.test.d.ts +2 -0
  251. package/dist/tools/diagnostics.test.d.ts.map +1 -0
  252. package/dist/tools/diagnostics.test.js +168 -0
  253. package/dist/tools/diagnostics.test.js.map +1 -0
  254. package/dist/tools/docs.d.ts +19 -0
  255. package/dist/tools/docs.d.ts.map +1 -0
  256. package/dist/tools/docs.js +230 -0
  257. package/dist/tools/docs.js.map +1 -0
  258. package/dist/tools/embed-registry.d.ts +125 -0
  259. package/dist/tools/embed-registry.d.ts.map +1 -0
  260. package/dist/tools/embed-registry.js +560 -0
  261. package/dist/tools/embed-registry.js.map +1 -0
  262. package/dist/tools/gradle.d.ts +16 -0
  263. package/dist/tools/gradle.d.ts.map +1 -0
  264. package/dist/tools/gradle.js +182 -0
  265. package/dist/tools/gradle.js.map +1 -0
  266. package/dist/tools/graphify.d.ts +144 -0
  267. package/dist/tools/graphify.d.ts.map +1 -0
  268. package/dist/tools/graphify.js +798 -0
  269. package/dist/tools/graphify.js.map +1 -0
  270. package/dist/tools/graphify.test.d.ts +2 -0
  271. package/dist/tools/graphify.test.d.ts.map +1 -0
  272. package/dist/tools/graphify.test.js +39 -0
  273. package/dist/tools/graphify.test.js.map +1 -0
  274. package/dist/tools/ingest.d.ts +85 -0
  275. package/dist/tools/ingest.d.ts.map +1 -0
  276. package/dist/tools/ingest.js +477 -0
  277. package/dist/tools/ingest.js.map +1 -0
  278. package/dist/tools/ingest.test.d.ts +2 -0
  279. package/dist/tools/ingest.test.d.ts.map +1 -0
  280. package/dist/tools/ingest.test.js +340 -0
  281. package/dist/tools/ingest.test.js.map +1 -0
  282. package/dist/tools/kubejs.d.ts +37 -0
  283. package/dist/tools/kubejs.d.ts.map +1 -0
  284. package/dist/tools/kubejs.js +245 -0
  285. package/dist/tools/kubejs.js.map +1 -0
  286. package/dist/tools/mappings.d.ts +17 -0
  287. package/dist/tools/mappings.d.ts.map +1 -0
  288. package/dist/tools/mappings.js +85 -0
  289. package/dist/tools/mappings.js.map +1 -0
  290. package/dist/tools/mc-fts.d.ts +101 -0
  291. package/dist/tools/mc-fts.d.ts.map +1 -0
  292. package/dist/tools/mc-fts.js +279 -0
  293. package/dist/tools/mc-fts.js.map +1 -0
  294. package/dist/tools/mcmeta.d.ts +41 -0
  295. package/dist/tools/mcmeta.d.ts.map +1 -0
  296. package/dist/tools/mcmeta.js +365 -0
  297. package/dist/tools/mcmeta.js.map +1 -0
  298. package/dist/tools/mixin-scan.d.ts +113 -0
  299. package/dist/tools/mixin-scan.d.ts.map +1 -0
  300. package/dist/tools/mixin-scan.js +433 -0
  301. package/dist/tools/mixin-scan.js.map +1 -0
  302. package/dist/tools/mod-data.d.ts +83 -0
  303. package/dist/tools/mod-data.d.ts.map +1 -0
  304. package/dist/tools/mod-data.js +630 -0
  305. package/dist/tools/mod-data.js.map +1 -0
  306. package/dist/tools/mod-tags.d.ts +59 -0
  307. package/dist/tools/mod-tags.d.ts.map +1 -0
  308. package/dist/tools/mod-tags.js +279 -0
  309. package/dist/tools/mod-tags.js.map +1 -0
  310. package/dist/tools/mod-tags.test.d.ts +2 -0
  311. package/dist/tools/mod-tags.test.d.ts.map +1 -0
  312. package/dist/tools/mod-tags.test.js +75 -0
  313. package/dist/tools/mod-tags.test.js.map +1 -0
  314. package/dist/tools/modpacks-ch.d.ts +257 -0
  315. package/dist/tools/modpacks-ch.d.ts.map +1 -0
  316. package/dist/tools/modpacks-ch.js +516 -0
  317. package/dist/tools/modpacks-ch.js.map +1 -0
  318. package/dist/tools/packtools.d.ts +113 -0
  319. package/dist/tools/packtools.d.ts.map +1 -0
  320. package/dist/tools/packtools.js +809 -0
  321. package/dist/tools/packtools.js.map +1 -0
  322. package/dist/tools/packtools.test.d.ts +2 -0
  323. package/dist/tools/packtools.test.d.ts.map +1 -0
  324. package/dist/tools/packtools.test.js +99 -0
  325. package/dist/tools/packtools.test.js.map +1 -0
  326. package/dist/tools/platform.d.ts +68 -0
  327. package/dist/tools/platform.d.ts.map +1 -0
  328. package/dist/tools/platform.js +452 -0
  329. package/dist/tools/platform.js.map +1 -0
  330. package/dist/tools/primers.d.ts +36 -0
  331. package/dist/tools/primers.d.ts.map +1 -0
  332. package/dist/tools/primers.js +451 -0
  333. package/dist/tools/primers.js.map +1 -0
  334. package/dist/tools/reports.d.ts +19 -0
  335. package/dist/tools/reports.d.ts.map +1 -0
  336. package/dist/tools/reports.js +573 -0
  337. package/dist/tools/reports.js.map +1 -0
  338. package/dist/tools/source.d.ts +10 -0
  339. package/dist/tools/source.d.ts.map +1 -0
  340. package/dist/tools/source.js +101 -0
  341. package/dist/tools/source.js.map +1 -0
  342. package/dist/tools/vanilla-data.d.ts +127 -0
  343. package/dist/tools/vanilla-data.d.ts.map +1 -0
  344. package/dist/tools/vanilla-data.js +824 -0
  345. package/dist/tools/vanilla-data.js.map +1 -0
  346. package/dist/tools/vanilla.d.ts +171 -0
  347. package/dist/tools/vanilla.d.ts.map +1 -0
  348. package/dist/tools/vanilla.js +612 -0
  349. package/dist/tools/vanilla.js.map +1 -0
  350. package/dist/tools/version-diff.d.ts +113 -0
  351. package/dist/tools/version-diff.d.ts.map +1 -0
  352. package/dist/tools/version-diff.js +519 -0
  353. package/dist/tools/version-diff.js.map +1 -0
  354. package/dist/tools/version-diff.test.d.ts +2 -0
  355. package/dist/tools/version-diff.test.d.ts.map +1 -0
  356. package/dist/tools/version-diff.test.js +102 -0
  357. package/dist/tools/version-diff.test.js.map +1 -0
  358. package/dist/validate.d.ts +19 -0
  359. package/dist/validate.d.ts.map +1 -0
  360. package/dist/validate.js +54 -0
  361. package/dist/validate.js.map +1 -0
  362. package/dist/validate.test.d.ts +2 -0
  363. package/dist/validate.test.d.ts.map +1 -0
  364. package/dist/validate.test.js +73 -0
  365. package/dist/validate.test.js.map +1 -0
  366. package/package.json +62 -0
  367. package/prisma/backends/schema.sqlite.prisma +215 -0
  368. package/prisma/backends/template.db +0 -0
  369. package/prisma/schema.prisma +303 -0
  370. package/scripts/backup.mjs +151 -0
  371. package/scripts/build-template-db.mjs +38 -0
  372. package/scripts/check-atm10.mjs +27 -0
  373. package/scripts/check-sync.mjs +13 -0
  374. package/scripts/create-fts-index.mjs +50 -0
  375. package/scripts/enable-pgvector.mjs +90 -0
  376. package/scripts/enable-sqlite-vec.mjs +158 -0
  377. package/scripts/migrate-backend.mjs +188 -0
  378. package/scripts/migrate-embed-provenance.mjs +80 -0
  379. package/scripts/migrate-metadata-source.mjs +62 -0
  380. package/scripts/migrate-mod-source-files.mjs +67 -0
  381. package/scripts/post-update.mjs +77 -0
  382. 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