@adhisang/minecraft-modding-mcp 4.0.0 → 4.1.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 (174) hide show
  1. package/CHANGELOG.md +47 -0
  2. package/README.md +36 -23
  3. package/dist/build-suggested-call.d.ts +29 -0
  4. package/dist/build-suggested-call.js +58 -0
  5. package/dist/cache-registry.d.ts +3 -1
  6. package/dist/cache-registry.js +50 -6
  7. package/dist/entry-tools/analyze-symbol-service.d.ts +16 -16
  8. package/dist/entry-tools/batch-class-members-service.d.ts +34 -0
  9. package/dist/entry-tools/batch-class-members-service.js +97 -0
  10. package/dist/entry-tools/batch-class-source-service.d.ts +37 -0
  11. package/dist/entry-tools/batch-class-source-service.js +100 -0
  12. package/dist/entry-tools/batch-mappings-service.d.ts +36 -0
  13. package/dist/entry-tools/batch-mappings-service.js +66 -0
  14. package/dist/entry-tools/batch-runner.d.ts +72 -0
  15. package/dist/entry-tools/batch-runner.js +90 -0
  16. package/dist/entry-tools/batch-symbol-exists-service.d.ts +46 -0
  17. package/dist/entry-tools/batch-symbol-exists-service.js +113 -0
  18. package/dist/entry-tools/compare-minecraft-service.d.ts +6 -6
  19. package/dist/entry-tools/inspect-minecraft/handlers/artifact.d.ts +5 -0
  20. package/dist/entry-tools/inspect-minecraft/handlers/artifact.js +83 -0
  21. package/dist/entry-tools/inspect-minecraft/handlers/class-members.d.ts +6 -0
  22. package/dist/entry-tools/inspect-minecraft/handlers/class-members.js +80 -0
  23. package/dist/entry-tools/inspect-minecraft/handlers/class-overview.d.ts +5 -0
  24. package/dist/entry-tools/inspect-minecraft/handlers/class-overview.js +248 -0
  25. package/dist/entry-tools/inspect-minecraft/handlers/class-source.d.ts +5 -0
  26. package/dist/entry-tools/inspect-minecraft/handlers/class-source.js +60 -0
  27. package/dist/entry-tools/inspect-minecraft/handlers/file.d.ts +5 -0
  28. package/dist/entry-tools/inspect-minecraft/handlers/file.js +54 -0
  29. package/dist/entry-tools/inspect-minecraft/handlers/list-files.d.ts +5 -0
  30. package/dist/entry-tools/inspect-minecraft/handlers/list-files.js +100 -0
  31. package/dist/entry-tools/inspect-minecraft/handlers/search.d.ts +5 -0
  32. package/dist/entry-tools/inspect-minecraft/handlers/search.js +155 -0
  33. package/dist/entry-tools/inspect-minecraft/handlers/versions.d.ts +6 -0
  34. package/dist/entry-tools/inspect-minecraft/handlers/versions.js +49 -0
  35. package/dist/entry-tools/inspect-minecraft/internal.d.ts +1042 -0
  36. package/dist/entry-tools/inspect-minecraft/internal.js +448 -0
  37. package/dist/entry-tools/inspect-minecraft-service.d.ts +193 -308
  38. package/dist/entry-tools/inspect-minecraft-service.js +20 -1244
  39. package/dist/entry-tools/manage-cache-service.d.ts +16 -16
  40. package/dist/entry-tools/validate-project/cases/access-transformer.d.ts +6 -0
  41. package/dist/entry-tools/validate-project/cases/access-transformer.js +106 -0
  42. package/dist/entry-tools/validate-project/cases/access-widener.d.ts +6 -0
  43. package/dist/entry-tools/validate-project/cases/access-widener.js +86 -0
  44. package/dist/entry-tools/validate-project/cases/mixin.d.ts +6 -0
  45. package/dist/entry-tools/validate-project/cases/mixin.js +90 -0
  46. package/dist/entry-tools/validate-project/cases/project-summary.d.ts +97 -0
  47. package/dist/entry-tools/validate-project/cases/project-summary.js +346 -0
  48. package/dist/entry-tools/validate-project/internal.d.ts +135 -0
  49. package/dist/entry-tools/validate-project/internal.js +287 -0
  50. package/dist/entry-tools/validate-project-service.d.ts +63 -47
  51. package/dist/entry-tools/validate-project-service.js +12 -562
  52. package/dist/entry-tools/verify-mixin-target-service.d.ts +133 -0
  53. package/dist/entry-tools/verify-mixin-target-service.js +323 -0
  54. package/dist/error-mapping.d.ts +40 -0
  55. package/dist/error-mapping.js +139 -0
  56. package/dist/errors.d.ts +6 -0
  57. package/dist/errors.js +6 -0
  58. package/dist/index.d.ts +2 -0
  59. package/dist/index.js +142 -1352
  60. package/dist/mapping/internal-types.d.ts +54 -0
  61. package/dist/mapping/internal-types.js +14 -0
  62. package/dist/mapping/loaders/mojang.d.ts +2 -0
  63. package/dist/mapping/loaders/mojang.js +64 -0
  64. package/dist/mapping/loaders/tiny-loom.d.ts +2 -0
  65. package/dist/mapping/loaders/tiny-loom.js +73 -0
  66. package/dist/mapping/loaders/tiny-maven.d.ts +2 -0
  67. package/dist/mapping/loaders/tiny-maven.js +104 -0
  68. package/dist/mapping/loaders/types.d.ts +14 -0
  69. package/dist/mapping/loaders/types.js +2 -0
  70. package/dist/mapping/lookup.d.ts +52 -0
  71. package/dist/mapping/lookup.js +496 -0
  72. package/dist/mapping/parsers/normalize.d.ts +10 -0
  73. package/dist/mapping/parsers/normalize.js +52 -0
  74. package/dist/mapping/parsers/proguard.d.ts +20 -0
  75. package/dist/mapping/parsers/proguard.js +138 -0
  76. package/dist/mapping/parsers/symbol-records.d.ts +27 -0
  77. package/dist/mapping/parsers/symbol-records.js +216 -0
  78. package/dist/mapping/parsers/tiny.d.ts +9 -0
  79. package/dist/mapping/parsers/tiny.js +96 -0
  80. package/dist/mapping/types.d.ts +147 -0
  81. package/dist/mapping/types.js +2 -0
  82. package/dist/mapping-pipeline-service.js +3 -2
  83. package/dist/mapping-service.d.ts +3 -144
  84. package/dist/mapping-service.js +19 -1201
  85. package/dist/mixin/access-validators.d.ts +9 -0
  86. package/dist/mixin/access-validators.js +257 -0
  87. package/dist/mixin/annotation-validators.d.ts +5 -0
  88. package/dist/mixin/annotation-validators.js +162 -0
  89. package/dist/mixin/helpers.d.ts +28 -0
  90. package/dist/mixin/helpers.js +315 -0
  91. package/dist/mixin/parsed-validator.d.ts +8 -0
  92. package/dist/mixin/parsed-validator.js +337 -0
  93. package/dist/mixin/types.d.ts +208 -0
  94. package/dist/mixin/types.js +28 -0
  95. package/dist/mixin-validator.d.ts +9 -201
  96. package/dist/mixin-validator.js +8 -1020
  97. package/dist/source/access-validate.d.ts +4 -0
  98. package/dist/source/access-validate.js +254 -0
  99. package/dist/source/artifact-resolver.d.ts +110 -0
  100. package/dist/source/artifact-resolver.js +1174 -0
  101. package/dist/source/cache-metrics.d.ts +26 -0
  102. package/dist/source/cache-metrics.js +172 -0
  103. package/dist/source/class-source/members-builder.d.ts +34 -0
  104. package/dist/source/class-source/members-builder.js +46 -0
  105. package/dist/source/class-source/snippet-builder.d.ts +19 -0
  106. package/dist/source/class-source/snippet-builder.js +46 -0
  107. package/dist/source/class-source-helpers.d.ts +34 -0
  108. package/dist/source/class-source-helpers.js +140 -0
  109. package/dist/source/class-source.d.ts +42 -0
  110. package/dist/source/class-source.js +883 -0
  111. package/dist/source/descriptor-utils.d.ts +6 -0
  112. package/dist/source/descriptor-utils.js +37 -0
  113. package/dist/source/file-access.d.ts +4 -0
  114. package/dist/source/file-access.js +102 -0
  115. package/dist/source/indexer.d.ts +82 -0
  116. package/dist/source/indexer.js +505 -0
  117. package/dist/source/lifecycle/diff-utils.d.ts +9 -0
  118. package/dist/source/lifecycle/diff-utils.js +107 -0
  119. package/dist/source/lifecycle/diff.d.ts +2 -0
  120. package/dist/source/lifecycle/diff.js +265 -0
  121. package/dist/source/lifecycle/mapping-helpers.d.ts +22 -0
  122. package/dist/source/lifecycle/mapping-helpers.js +327 -0
  123. package/dist/source/lifecycle/runtime-check.d.ts +2 -0
  124. package/dist/source/lifecycle/runtime-check.js +142 -0
  125. package/dist/source/lifecycle/trace.d.ts +2 -0
  126. package/dist/source/lifecycle/trace.js +231 -0
  127. package/dist/source/lifecycle.d.ts +4 -0
  128. package/dist/source/lifecycle.js +5 -0
  129. package/dist/source/search.d.ts +51 -0
  130. package/dist/source/search.js +676 -0
  131. package/dist/source/shared-utils.d.ts +6 -0
  132. package/dist/source/shared-utils.js +55 -0
  133. package/dist/source/state.d.ts +21 -0
  134. package/dist/source/state.js +19 -0
  135. package/dist/source/symbol-resolver.d.ts +3 -0
  136. package/dist/source/symbol-resolver.js +212 -0
  137. package/dist/source/validate-mixin/pipeline/mapping-health.d.ts +3 -0
  138. package/dist/source/validate-mixin/pipeline/mapping-health.js +41 -0
  139. package/dist/source/validate-mixin/pipeline/parse.d.ts +2 -0
  140. package/dist/source/validate-mixin/pipeline/parse.js +10 -0
  141. package/dist/source/validate-mixin/pipeline/resolve.d.ts +3 -0
  142. package/dist/source/validate-mixin/pipeline/resolve.js +78 -0
  143. package/dist/source/validate-mixin/pipeline/target-lookup.d.ts +6 -0
  144. package/dist/source/validate-mixin/pipeline/target-lookup.js +260 -0
  145. package/dist/source/validate-mixin/pipeline-context.d.ts +72 -0
  146. package/dist/source/validate-mixin/pipeline-context.js +93 -0
  147. package/dist/source/validate-mixin.d.ts +22 -0
  148. package/dist/source/validate-mixin.js +799 -0
  149. package/dist/source/workspace-target.d.ts +18 -0
  150. package/dist/source/workspace-target.js +305 -0
  151. package/dist/source-service.d.ts +147 -170
  152. package/dist/source-service.js +67 -6116
  153. package/dist/stage-emitter.d.ts +13 -0
  154. package/dist/stage-emitter.js +30 -0
  155. package/dist/stdio-supervisor.d.ts +61 -0
  156. package/dist/stdio-supervisor.js +326 -9
  157. package/dist/tool-contract-manifest.d.ts +1 -1
  158. package/dist/tool-contract-manifest.js +23 -6
  159. package/dist/tool-guidance.d.ts +82 -0
  160. package/dist/tool-guidance.js +734 -0
  161. package/dist/tool-schema-registry.d.ts +16 -0
  162. package/dist/tool-schema-registry.js +37 -0
  163. package/dist/tool-schemas.d.ts +3518 -0
  164. package/dist/tool-schemas.js +813 -0
  165. package/dist/types.d.ts +36 -0
  166. package/dist/version-service.js +7 -6
  167. package/dist/workspace-context-cache.d.ts +32 -0
  168. package/dist/workspace-context-cache.js +66 -0
  169. package/dist/workspace-mapping-service.d.ts +16 -0
  170. package/dist/workspace-mapping-service.js +173 -1
  171. package/docs/README-ja.md +414 -0
  172. package/docs/examples.md +483 -0
  173. package/docs/tool-reference.md +459 -0
  174. package/package.json +3 -2
