mcmodding-mcp 0.3.0 → 0.4.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.
@@ -0,0 +1,788 @@
1
+ import Database from 'better-sqlite3';
2
+ import * as fs from 'fs';
3
+ import * as path from 'path';
4
+ const ABBREVIATION_MAP = {
5
+ msg: ['message'],
6
+ btn: ['button'],
7
+ inv: ['inventory'],
8
+ pos: ['position'],
9
+ vel: ['velocity'],
10
+ dir: ['direction'],
11
+ cfg: ['config', 'configuration'],
12
+ pkt: ['packet'],
13
+ tex: ['texture'],
14
+ gui: ['gui', 'screen', 'interface'],
15
+ nbt: ['nbt', 'tag', 'compound'],
16
+ mc: ['minecraft'],
17
+ bb: ['bounding', 'boundingbox'],
18
+ aabb: ['aabb', 'boundingbox'],
19
+ ai: ['ai', 'artificial'],
20
+ dmg: ['damage'],
21
+ hp: ['health'],
22
+ xp: ['experience'],
23
+ lvl: ['level'],
24
+ idx: ['index'],
25
+ len: ['length'],
26
+ cnt: ['count'],
27
+ num: ['number'],
28
+ str: ['string'],
29
+ obj: ['object'],
30
+ arr: ['array'],
31
+ vec: ['vector'],
32
+ mat: ['matrix', 'material'],
33
+ col: ['color', 'column', 'collision'],
34
+ rot: ['rotation'],
35
+ trans: ['translation', 'transform'],
36
+ ent: ['entity'],
37
+ blk: ['block'],
38
+ itm: ['item'],
39
+ ply: ['player'],
40
+ srv: ['server'],
41
+ cli: ['client'],
42
+ net: ['network'],
43
+ reg: ['register', 'registry'],
44
+ evt: ['event'],
45
+ cb: ['callback'],
46
+ fn: ['function'],
47
+ ctx: ['context'],
48
+ req: ['request'],
49
+ res: ['response', 'result'],
50
+ err: ['error'],
51
+ def: ['default', 'definition'],
52
+ init: ['initialize', 'initial'],
53
+ desc: ['descriptor', 'description'],
54
+ info: ['information'],
55
+ src: ['source'],
56
+ dst: ['destination'],
57
+ tmp: ['temporary'],
58
+ max: ['maximum'],
59
+ min: ['minimum'],
60
+ avg: ['average'],
61
+ rnd: ['random', 'render'],
62
+ gen: ['generate', 'generator'],
63
+ sync: ['synchronized', 'synchronize'],
64
+ async: ['asynchronous'],
65
+ };
66
+ const INTERNAL_PREFIXES = ['_', 'lambda$', 'access$', '$'];
67
+ function isInternalName(name) {
68
+ if (!name)
69
+ return false;
70
+ for (const prefix of INTERNAL_PREFIXES) {
71
+ if (name.startsWith(prefix))
72
+ return true;
73
+ }
74
+ if (name.includes('$') && !name.endsWith('$'))
75
+ return true;
76
+ return false;
77
+ }
78
+ function expandAbbreviations(tokens) {
79
+ const expanded = [];
80
+ for (const token of tokens) {
81
+ expanded.push(token);
82
+ const expansions = ABBREVIATION_MAP[token.toLowerCase()];
83
+ if (expansions) {
84
+ for (const exp of expansions) {
85
+ if (!expanded.includes(exp)) {
86
+ expanded.push(exp);
87
+ }
88
+ }
89
+ }
90
+ }
91
+ return expanded;
92
+ }
93
+ function tokenizeIdentifier(identifier) {
94
+ if (!identifier)
95
+ return [];
96
+ let normalized = identifier.replace(/[_-]/g, ' ');
97
+ normalized = normalized.replace(/([a-z])([A-Z])/g, '$1 $2');
98
+ normalized = normalized.replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2');
99
+ return normalized
100
+ .toLowerCase()
101
+ .split(/\s+/)
102
+ .filter((t) => t.length > 0);
103
+ }
104
+ function levenshteinDistance(a, b) {
105
+ if (a.length === 0)
106
+ return b.length;
107
+ if (b.length === 0)
108
+ return a.length;
109
+ const matrix = [];
110
+ for (let i = 0; i <= b.length; i++) {
111
+ matrix[i] = new Array(a.length + 1).fill(0);
112
+ }
113
+ for (let i = 0; i <= b.length; i++) {
114
+ matrix[i][0] = i;
115
+ }
116
+ for (let j = 0; j <= a.length; j++) {
117
+ matrix[0][j] = j;
118
+ }
119
+ for (let i = 1; i <= b.length; i++) {
120
+ for (let j = 1; j <= a.length; j++) {
121
+ if (b.charAt(i - 1) === a.charAt(j - 1)) {
122
+ matrix[i][j] = matrix[i - 1][j - 1];
123
+ }
124
+ else {
125
+ matrix[i][j] = Math.min(matrix[i - 1][j - 1] + 1, matrix[i][j - 1] + 1, matrix[i - 1][j] + 1);
126
+ }
127
+ }
128
+ }
129
+ return matrix[b.length][a.length];
130
+ }
131
+ function stringSimilarity(a, b) {
132
+ if (a === b)
133
+ return 1.0;
134
+ if (a.length === 0 || b.length === 0)
135
+ return 0.0;
136
+ const distance = levenshteinDistance(a.toLowerCase(), b.toLowerCase());
137
+ const maxLength = Math.max(a.length, b.length);
138
+ return 1 - distance / maxLength;
139
+ }
140
+ function containsCI(str, substr) {
141
+ return str.toLowerCase().includes(substr.toLowerCase());
142
+ }
143
+ function tokenizeQuery(query) {
144
+ const parts = query.toLowerCase().split(/[\s_-]+/);
145
+ const tokens = [];
146
+ for (const part of parts) {
147
+ tokens.push(...tokenizeIdentifier(part));
148
+ }
149
+ const uniqueTokens = [...new Set(tokens)].filter((t) => t.length >= 2);
150
+ return expandAbbreviations(uniqueTokens);
151
+ }
152
+ function calculateSemanticScore(identifier, queryTokens, originalQuery) {
153
+ if (!identifier || queryTokens.length === 0)
154
+ return 0;
155
+ const identifierLower = identifier.toLowerCase();
156
+ const originalLower = originalQuery.toLowerCase().replace(/[\s_-]+/g, '');
157
+ const internalPenalty = isInternalName(identifier) ? 25 : 0;
158
+ if (identifierLower === originalLower) {
159
+ return 100 - internalPenalty;
160
+ }
161
+ if (identifierLower.startsWith(originalLower)) {
162
+ const lengthBonus = Math.max(0, 10 - (identifier.length - originalQuery.length));
163
+ return Math.min(95, 85 + lengthBonus) - internalPenalty;
164
+ }
165
+ const wholeStringSimilarity = stringSimilarity(originalLower, identifierLower);
166
+ if (wholeStringSimilarity >= 0.75) {
167
+ const fuzzyScore = 50 + wholeStringSimilarity * 40;
168
+ return Math.min(Math.round(fuzzyScore), 90) - internalPenalty;
169
+ }
170
+ const identifierTokens = tokenizeIdentifier(identifier);
171
+ const baseQueryTokens = originalQuery
172
+ .toLowerCase()
173
+ .split(/[\s_-]+/)
174
+ .flatMap((p) => tokenizeIdentifier(p))
175
+ .filter((t) => t.length >= 2);
176
+ const baseTokenSet = new Set(baseQueryTokens);
177
+ let exactMatches = 0;
178
+ let prefixMatches = 0;
179
+ let containsMatches = 0;
180
+ let fuzzyMatches = 0;
181
+ let totalMatchScore = 0;
182
+ for (const queryToken of queryTokens) {
183
+ let bestMatch = 0;
184
+ const isBaseToken = baseTokenSet.has(queryToken);
185
+ const tokenWeight = isBaseToken ? 1.0 : 0.6;
186
+ for (const idToken of identifierTokens) {
187
+ if (idToken === queryToken) {
188
+ bestMatch = Math.max(bestMatch, 1.0);
189
+ break;
190
+ }
191
+ if (idToken.startsWith(queryToken)) {
192
+ const prefixScore = 0.9 - (idToken.length - queryToken.length) * 0.02;
193
+ bestMatch = Math.max(bestMatch, Math.max(0.75, prefixScore));
194
+ continue;
195
+ }
196
+ if (queryToken.startsWith(idToken) && idToken.length >= 3) {
197
+ const prefixScore = 0.8 - (queryToken.length - idToken.length) * 0.03;
198
+ bestMatch = Math.max(bestMatch, Math.max(0.65, prefixScore));
199
+ continue;
200
+ }
201
+ if (queryToken.length >= 3 && idToken.includes(queryToken)) {
202
+ bestMatch = Math.max(bestMatch, 0.7);
203
+ continue;
204
+ }
205
+ if (idToken.length >= 3 && queryToken.includes(idToken)) {
206
+ bestMatch = Math.max(bestMatch, 0.6);
207
+ continue;
208
+ }
209
+ const minLen = Math.min(idToken.length, queryToken.length);
210
+ const fuzzyThreshold = minLen >= 6 ? 0.65 : minLen >= 4 ? 0.7 : 0.75;
211
+ const similarity = stringSimilarity(idToken, queryToken);
212
+ if (similarity >= fuzzyThreshold) {
213
+ const fuzzyScore = similarity * 0.7;
214
+ bestMatch = Math.max(bestMatch, fuzzyScore);
215
+ }
216
+ }
217
+ if (bestMatch >= 0.95) {
218
+ exactMatches++;
219
+ }
220
+ else if (bestMatch >= 0.75) {
221
+ prefixMatches++;
222
+ }
223
+ else if (bestMatch >= 0.55) {
224
+ containsMatches++;
225
+ }
226
+ else if (bestMatch > 0.3) {
227
+ fuzzyMatches++;
228
+ }
229
+ totalMatchScore += bestMatch * tokenWeight;
230
+ }
231
+ let score = 0;
232
+ const totalBaseTokens = baseQueryTokens.length || 1;
233
+ const matchedBase = exactMatches + prefixMatches >= totalBaseTokens * 0.8 ? totalBaseTokens : exactMatches;
234
+ if (matchedBase === totalBaseTokens) {
235
+ score = 75 + (totalMatchScore / queryTokens.length) * 15;
236
+ const joinedQuery = baseQueryTokens.join('');
237
+ if (identifierLower.includes(joinedQuery)) {
238
+ score += 5;
239
+ }
240
+ if (identifierTokens[0] === baseQueryTokens[0]) {
241
+ score += 10;
242
+ }
243
+ }
244
+ else if (exactMatches + prefixMatches >= totalBaseTokens * 0.7) {
245
+ score = 55 + (totalMatchScore / queryTokens.length) * 20;
246
+ }
247
+ else if (exactMatches + prefixMatches + containsMatches >= totalBaseTokens * 0.5) {
248
+ score = 40 + (totalMatchScore / queryTokens.length) * 20;
249
+ }
250
+ else if (fuzzyMatches > 0 || totalMatchScore > 0.3) {
251
+ score = 20 + (totalMatchScore / queryTokens.length) * 30;
252
+ }
253
+ if (identifier.length < 15) {
254
+ score += 5;
255
+ }
256
+ else if (identifier.length > 40) {
257
+ score -= 5;
258
+ }
259
+ if (containsCI(identifier, originalLower) && originalLower.length >= 3) {
260
+ const containsScore = 55 + (originalLower.length / identifier.length) * 20;
261
+ score = Math.max(score, containsScore);
262
+ }
263
+ score -= internalPenalty;
264
+ return Math.min(Math.max(Math.round(score), 0), 100);
265
+ }
266
+ export class MappingsService {
267
+ db;
268
+ static dbPath = path.join(process.cwd(), 'data', 'parchment-mappings.db');
269
+ constructor(dbPath) {
270
+ const finalPath = dbPath || MappingsService.dbPath;
271
+ console.error(`[MappingsService] Using database at: ${finalPath}`);
272
+ this.db = new Database(finalPath, { readonly: true });
273
+ }
274
+ static isAvailable() {
275
+ return fs.existsSync(MappingsService.dbPath);
276
+ }
277
+ static getDbPath() {
278
+ return MappingsService.dbPath;
279
+ }
280
+ getStats() {
281
+ const counts = this.db
282
+ .prepare(`
283
+ SELECT
284
+ (SELECT COUNT(*) FROM classes) as totalClasses,
285
+ (SELECT COUNT(*) FROM methods) as totalMethods,
286
+ (SELECT COUNT(*) FROM fields) as totalFields,
287
+ (SELECT COUNT(*) FROM parameters) as totalParameters,
288
+ (SELECT COUNT(*) FROM methods WHERE javadoc IS NOT NULL AND javadoc != '') as documentedMethods,
289
+ (SELECT COUNT(*) FROM fields WHERE javadoc IS NOT NULL AND javadoc != '') as documentedFields
290
+ `)
291
+ .get();
292
+ const versions = this.db
293
+ .prepare(`SELECT DISTINCT minecraft_version FROM classes ORDER BY minecraft_version DESC`)
294
+ .all();
295
+ const topPackages = this.db
296
+ .prepare(`
297
+ SELECT package_name as packageName, COUNT(*) as count
298
+ FROM classes
299
+ GROUP BY package_name
300
+ ORDER BY count DESC
301
+ LIMIT 10
302
+ `)
303
+ .all();
304
+ return {
305
+ ...counts,
306
+ minecraftVersions: versions.map((v) => v.minecraft_version),
307
+ topPackages,
308
+ };
309
+ }
310
+ getMinecraftVersions() {
311
+ const versions = this.db
312
+ .prepare(`SELECT DISTINCT minecraft_version FROM classes ORDER BY minecraft_version DESC`)
313
+ .all();
314
+ return versions.map((v) => v.minecraft_version);
315
+ }
316
+ getLatestVersion() {
317
+ const result = this.db
318
+ .prepare(`
319
+ SELECT minecraft_version FROM classes
320
+ ORDER BY minecraft_version DESC
321
+ LIMIT 1
322
+ `)
323
+ .get();
324
+ return result?.minecraft_version || null;
325
+ }
326
+ search(options) {
327
+ const { query, type = 'all', minecraftVersion, packageFilter, includeJavadoc = true, limit = 20, } = options;
328
+ const results = [];
329
+ const queryTokens = tokenizeQuery(query);
330
+ console.error(`[MappingsService] Search query: "${query}" → tokens: [${queryTokens.join(', ')}]`);
331
+ const version = minecraftVersion || this.getLatestVersion();
332
+ if (!version) {
333
+ return [];
334
+ }
335
+ const sqlPatterns = this.generateSqlPatterns(query, queryTokens);
336
+ if (type === 'all' || type === 'class') {
337
+ const classResults = this.searchClassesSemantic(sqlPatterns, queryTokens, query, version, packageFilter, limit * 3);
338
+ for (const cls of classResults) {
339
+ results.push({
340
+ type: 'class',
341
+ name: cls.name,
342
+ fullName: `${cls.packageName}.${cls.name}`,
343
+ obfuscatedName: cls.obfuscatedName,
344
+ descriptor: null,
345
+ javadoc: includeJavadoc ? cls.javadoc : null,
346
+ className: null,
347
+ packageName: cls.packageName,
348
+ minecraftVersion: cls.minecraftVersion,
349
+ score: cls.score,
350
+ });
351
+ }
352
+ }
353
+ if (type === 'all' || type === 'method') {
354
+ const methodResults = this.searchMethodsSemantic(sqlPatterns, queryTokens, query, version, packageFilter, limit * 3);
355
+ for (const method of methodResults) {
356
+ results.push({
357
+ type: 'method',
358
+ name: method.name,
359
+ fullName: `${method.className}.${method.name}`,
360
+ obfuscatedName: method.obfuscatedName,
361
+ descriptor: method.descriptor,
362
+ javadoc: includeJavadoc ? method.javadoc : null,
363
+ className: method.className,
364
+ packageName: method.packageName,
365
+ minecraftVersion: method.minecraftVersion,
366
+ parameters: method.parameters,
367
+ score: method.score,
368
+ });
369
+ }
370
+ }
371
+ if (type === 'all' || type === 'field') {
372
+ const fieldResults = this.searchFieldsSemantic(sqlPatterns, queryTokens, query, version, packageFilter, limit * 3);
373
+ for (const field of fieldResults) {
374
+ results.push({
375
+ type: 'field',
376
+ name: field.name,
377
+ fullName: `${field.className}.${field.name}`,
378
+ obfuscatedName: field.obfuscatedName,
379
+ descriptor: field.descriptor,
380
+ javadoc: includeJavadoc ? field.javadoc : null,
381
+ className: field.className,
382
+ packageName: field.packageName,
383
+ minecraftVersion: field.minecraftVersion,
384
+ score: field.score,
385
+ });
386
+ }
387
+ }
388
+ results.sort((a, b) => b.score - a.score);
389
+ return results.slice(0, limit);
390
+ }
391
+ generateSqlPatterns(query, tokens) {
392
+ const patterns = [];
393
+ const queryLower = query.toLowerCase();
394
+ patterns.push(`%${query}%`);
395
+ const joinedQuery = query.replace(/\s+/g, '');
396
+ patterns.push(`%${joinedQuery}%`);
397
+ for (const token of tokens) {
398
+ if (token.length >= 2) {
399
+ patterns.push(`%${token}%`);
400
+ if (token.length >= 4) {
401
+ for (let i = 0; i < token.length; i++) {
402
+ const variant = token.slice(0, i) + token.slice(i + 1);
403
+ if (variant.length >= 3) {
404
+ patterns.push(`%${variant}%`);
405
+ }
406
+ }
407
+ for (let i = 1; i < token.length; i++) {
408
+ const variant = token.slice(0, i) + '%' + token.slice(i);
409
+ patterns.push(`%${variant}%`);
410
+ }
411
+ }
412
+ if (token.length >= 3) {
413
+ patterns.push(`${token}%`);
414
+ }
415
+ }
416
+ }
417
+ for (let i = 0; i < tokens.length - 1; i++) {
418
+ const pair = `%${tokens[i]}%${tokens[i + 1]}%`;
419
+ patterns.push(pair);
420
+ patterns.push(`%${tokens[i]}${tokens[i + 1]}%`);
421
+ }
422
+ if (queryLower.length <= 4 && tokens.length === 1) {
423
+ const common = ['er', 'ing', 'ed', 'tion', 'ment', 'able', 'ible'];
424
+ for (const suffix of common) {
425
+ patterns.push(`%${tokens[0]}${suffix}%`);
426
+ }
427
+ }
428
+ return [...new Set(patterns)];
429
+ }
430
+ searchClassesSemantic(patterns, queryTokens, originalQuery, version, packageFilter, limit = 60) {
431
+ const patternConditions = patterns.map(() => 'name LIKE ?').join(' OR ');
432
+ const retrievalLimit = Math.max(limit * 3, 200);
433
+ let sql = `
434
+ SELECT id, name, obfuscated_name as obfuscatedName, javadoc, package_name as packageName, minecraft_version as minecraftVersion
435
+ FROM classes
436
+ WHERE minecraft_version = ?
437
+ AND (${patternConditions})
438
+ `;
439
+ const params = [version, ...patterns];
440
+ if (packageFilter) {
441
+ sql += ` AND package_name LIKE ?`;
442
+ params.push(`%${packageFilter}%`);
443
+ }
444
+ sql += ` LIMIT ?`;
445
+ params.push(retrievalLimit);
446
+ const rawResults = this.db.prepare(sql).all(...params);
447
+ return rawResults
448
+ .map((cls) => ({
449
+ ...cls,
450
+ score: calculateSemanticScore(cls.name, queryTokens, originalQuery),
451
+ }))
452
+ .filter((cls) => cls.score > 10)
453
+ .sort((a, b) => b.score - a.score)
454
+ .slice(0, limit);
455
+ }
456
+ searchMethodsSemantic(patterns, queryTokens, originalQuery, version, packageFilter, limit = 60) {
457
+ const patternConditions = patterns.map(() => 'm.name LIKE ?').join(' OR ');
458
+ const retrievalLimit = Math.max(limit * 3, 200);
459
+ let sql = `
460
+ SELECT
461
+ m.id,
462
+ m.name,
463
+ m.obfuscated_name as obfuscatedName,
464
+ m.descriptor,
465
+ m.javadoc,
466
+ c.name as className,
467
+ c.package_name as packageName,
468
+ c.minecraft_version as minecraftVersion
469
+ FROM methods m
470
+ JOIN classes c ON m.class_id = c.id
471
+ WHERE c.minecraft_version = ?
472
+ AND (${patternConditions})
473
+ `;
474
+ const params = [version, ...patterns];
475
+ if (packageFilter) {
476
+ sql += ` AND c.package_name LIKE ?`;
477
+ params.push(`%${packageFilter}%`);
478
+ }
479
+ sql += ` LIMIT ?`;
480
+ params.push(retrievalLimit);
481
+ const rawResults = this.db.prepare(sql).all(...params);
482
+ const paramStmt = this.db.prepare(`
483
+ SELECT id, method_id as methodId, param_index as "index", name, javadoc
484
+ FROM parameters
485
+ WHERE method_id = ?
486
+ ORDER BY param_index
487
+ `);
488
+ return rawResults
489
+ .map((method) => ({
490
+ ...method,
491
+ parameters: paramStmt.all(method.id),
492
+ score: calculateSemanticScore(method.name, queryTokens, originalQuery),
493
+ }))
494
+ .filter((method) => method.score > 10)
495
+ .sort((a, b) => b.score - a.score)
496
+ .slice(0, limit);
497
+ }
498
+ searchFieldsSemantic(patterns, queryTokens, originalQuery, version, packageFilter, limit = 60) {
499
+ const patternConditions = patterns.map(() => 'f.name LIKE ?').join(' OR ');
500
+ const retrievalLimit = Math.max(limit * 3, 200);
501
+ let sql = `
502
+ SELECT
503
+ f.id,
504
+ f.name,
505
+ f.obfuscated_name as obfuscatedName,
506
+ f.descriptor,
507
+ f.javadoc,
508
+ c.name as className,
509
+ c.package_name as packageName,
510
+ c.minecraft_version as minecraftVersion
511
+ FROM fields f
512
+ JOIN classes c ON f.class_id = c.id
513
+ WHERE c.minecraft_version = ?
514
+ AND (${patternConditions})
515
+ `;
516
+ const params = [version, ...patterns];
517
+ if (packageFilter) {
518
+ sql += ` AND c.package_name LIKE ?`;
519
+ params.push(`%${packageFilter}%`);
520
+ }
521
+ sql += ` LIMIT ?`;
522
+ params.push(retrievalLimit);
523
+ const rawResults = this.db.prepare(sql).all(...params);
524
+ return rawResults
525
+ .map((field) => ({
526
+ ...field,
527
+ score: calculateSemanticScore(field.name, queryTokens, originalQuery),
528
+ }))
529
+ .filter((field) => field.score > 10)
530
+ .sort((a, b) => b.score - a.score)
531
+ .slice(0, limit);
532
+ }
533
+ getClass(fullName, minecraftVersion) {
534
+ const version = minecraftVersion || this.getLatestVersion();
535
+ if (!version)
536
+ return null;
537
+ const parts = fullName.split('.');
538
+ const className = parts.pop() || fullName;
539
+ const packageName = parts.join('.');
540
+ let sql = `
541
+ SELECT
542
+ c.id,
543
+ c.name,
544
+ c.obfuscated_name as obfuscatedName,
545
+ c.javadoc,
546
+ c.package_name as packageName,
547
+ c.minecraft_version as minecraftVersion,
548
+ (SELECT COUNT(*) FROM methods WHERE class_id = c.id) as methodCount,
549
+ (SELECT COUNT(*) FROM fields WHERE class_id = c.id) as fieldCount
550
+ FROM classes c
551
+ WHERE c.minecraft_version = ?
552
+ AND c.name = ?
553
+ `;
554
+ const params = [version, className];
555
+ if (packageName) {
556
+ sql += ` AND c.package_name = ?`;
557
+ params.push(packageName);
558
+ }
559
+ return this.db.prepare(sql).get(...params);
560
+ }
561
+ getClassMethods(classId) {
562
+ const methods = this.db
563
+ .prepare(`
564
+ SELECT
565
+ m.id,
566
+ m.class_id as classId,
567
+ c.name as className,
568
+ m.name,
569
+ m.obfuscated_name as obfuscatedName,
570
+ m.descriptor,
571
+ m.javadoc,
572
+ c.minecraft_version as minecraftVersion
573
+ FROM methods m
574
+ JOIN classes c ON m.class_id = c.id
575
+ WHERE m.class_id = ?
576
+ ORDER BY m.name
577
+ `)
578
+ .all(classId);
579
+ const paramStmt = this.db.prepare(`
580
+ SELECT id, method_id as methodId, param_index as "index", name, javadoc
581
+ FROM parameters
582
+ WHERE method_id = ?
583
+ ORDER BY param_index
584
+ `);
585
+ return methods.map((method) => ({
586
+ ...method,
587
+ parameters: paramStmt.all(method.id),
588
+ }));
589
+ }
590
+ getClassFields(classId) {
591
+ return this.db
592
+ .prepare(`
593
+ SELECT
594
+ f.id,
595
+ f.class_id as classId,
596
+ c.name as className,
597
+ f.name,
598
+ f.obfuscated_name as obfuscatedName,
599
+ f.descriptor,
600
+ f.javadoc,
601
+ c.minecraft_version as minecraftVersion
602
+ FROM fields f
603
+ JOIN classes c ON f.class_id = c.id
604
+ WHERE f.class_id = ?
605
+ ORDER BY f.name
606
+ `)
607
+ .all(classId);
608
+ }
609
+ getMethod(className, methodName, minecraftVersion) {
610
+ const version = minecraftVersion || this.getLatestVersion();
611
+ if (!version)
612
+ return null;
613
+ const method = this.db
614
+ .prepare(`
615
+ SELECT
616
+ m.id,
617
+ m.class_id as classId,
618
+ c.name as className,
619
+ m.name,
620
+ m.obfuscated_name as obfuscatedName,
621
+ m.descriptor,
622
+ m.javadoc,
623
+ c.minecraft_version as minecraftVersion
624
+ FROM methods m
625
+ JOIN classes c ON m.class_id = c.id
626
+ WHERE c.minecraft_version = ?
627
+ AND (c.name = ? OR (c.package_name || '.' || c.name) = ?)
628
+ AND m.name = ?
629
+ LIMIT 1
630
+ `)
631
+ .get(version, className, className, methodName);
632
+ if (!method)
633
+ return null;
634
+ const parameters = this.db
635
+ .prepare(`
636
+ SELECT id, method_id as methodId, param_index as "index", name, javadoc
637
+ FROM parameters
638
+ WHERE method_id = ?
639
+ ORDER BY param_index
640
+ `)
641
+ .all(method.id);
642
+ return { ...method, parameters };
643
+ }
644
+ lookupObfuscated(obfuscatedName, minecraftVersion) {
645
+ const version = minecraftVersion || this.getLatestVersion();
646
+ if (!version)
647
+ return null;
648
+ const cls = this.db
649
+ .prepare(`
650
+ SELECT id, name, obfuscated_name as obfuscatedName, javadoc, package_name as packageName, minecraft_version as minecraftVersion
651
+ FROM classes
652
+ WHERE minecraft_version = ? AND obfuscated_name = ?
653
+ LIMIT 1
654
+ `)
655
+ .get(version, obfuscatedName);
656
+ if (cls) {
657
+ return {
658
+ type: 'class',
659
+ name: cls.name,
660
+ fullName: `${cls.packageName}.${cls.name}`,
661
+ obfuscatedName: cls.obfuscatedName,
662
+ descriptor: null,
663
+ javadoc: cls.javadoc,
664
+ className: null,
665
+ packageName: cls.packageName,
666
+ minecraftVersion: cls.minecraftVersion,
667
+ score: 1.0,
668
+ };
669
+ }
670
+ const method = this.db
671
+ .prepare(`
672
+ SELECT
673
+ m.id,
674
+ m.name,
675
+ m.obfuscated_name as obfuscatedName,
676
+ m.descriptor,
677
+ m.javadoc,
678
+ c.name as className,
679
+ c.package_name as packageName,
680
+ c.minecraft_version as minecraftVersion
681
+ FROM methods m
682
+ JOIN classes c ON m.class_id = c.id
683
+ WHERE c.minecraft_version = ? AND m.obfuscated_name = ?
684
+ LIMIT 1
685
+ `)
686
+ .get(version, obfuscatedName);
687
+ if (method) {
688
+ const parameters = this.db
689
+ .prepare(`
690
+ SELECT id, method_id as methodId, param_index as "index", name, javadoc
691
+ FROM parameters
692
+ WHERE method_id = ?
693
+ ORDER BY param_index
694
+ `)
695
+ .all(method.id);
696
+ return {
697
+ type: 'method',
698
+ name: method.name,
699
+ fullName: `${method.className}.${method.name}`,
700
+ obfuscatedName: method.obfuscatedName,
701
+ descriptor: method.descriptor,
702
+ javadoc: method.javadoc,
703
+ className: method.className,
704
+ packageName: method.packageName,
705
+ minecraftVersion: method.minecraftVersion,
706
+ parameters,
707
+ score: 1.0,
708
+ };
709
+ }
710
+ const field = this.db
711
+ .prepare(`
712
+ SELECT
713
+ f.id,
714
+ f.name,
715
+ f.obfuscated_name as obfuscatedName,
716
+ f.descriptor,
717
+ f.javadoc,
718
+ c.name as className,
719
+ c.package_name as packageName,
720
+ c.minecraft_version as minecraftVersion
721
+ FROM fields f
722
+ JOIN classes c ON f.class_id = c.id
723
+ WHERE c.minecraft_version = ? AND f.obfuscated_name = ?
724
+ LIMIT 1
725
+ `)
726
+ .get(version, obfuscatedName);
727
+ if (field) {
728
+ return {
729
+ type: 'field',
730
+ name: field.name,
731
+ fullName: `${field.className}.${field.name}`,
732
+ obfuscatedName: field.obfuscatedName,
733
+ descriptor: field.descriptor,
734
+ javadoc: field.javadoc,
735
+ className: field.className,
736
+ packageName: field.packageName,
737
+ minecraftVersion: field.minecraftVersion,
738
+ score: 1.0,
739
+ };
740
+ }
741
+ return null;
742
+ }
743
+ getPackages(minecraftVersion) {
744
+ const version = minecraftVersion || this.getLatestVersion();
745
+ if (!version)
746
+ return [];
747
+ const packages = this.db
748
+ .prepare(`
749
+ SELECT DISTINCT
750
+ CASE
751
+ WHEN INSTR(package_name, '.') > 0
752
+ THEN SUBSTR(package_name, 1, INSTR(package_name, '.') - 1)
753
+ ELSE package_name
754
+ END as topPackage
755
+ FROM classes
756
+ WHERE minecraft_version = ?
757
+ ORDER BY topPackage
758
+ `)
759
+ .all(version);
760
+ return packages.map((p) => p.topPackage);
761
+ }
762
+ getClassesInPackage(packageName, minecraftVersion) {
763
+ const version = minecraftVersion || this.getLatestVersion();
764
+ if (!version)
765
+ return [];
766
+ return this.db
767
+ .prepare(`
768
+ SELECT
769
+ c.id,
770
+ c.name,
771
+ c.obfuscated_name as obfuscatedName,
772
+ c.javadoc,
773
+ c.package_name as packageName,
774
+ c.minecraft_version as minecraftVersion,
775
+ (SELECT COUNT(*) FROM methods WHERE class_id = c.id) as methodCount,
776
+ (SELECT COUNT(*) FROM fields WHERE class_id = c.id) as fieldCount
777
+ FROM classes c
778
+ WHERE c.minecraft_version = ?
779
+ AND c.package_name LIKE ?
780
+ ORDER BY c.name
781
+ `)
782
+ .all(version, `${packageName}%`);
783
+ }
784
+ close() {
785
+ this.db.close();
786
+ }
787
+ }
788
+ //# sourceMappingURL=mappings-service.js.map