@@ -0,0 +1,676 @@
1
+ import { ERROR_CODES, createError } from "../errors.js";
2
+ import { log } from "../logger.js";
3
+ import { createSearchHitAccumulator, decodeSearchCursor, encodeSearchCursor } from "../search-hit-accumulator.js";
4
+ import { normalizePathStyle } from "./shared-utils.js";
5
+ const SYMBOL_KINDS = ["class", "interface", "enum", "record", "method", "field"];
6
+ const MAX_REGEX_QUERY_LENGTH = 200;
7
+ const MAX_REGEX_RESULT_LIMIT = 100;
8
+ const MAX_HELPER_REGEX_CACHE = 128;
9
+ const GLOB_REGEX_CACHE = new Map();
10
+ export { MAX_REGEX_QUERY_LENGTH, MAX_REGEX_RESULT_LIMIT };
11
+ function rememberCachedRegex(cache, key, regex) {
12
+ if (cache.size >= MAX_HELPER_REGEX_CACHE) {
13
+ const oldestKey = cache.keys().next().value;
14
+ if (oldestKey) {
15
+ cache.delete(oldestKey);
16
+ }
17
+ }
18
+ cache.set(key, regex);
19
+ return regex;
20
+ }
21
+ function isSymbolKind(value) {
22
+ return SYMBOL_KINDS.includes(value);
23
+ }
24
+ function clampLimit(limit, fallback, max) {
25
+ if (!Number.isFinite(limit) || limit == null) {
26
+ return fallback;
27
+ }
28
+ return Math.max(1, Math.min(max, Math.trunc(limit)));
29
+ }
30
+ export function normalizeIntent(intent) {
31
+ if (intent === "path" || intent === "text") {
32
+ return intent;
33
+ }
34
+ return "symbol";
35
+ }
36
+ export function normalizeMatch(match) {
37
+ if (match === "exact" || match === "contains" || match === "regex") {
38
+ return match;
39
+ }
40
+ return "prefix";
41
+ }
42
+ export function canUseIndexedSearchPath(indexedSearchEnabled, intent, match, _scope) {
43
+ if (!indexedSearchEnabled) {
44
+ return false;
45
+ }
46
+ if (intent !== "text" && intent !== "path") {
47
+ return false;
48
+ }
49
+ if (match === "regex") {
50
+ return false;
51
+ }
52
+ // packagePrefix and fileGlob are applied as post-filters on indexed candidates.
53
+ return true;
54
+ }
55
+ export function buildGlobRegex(pattern) {
56
+ const cached = GLOB_REGEX_CACHE.get(pattern);
57
+ if (cached) {
58
+ GLOB_REGEX_CACHE.delete(pattern);
59
+ GLOB_REGEX_CACHE.set(pattern, cached);
60
+ return cached;
61
+ }
62
+ const REGEX_META = /[-/\\^$+.()|[\]{}]/;
63
+ let result = "";
64
+ let i = 0;
65
+ while (i < pattern.length) {
66
+ const ch = pattern[i];
67
+ if (ch === "*" && pattern[i + 1] === "*") {
68
+ result += ".*";
69
+ i += 2;
70
+ if (pattern[i] === "/") {
71
+ result += "(?:/)?";
72
+ i += 1;
73
+ }
74
+ }
75
+ else if (ch === "*") {
76
+ result += "[^/]*";
77
+ i += 1;
78
+ }
79
+ else if (ch === "?") {
80
+ result += "[^/]";
81
+ i += 1;
82
+ }
83
+ else {
84
+ result += REGEX_META.test(ch) ? `\\${ch}` : ch;
85
+ i += 1;
86
+ }
87
+ }
88
+ return rememberCachedRegex(GLOB_REGEX_CACHE, pattern, new RegExp(`^${result}$`));
89
+ }
90
+ export function globToSqlLike(pattern) {
91
+ let result = "";
92
+ for (const char of pattern) {
93
+ if (char === "*") {
94
+ result += "%";
95
+ continue;
96
+ }
97
+ if (char === "?") {
98
+ result += "_";
99
+ continue;
100
+ }
101
+ if (char === "%" || char === "_" || char === "\\") {
102
+ result += `\\${char}`;
103
+ continue;
104
+ }
105
+ result += char;
106
+ }
107
+ return result;
108
+ }
109
+ export function checkPackagePrefix(filePath, packagePrefix) {
110
+ if (!packagePrefix) {
111
+ return true;
112
+ }
113
+ const normalizedPrefix = packagePrefix.replace(/\.+/g, "/").replace(/\/+$/, "");
114
+ return normalizePathStyle(filePath).startsWith(`${normalizedPrefix}/`);
115
+ }
116
+ export function buildSearchCursorContext(input) {
117
+ return JSON.stringify({
118
+ artifactId: input.artifactId,
119
+ query: input.query,
120
+ intent: input.intent,
121
+ match: input.match,
122
+ queryMode: input.queryMode,
123
+ packagePrefix: input.scope?.packagePrefix ?? "",
124
+ fileGlob: input.scope?.fileGlob ?? "",
125
+ symbolKind: input.scope?.symbolKind ?? ""
126
+ });
127
+ }
128
+ function toLower(value) {
129
+ return value.toLocaleLowerCase();
130
+ }
131
+ export function compileRegex(query) {
132
+ try {
133
+ return new RegExp(query, "i");
134
+ }
135
+ catch {
136
+ throw createError({
137
+ code: ERROR_CODES.INVALID_INPUT,
138
+ message: "Invalid regex query.",
139
+ details: { query }
140
+ });
141
+ }
142
+ }
143
+ export function findMatchIndex(target, query, match, pattern) {
144
+ if (!query) {
145
+ return -1;
146
+ }
147
+ if (match === "regex") {
148
+ if (!pattern) {
149
+ return -1;
150
+ }
151
+ pattern.lastIndex = 0;
152
+ const result = pattern.exec(target);
153
+ return result?.index ?? -1;
154
+ }
155
+ if (match === "exact") {
156
+ return target === query ? 0 : -1;
157
+ }
158
+ const normalizedTarget = toLower(target);
159
+ const normalizedQuery = toLower(query);
160
+ if (match === "prefix") {
161
+ return normalizedTarget.startsWith(normalizedQuery) ? 0 : -1;
162
+ }
163
+ return normalizedTarget.indexOf(normalizedQuery);
164
+ }
165
+ /**
166
+ * Content-aware variant of findMatchIndex for searching within file text.
167
+ */
168
+ export function findContentMatchIndex(content, query, match, pattern) {
169
+ if (!query) {
170
+ return -1;
171
+ }
172
+ if (match === "regex") {
173
+ if (!pattern) {
174
+ return -1;
175
+ }
176
+ pattern.lastIndex = 0;
177
+ const result = pattern.exec(content);
178
+ return result?.index ?? -1;
179
+ }
180
+ if (match === "exact") {
181
+ return content.indexOf(query);
182
+ }
183
+ const normalizedContent = toLower(content);
184
+ const normalizedQuery = toLower(query);
185
+ return normalizedContent.indexOf(normalizedQuery);
186
+ }
187
+ export function scoreSymbolMatch(match, index, symbolKind) {
188
+ const matchBase = match === "exact" ? 350 : match === "prefix" ? 310 : match === "contains" ? 270 : 250;
189
+ const kindBonus = symbolKind === "class" || symbolKind === "interface" || symbolKind === "record"
190
+ ? 25
191
+ : symbolKind === "enum"
192
+ ? 20
193
+ : symbolKind === "method"
194
+ ? 15
195
+ : 8;
196
+ return matchBase + kindBonus + Math.max(0, 80 - Math.min(80, index));
197
+ }
198
+ export function scoreTextMatch(match, index) {
199
+ const matchBase = match === "exact" ? 280 : match === "prefix" ? 250 : match === "contains" ? 220 : 200;
200
+ return matchBase + Math.max(0, 90 - Math.min(90, Math.floor(index / 2)));
201
+ }
202
+ export function scorePathMatch(match, index) {
203
+ const matchBase = match === "exact" ? 260 : match === "prefix" ? 230 : match === "contains" ? 210 : 190;
204
+ return matchBase + Math.max(0, 100 - Math.min(100, index));
205
+ }
206
+ export function matchRegexIndex(target, regex) {
207
+ regex.lastIndex = 0;
208
+ const result = regex.exec(target);
209
+ return result?.index ?? -1;
210
+ }
211
+ export async function searchClassSource(svc, input) {
212
+ const startedAt = Date.now();
213
+ try {
214
+ const artifact = svc.getArtifact(input.artifactId);
215
+ const originalQuery = input.query.trim();
216
+ if (!originalQuery) {
217
+ return {
218
+ hits: [],
219
+ mappingApplied: artifact.mappingApplied ?? "obfuscated",
220
+ returnedNamespace: artifact.mappingApplied ?? "obfuscated",
221
+ artifactContents: svc.buildArtifactContentsSummary({
222
+ origin: artifact.origin,
223
+ sourceJarPath: artifact.sourceJarPath,
224
+ isDecompiled: artifact.isDecompiled,
225
+ qualityFlags: artifact.qualityFlags
226
+ })
227
+ };
228
+ }
229
+ const intent = normalizeIntent(input.intent);
230
+ const match = normalizeMatch(input.match);
231
+ const artifactMapping = artifact.mappingApplied ?? "obfuscated";
232
+ const searchWarnings = [];
233
+ let translatedInfo;
234
+ let query = originalQuery;
235
+ let translationPackagePrefix;
236
+ if (input.queryNamespace
237
+ && input.queryNamespace !== artifactMapping
238
+ && !artifact.version) {
239
+ searchWarnings.push(`queryNamespace=${input.queryNamespace} could not be applied because the artifact has no version recorded; namespace translation requires a version. Running literal search in ${artifactMapping} instead.`);
240
+ }
241
+ if (input.queryNamespace
242
+ && input.queryNamespace !== artifactMapping
243
+ && artifact.version) {
244
+ if (intent === "symbol" && originalQuery.includes(".") && /^[\w.$]+$/.test(originalQuery)) {
245
+ try {
246
+ const translated = await svc.mappingService.findMapping({
247
+ version: artifact.version,
248
+ kind: "class",
249
+ name: originalQuery,
250
+ sourceMapping: input.queryNamespace,
251
+ targetMapping: artifactMapping,
252
+ sourcePriority: input.sourcePriority,
253
+ signatureMode: "name-only",
254
+ maxCandidates: 5
255
+ });
256
+ if (translated.resolved === true && translated.resolvedSymbol) {
257
+ const resolvedName = translated.resolvedSymbol.symbol
258
+ ?? translated.resolvedSymbol.name;
259
+ if (resolvedName && resolvedName !== originalQuery) {
260
+ translatedInfo = {
261
+ original: originalQuery,
262
+ translated: resolvedName,
263
+ fromNamespace: input.queryNamespace,
264
+ toNamespace: artifactMapping
265
+ };
266
+ if (resolvedName.includes(".")) {
267
+ const lastDot = resolvedName.lastIndexOf(".");
268
+ const simpleName = resolvedName.slice(lastDot + 1);
269
+ const packagePart = resolvedName.slice(0, lastDot);
270
+ query = simpleName;
271
+ if (!input.scope?.packagePrefix) {
272
+ translationPackagePrefix = packagePart;
273
+ }
274
+ }
275
+ else {
276
+ query = resolvedName;
277
+ }
278
+ }
279
+ }
280
+ else if (translated.status === "ambiguous") {
281
+ const candidateCount = translated.candidateCount ?? translated.candidates?.length ?? 0;
282
+ searchWarnings.push(`queryNamespace=${input.queryNamespace}: translation for "${originalQuery}" was ambiguous (${candidateCount} candidates); running literal search instead. Narrow the query with a more specific FQCN or call find-mapping directly.`);
283
+ }
284
+ else if (translated.status === "not_found") {
285
+ searchWarnings.push(`queryNamespace=${input.queryNamespace}: no ${artifactMapping} mapping found for "${originalQuery}"; running literal search instead.`);
286
+ }
287
+ else if (translated.status === "mapping_unavailable") {
288
+ searchWarnings.push(`queryNamespace=${input.queryNamespace}: mapping data unavailable for version ${artifact.version}; running literal search instead.`);
289
+ }
290
+ else {
291
+ searchWarnings.push(`queryNamespace=${input.queryNamespace}: could not translate "${originalQuery}" to ${artifactMapping}; running literal search instead.`);
292
+ }
293
+ }
294
+ catch (caughtError) {
295
+ searchWarnings.push(`queryNamespace=${input.queryNamespace}: translation failed (${caughtError instanceof Error ? caughtError.message : String(caughtError)}); running literal search instead.`);
296
+ }
297
+ }
298
+ else if (intent === "text" || intent === "path") {
299
+ searchWarnings.push(`queryNamespace=${input.queryNamespace} has no effect when intent="${intent}" — ${intent} search is a literal match against the artifact's ${artifactMapping} index. Use intent="symbol" for namespace translation.`);
300
+ }
301
+ }
302
+ if (match === "regex" && query.length > MAX_REGEX_QUERY_LENGTH) {
303
+ throw createError({
304
+ code: ERROR_CODES.INVALID_INPUT,
305
+ message: `Regex query exceeds max length of ${MAX_REGEX_QUERY_LENGTH} characters.`,
306
+ details: {
307
+ queryLength: query.length,
308
+ maxLength: MAX_REGEX_QUERY_LENGTH
309
+ }
310
+ });
311
+ }
312
+ const searchLimitCap = match === "regex"
313
+ ? Math.max(1, Math.min(svc.config.maxSearchHits, MAX_REGEX_RESULT_LIMIT))
314
+ : svc.config.maxSearchHits;
315
+ const scope = translationPackagePrefix
316
+ ? { ...(input.scope ?? {}), packagePrefix: translationPackagePrefix }
317
+ : input.scope;
318
+ if (scope?.symbolKind && intent !== "symbol") {
319
+ throw createError({
320
+ code: ERROR_CODES.INVALID_INPUT,
321
+ message: 'symbolKind filter is only supported when intent="symbol".'
322
+ });
323
+ }
324
+ const limit = clampLimit(input.limit, 20, searchLimitCap);
325
+ const regexPattern = match === "regex" ? compileRegex(query) : undefined;
326
+ const queryMode = input.queryMode ?? "auto";
327
+ svc.metrics.recordSearchQueryMode(queryMode);
328
+ const cursorContext = buildSearchCursorContext({
329
+ artifactId: artifact.artifactId,
330
+ query,
331
+ intent,
332
+ match,
333
+ queryMode,
334
+ scope
335
+ });
336
+ const decodedCursor = decodeSearchCursor(input.cursor);
337
+ const cursor = decodedCursor?.contextKey === cursorContext ? decodedCursor : undefined;
338
+ const accumulator = createSearchHitAccumulator(limit, cursor);
339
+ const indexedSearchEnabled = svc.config.indexedSearchEnabled !== false;
340
+ if (match === "regex") {
341
+ svc.metrics.recordSearchRegexFallback();
342
+ }
343
+ const intentStartedAt = Date.now();
344
+ const recordHit = (hit) => {
345
+ accumulator.add(hit);
346
+ };
347
+ const tokenOnlyTextIntent = intent === "text" && queryMode === "token";
348
+ if (intent === "symbol") {
349
+ searchSymbolIntent(svc, artifact.artifactId, query, match, scope, regexPattern, recordHit);
350
+ }
351
+ else if (queryMode === "literal" && intent === "text") {
352
+ svc.metrics.recordSearchFallback();
353
+ searchTextIntent(svc, artifact.artifactId, query, match, scope, regexPattern, recordHit);
354
+ }
355
+ else if (!indexedSearchEnabled) {
356
+ svc.metrics.recordIndexedDisabled();
357
+ if (!tokenOnlyTextIntent) {
358
+ svc.metrics.recordSearchFallback();
359
+ if (intent === "path") {
360
+ searchPathIntent(svc, artifact.artifactId, query, match, scope, regexPattern, recordHit);
361
+ }
362
+ else {
363
+ searchTextIntent(svc, artifact.artifactId, query, match, scope, regexPattern, recordHit);
364
+ }
365
+ }
366
+ }
367
+ else if (canUseIndexedSearchPath(indexedSearchEnabled, intent, match, scope)) {
368
+ try {
369
+ if (intent === "path") {
370
+ searchPathIntentIndexed(svc, artifact.artifactId, query, match, scope, recordHit);
371
+ }
372
+ else {
373
+ searchTextIntentIndexed(svc, artifact.artifactId, query, match, scope, recordHit);
374
+ }
375
+ svc.metrics.recordSearchIndexedHit();
376
+ }
377
+ catch (caughtError) {
378
+ svc.metrics.recordSearchFallback();
379
+ log("warn", "search.indexed_fallback", {
380
+ artifactId: artifact.artifactId,
381
+ intent,
382
+ match,
383
+ reason: caughtError instanceof Error ? caughtError.message : String(caughtError)
384
+ });
385
+ if (!tokenOnlyTextIntent) {
386
+ if (intent === "path") {
387
+ searchPathIntent(svc, artifact.artifactId, query, match, scope, regexPattern, recordHit);
388
+ }
389
+ else {
390
+ searchTextIntent(svc, artifact.artifactId, query, match, scope, regexPattern, recordHit);
391
+ }
392
+ }
393
+ }
394
+ }
395
+ else {
396
+ if (!tokenOnlyTextIntent) {
397
+ svc.metrics.recordSearchFallback();
398
+ if (intent === "path") {
399
+ searchPathIntent(svc, artifact.artifactId, query, match, scope, regexPattern, recordHit);
400
+ }
401
+ else {
402
+ searchTextIntent(svc, artifact.artifactId, query, match, scope, regexPattern, recordHit);
403
+ }
404
+ }
405
+ }
406
+ svc.metrics.recordSearchIntentDuration(intent, Date.now() - intentStartedAt);
407
+ const finalizedHits = accumulator.finalize();
408
+ const page = finalizedHits.page;
409
+ svc.metrics.recordSearchRowsReturned(page.length);
410
+ const nextCursor = finalizedHits.nextCursorHit
411
+ ? encodeSearchCursor(finalizedHits.nextCursorHit, cursorContext)
412
+ : undefined;
413
+ svc.metrics.recordSearchTokenBytesReturned(Buffer.byteLength(JSON.stringify({ hits: page }), "utf8"));
414
+ return {
415
+ hits: page,
416
+ nextCursor,
417
+ mappingApplied: artifact.mappingApplied ?? "obfuscated",
418
+ returnedNamespace: artifact.mappingApplied ?? "obfuscated",
419
+ artifactContents: svc.buildArtifactContentsSummary({
420
+ origin: artifact.origin,
421
+ sourceJarPath: artifact.sourceJarPath,
422
+ isDecompiled: artifact.isDecompiled,
423
+ qualityFlags: artifact.qualityFlags
424
+ }),
425
+ ...(translatedInfo ? { translatedQuery: translatedInfo } : {}),
426
+ ...(searchWarnings.length > 0 ? { warnings: searchWarnings } : {})
427
+ };
428
+ }
429
+ finally {
430
+ svc.metrics.recordDuration("search_duration_ms", Date.now() - startedAt);
431
+ }
432
+ }
433
+ export function searchSymbolIntent(svc, artifactId, query, match, scope, regexPattern, onHit) {
434
+ const matchedSymbols = findSymbolHits(svc, artifactId, query, match, scope, regexPattern);
435
+ for (const item of matchedSymbols) {
436
+ onHit({
437
+ filePath: item.symbol.filePath,
438
+ score: item.score,
439
+ matchedIn: "symbol",
440
+ reasonCodes: [`symbol_${match}`],
441
+ symbol: {
442
+ symbolKind: item.symbol.symbolKind,
443
+ symbolName: item.symbol.symbolName,
444
+ qualifiedName: item.symbol.qualifiedName,
445
+ line: item.symbol.line
446
+ }
447
+ });
448
+ }
449
+ }
450
+ export function searchTextIntentIndexed(svc, artifactId, query, match, scope, onHit) {
451
+ const candidateLimit = indexedCandidateLimitForMatch(svc, match);
452
+ const indexed = svc.filesRepo.searchFileCandidates(artifactId, {
453
+ query,
454
+ match,
455
+ limit: candidateLimit,
456
+ mode: "text"
457
+ });
458
+ svc.metrics.recordSearchDbRoundtrip(indexed.dbRoundtrips);
459
+ svc.metrics.recordSearchRowsScanned(indexed.scannedRows);
460
+ if (indexed.items.length === 0) {
461
+ svc.metrics.recordSearchIndexedZeroShortcircuit();
462
+ return;
463
+ }
464
+ const globFilter = scope?.fileGlob ? buildGlobRegex(normalizePathStyle(scope.fileGlob)) : undefined;
465
+ const candidatePaths = indexed.items
466
+ .filter((candidate) => candidate.matchedIn !== "path")
467
+ .map((candidate) => candidate.filePath)
468
+ .filter((filePath) => checkPackagePrefix(filePath, scope?.packagePrefix))
469
+ .filter((filePath) => !globFilter || globFilter.test(filePath));
470
+ const candidateContentRows = svc.filesRepo.getFileContentsByPaths(artifactId, candidatePaths);
471
+ svc.metrics.recordSearchDbRoundtrip();
472
+ svc.metrics.recordSearchRowsScanned(candidateContentRows.length);
473
+ const candidateRows = [];
474
+ for (const candidate of candidateContentRows) {
475
+ const contentIndex = findContentMatchIndex(candidate.content, query, match);
476
+ if (contentIndex < 0) {
477
+ continue;
478
+ }
479
+ candidateRows.push({
480
+ filePath: candidate.filePath,
481
+ contentIndex
482
+ });
483
+ }
484
+ for (const candidate of candidateRows) {
485
+ onHit({
486
+ filePath: candidate.filePath,
487
+ score: scoreTextMatch(match, candidate.contentIndex),
488
+ matchedIn: "content",
489
+ reasonCodes: ["content_match", `text_${match}`, "indexed"]
490
+ });
491
+ }
492
+ }
493
+ export function searchPathIntentIndexed(svc, artifactId, query, match, scope, onHit) {
494
+ const candidateLimit = indexedCandidateLimitForMatch(svc, match);
495
+ const indexed = svc.filesRepo.searchFileCandidates(artifactId, {
496
+ query,
497
+ limit: candidateLimit,
498
+ mode: "path"
499
+ });
500
+ svc.metrics.recordSearchDbRoundtrip(indexed.dbRoundtrips);
501
+ svc.metrics.recordSearchRowsScanned(indexed.scannedRows);
502
+ if (indexed.items.length === 0) {
503
+ svc.metrics.recordSearchIndexedZeroShortcircuit();
504
+ return;
505
+ }
506
+ const globFilter = scope?.fileGlob ? buildGlobRegex(normalizePathStyle(scope.fileGlob)) : undefined;
507
+ const candidateRows = [];
508
+ for (const candidate of indexed.items) {
509
+ if (candidate.matchedIn === "content") {
510
+ continue;
511
+ }
512
+ if (!checkPackagePrefix(candidate.filePath, scope?.packagePrefix)) {
513
+ continue;
514
+ }
515
+ if (globFilter && !globFilter.test(candidate.filePath)) {
516
+ continue;
517
+ }
518
+ const pathIndex = findMatchIndex(candidate.filePath, query, match);
519
+ if (pathIndex < 0) {
520
+ continue;
521
+ }
522
+ candidateRows.push({
523
+ filePath: candidate.filePath,
524
+ pathIndex
525
+ });
526
+ }
527
+ for (const candidate of candidateRows) {
528
+ onHit({
529
+ filePath: candidate.filePath,
530
+ score: scorePathMatch(match, candidate.pathIndex),
531
+ matchedIn: "path",
532
+ reasonCodes: ["path_match", `path_${match}`, "indexed"]
533
+ });
534
+ }
535
+ }
536
+ export function searchTextIntent(svc, artifactId, query, match, scope, regexPattern, onHit) {
537
+ const pageSize = Math.max(1, svc.config.searchScanPageSize ?? 250);
538
+ const glob = scope?.fileGlob ? buildGlobRegex(normalizePathStyle(scope.fileGlob)) : undefined;
539
+ let cursor = undefined;
540
+ while (true) {
541
+ const page = svc.filesRepo.listFileRows(artifactId, { limit: pageSize, cursor });
542
+ svc.metrics.recordSearchDbRoundtrip();
543
+ svc.metrics.recordSearchRowsScanned(page.items.length);
544
+ for (const row of page.items) {
545
+ if (!checkPackagePrefix(row.filePath, scope?.packagePrefix)) {
546
+ continue;
547
+ }
548
+ if (glob && !glob.test(row.filePath)) {
549
+ continue;
550
+ }
551
+ const contentIndex = match === "regex"
552
+ ? matchRegexIndex(row.content, regexPattern)
553
+ : findContentMatchIndex(row.content, query, match);
554
+ if (contentIndex < 0) {
555
+ continue;
556
+ }
557
+ onHit({
558
+ filePath: row.filePath,
559
+ score: scoreTextMatch(match, contentIndex),
560
+ matchedIn: "content",
561
+ reasonCodes: ["content_match", `text_${match}`]
562
+ });
563
+ }
564
+ if (!page.nextCursor) {
565
+ break;
566
+ }
567
+ cursor = page.nextCursor;
568
+ }
569
+ }
570
+ export function searchPathIntent(svc, artifactId, query, match, scope, regexPattern, onHit) {
571
+ const pageSize = Math.max(1, svc.config.searchScanPageSize ?? 250);
572
+ const glob = scope?.fileGlob ? buildGlobRegex(normalizePathStyle(scope.fileGlob)) : undefined;
573
+ let cursor = undefined;
574
+ while (true) {
575
+ const page = svc.filesRepo.listFiles(artifactId, { limit: pageSize, cursor });
576
+ svc.metrics.recordSearchDbRoundtrip();
577
+ svc.metrics.recordSearchRowsScanned(page.items.length);
578
+ for (const filePath of page.items) {
579
+ if (!checkPackagePrefix(filePath, scope?.packagePrefix)) {
580
+ continue;
581
+ }
582
+ if (glob && !glob.test(filePath)) {
583
+ continue;
584
+ }
585
+ const pathIndex = match === "regex"
586
+ ? matchRegexIndex(filePath, regexPattern)
587
+ : findMatchIndex(filePath, query, match);
588
+ if (pathIndex < 0) {
589
+ continue;
590
+ }
591
+ onHit({
592
+ filePath,
593
+ score: scorePathMatch(match, pathIndex),
594
+ matchedIn: "path",
595
+ reasonCodes: ["path_match", `path_${match}`]
596
+ });
597
+ }
598
+ if (!page.nextCursor) {
599
+ break;
600
+ }
601
+ cursor = page.nextCursor;
602
+ }
603
+ }
604
+ export function findSymbolHits(svc, artifactId, query, match, scope, regexPattern) {
605
+ if (match !== "regex") {
606
+ const filePathLike = scope?.fileGlob ? globToSqlLike(normalizePathStyle(scope.fileGlob)) : undefined;
607
+ const scoped = svc.symbolsRepo.findScopedSymbols({
608
+ artifactId,
609
+ query,
610
+ match,
611
+ symbolKind: scope?.symbolKind,
612
+ packagePrefix: scope?.packagePrefix,
613
+ filePathLike,
614
+ limit: indexedCandidateLimit(svc)
615
+ });
616
+ svc.metrics.recordSearchDbRoundtrip();
617
+ svc.metrics.recordSearchRowsScanned(scoped.items.length);
618
+ const result = [];
619
+ for (const symbol of scoped.items) {
620
+ if (!isSymbolKind(symbol.symbolKind)) {
621
+ continue;
622
+ }
623
+ const index = findMatchIndex(symbol.symbolName, query, match);
624
+ if (index < 0) {
625
+ continue;
626
+ }
627
+ result.push({
628
+ symbol,
629
+ score: scoreSymbolMatch(match, index, symbol.symbolKind),
630
+ matchIndex: index
631
+ });
632
+ }
633
+ return result;
634
+ }
635
+ const candidates = svc.symbolsRepo.listSymbolsForArtifact(artifactId, scope?.symbolKind);
636
+ svc.metrics.recordSearchDbRoundtrip();
637
+ svc.metrics.recordSearchRowsScanned(candidates.length);
638
+ const result = [];
639
+ const glob = scope?.fileGlob ? buildGlobRegex(normalizePathStyle(scope.fileGlob)) : undefined;
640
+ for (const symbol of candidates) {
641
+ if (!checkPackagePrefix(symbol.filePath, scope?.packagePrefix)) {
642
+ continue;
643
+ }
644
+ if (glob && !glob.test(symbol.filePath)) {
645
+ continue;
646
+ }
647
+ if (!isSymbolKind(symbol.symbolKind)) {
648
+ continue;
649
+ }
650
+ const index = match === "regex"
651
+ ? matchRegexIndex(symbol.symbolName, regexPattern)
652
+ : findMatchIndex(symbol.symbolName, query, match);
653
+ if (index < 0) {
654
+ continue;
655
+ }
656
+ result.push({
657
+ symbol,
658
+ score: scoreSymbolMatch(match, index, symbol.symbolKind),
659
+ matchIndex: index
660
+ });
661
+ }
662
+ return result;
663
+ }
664
+ export function indexedCandidateLimit(svc) {
665
+ return Math.min(Math.max(svc.config.maxSearchHits * 5, 500), 5000);
666
+ }
667
+ export function indexedCandidateLimitForMatch(svc, match) {
668
+ const base = indexedCandidateLimit(svc);
669
+ if (match === "exact" || match === "prefix") {
670
+ // Exact/prefix matches are more selective — fewer candidates needed
671
+ return Math.min(base, 500);
672
+ }
673
+ // Contains matches need more candidates
674
+ return base;
675
+ }
676
+ //# sourceMappingURL=search.js.map
@@ -0,0 +1,6 @@
1
+ import type { SourceMapping } from "../types.js";
2
+ export declare function normalizePathStyle(path: string): string;
3
+ export declare function normalizeOptionalString(value: string | undefined): string | undefined;
4
+ export declare function normalizeMapping(mapping: SourceMapping | undefined): SourceMapping;
5
+ export declare function pathExists(filePath: string): Promise<boolean>;
6
+ export declare function dedupeQualityFlags(qualityFlags: readonly string[]): string[];