@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.
- package/CHANGELOG.md +47 -0
- package/README.md +36 -23
- package/dist/build-suggested-call.d.ts +29 -0
- package/dist/build-suggested-call.js +58 -0
- package/dist/cache-registry.d.ts +3 -1
- package/dist/cache-registry.js +50 -6
- package/dist/entry-tools/analyze-symbol-service.d.ts +16 -16
- package/dist/entry-tools/batch-class-members-service.d.ts +34 -0
- package/dist/entry-tools/batch-class-members-service.js +97 -0
- package/dist/entry-tools/batch-class-source-service.d.ts +37 -0
- package/dist/entry-tools/batch-class-source-service.js +100 -0
- package/dist/entry-tools/batch-mappings-service.d.ts +36 -0
- package/dist/entry-tools/batch-mappings-service.js +66 -0
- package/dist/entry-tools/batch-runner.d.ts +72 -0
- package/dist/entry-tools/batch-runner.js +90 -0
- package/dist/entry-tools/batch-symbol-exists-service.d.ts +46 -0
- package/dist/entry-tools/batch-symbol-exists-service.js +113 -0
- package/dist/entry-tools/compare-minecraft-service.d.ts +6 -6
- package/dist/entry-tools/inspect-minecraft/handlers/artifact.d.ts +5 -0
- package/dist/entry-tools/inspect-minecraft/handlers/artifact.js +83 -0
- package/dist/entry-tools/inspect-minecraft/handlers/class-members.d.ts +6 -0
- package/dist/entry-tools/inspect-minecraft/handlers/class-members.js +80 -0
- package/dist/entry-tools/inspect-minecraft/handlers/class-overview.d.ts +5 -0
- package/dist/entry-tools/inspect-minecraft/handlers/class-overview.js +248 -0
- package/dist/entry-tools/inspect-minecraft/handlers/class-source.d.ts +5 -0
- package/dist/entry-tools/inspect-minecraft/handlers/class-source.js +60 -0
- package/dist/entry-tools/inspect-minecraft/handlers/file.d.ts +5 -0
- package/dist/entry-tools/inspect-minecraft/handlers/file.js +54 -0
- package/dist/entry-tools/inspect-minecraft/handlers/list-files.d.ts +5 -0
- package/dist/entry-tools/inspect-minecraft/handlers/list-files.js +100 -0
- package/dist/entry-tools/inspect-minecraft/handlers/search.d.ts +5 -0
- package/dist/entry-tools/inspect-minecraft/handlers/search.js +155 -0
- package/dist/entry-tools/inspect-minecraft/handlers/versions.d.ts +6 -0
- package/dist/entry-tools/inspect-minecraft/handlers/versions.js +49 -0
- package/dist/entry-tools/inspect-minecraft/internal.d.ts +1042 -0
- package/dist/entry-tools/inspect-minecraft/internal.js +448 -0
- package/dist/entry-tools/inspect-minecraft-service.d.ts +193 -308
- package/dist/entry-tools/inspect-minecraft-service.js +20 -1244
- package/dist/entry-tools/manage-cache-service.d.ts +16 -16
- package/dist/entry-tools/validate-project/cases/access-transformer.d.ts +6 -0
- package/dist/entry-tools/validate-project/cases/access-transformer.js +106 -0
- package/dist/entry-tools/validate-project/cases/access-widener.d.ts +6 -0
- package/dist/entry-tools/validate-project/cases/access-widener.js +86 -0
- package/dist/entry-tools/validate-project/cases/mixin.d.ts +6 -0
- package/dist/entry-tools/validate-project/cases/mixin.js +90 -0
- package/dist/entry-tools/validate-project/cases/project-summary.d.ts +97 -0
- package/dist/entry-tools/validate-project/cases/project-summary.js +346 -0
- package/dist/entry-tools/validate-project/internal.d.ts +135 -0
- package/dist/entry-tools/validate-project/internal.js +287 -0
- package/dist/entry-tools/validate-project-service.d.ts +63 -47
- package/dist/entry-tools/validate-project-service.js +12 -562
- package/dist/entry-tools/verify-mixin-target-service.d.ts +133 -0
- package/dist/entry-tools/verify-mixin-target-service.js +323 -0
- package/dist/error-mapping.d.ts +40 -0
- package/dist/error-mapping.js +139 -0
- package/dist/errors.d.ts +6 -0
- package/dist/errors.js +6 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +142 -1352
- package/dist/mapping/internal-types.d.ts +54 -0
- package/dist/mapping/internal-types.js +14 -0
- package/dist/mapping/loaders/mojang.d.ts +2 -0
- package/dist/mapping/loaders/mojang.js +64 -0
- package/dist/mapping/loaders/tiny-loom.d.ts +2 -0
- package/dist/mapping/loaders/tiny-loom.js +73 -0
- package/dist/mapping/loaders/tiny-maven.d.ts +2 -0
- package/dist/mapping/loaders/tiny-maven.js +104 -0
- package/dist/mapping/loaders/types.d.ts +14 -0
- package/dist/mapping/loaders/types.js +2 -0
- package/dist/mapping/lookup.d.ts +52 -0
- package/dist/mapping/lookup.js +496 -0
- package/dist/mapping/parsers/normalize.d.ts +10 -0
- package/dist/mapping/parsers/normalize.js +52 -0
- package/dist/mapping/parsers/proguard.d.ts +20 -0
- package/dist/mapping/parsers/proguard.js +138 -0
- package/dist/mapping/parsers/symbol-records.d.ts +27 -0
- package/dist/mapping/parsers/symbol-records.js +216 -0
- package/dist/mapping/parsers/tiny.d.ts +9 -0
- package/dist/mapping/parsers/tiny.js +96 -0
- package/dist/mapping/types.d.ts +147 -0
- package/dist/mapping/types.js +2 -0
- package/dist/mapping-pipeline-service.js +3 -2
- package/dist/mapping-service.d.ts +3 -144
- package/dist/mapping-service.js +19 -1201
- package/dist/mixin/access-validators.d.ts +9 -0
- package/dist/mixin/access-validators.js +257 -0
- package/dist/mixin/annotation-validators.d.ts +5 -0
- package/dist/mixin/annotation-validators.js +162 -0
- package/dist/mixin/helpers.d.ts +28 -0
- package/dist/mixin/helpers.js +315 -0
- package/dist/mixin/parsed-validator.d.ts +8 -0
- package/dist/mixin/parsed-validator.js +337 -0
- package/dist/mixin/types.d.ts +208 -0
- package/dist/mixin/types.js +28 -0
- package/dist/mixin-validator.d.ts +9 -201
- package/dist/mixin-validator.js +8 -1020
- package/dist/source/access-validate.d.ts +4 -0
- package/dist/source/access-validate.js +254 -0
- package/dist/source/artifact-resolver.d.ts +110 -0
- package/dist/source/artifact-resolver.js +1174 -0
- package/dist/source/cache-metrics.d.ts +26 -0
- package/dist/source/cache-metrics.js +172 -0
- package/dist/source/class-source/members-builder.d.ts +34 -0
- package/dist/source/class-source/members-builder.js +46 -0
- package/dist/source/class-source/snippet-builder.d.ts +19 -0
- package/dist/source/class-source/snippet-builder.js +46 -0
- package/dist/source/class-source-helpers.d.ts +34 -0
- package/dist/source/class-source-helpers.js +140 -0
- package/dist/source/class-source.d.ts +42 -0
- package/dist/source/class-source.js +883 -0
- package/dist/source/descriptor-utils.d.ts +6 -0
- package/dist/source/descriptor-utils.js +37 -0
- package/dist/source/file-access.d.ts +4 -0
- package/dist/source/file-access.js +102 -0
- package/dist/source/indexer.d.ts +82 -0
- package/dist/source/indexer.js +505 -0
- package/dist/source/lifecycle/diff-utils.d.ts +9 -0
- package/dist/source/lifecycle/diff-utils.js +107 -0
- package/dist/source/lifecycle/diff.d.ts +2 -0
- package/dist/source/lifecycle/diff.js +265 -0
- package/dist/source/lifecycle/mapping-helpers.d.ts +22 -0
- package/dist/source/lifecycle/mapping-helpers.js +327 -0
- package/dist/source/lifecycle/runtime-check.d.ts +2 -0
- package/dist/source/lifecycle/runtime-check.js +142 -0
- package/dist/source/lifecycle/trace.d.ts +2 -0
- package/dist/source/lifecycle/trace.js +231 -0
- package/dist/source/lifecycle.d.ts +4 -0
- package/dist/source/lifecycle.js +5 -0
- package/dist/source/search.d.ts +51 -0
- package/dist/source/search.js +676 -0
- package/dist/source/shared-utils.d.ts +6 -0
- package/dist/source/shared-utils.js +55 -0
- package/dist/source/state.d.ts +21 -0
- package/dist/source/state.js +19 -0
- package/dist/source/symbol-resolver.d.ts +3 -0
- package/dist/source/symbol-resolver.js +212 -0
- package/dist/source/validate-mixin/pipeline/mapping-health.d.ts +3 -0
- package/dist/source/validate-mixin/pipeline/mapping-health.js +41 -0
- package/dist/source/validate-mixin/pipeline/parse.d.ts +2 -0
- package/dist/source/validate-mixin/pipeline/parse.js +10 -0
- package/dist/source/validate-mixin/pipeline/resolve.d.ts +3 -0
- package/dist/source/validate-mixin/pipeline/resolve.js +78 -0
- package/dist/source/validate-mixin/pipeline/target-lookup.d.ts +6 -0
- package/dist/source/validate-mixin/pipeline/target-lookup.js +260 -0
- package/dist/source/validate-mixin/pipeline-context.d.ts +72 -0
- package/dist/source/validate-mixin/pipeline-context.js +93 -0
- package/dist/source/validate-mixin.d.ts +22 -0
- package/dist/source/validate-mixin.js +799 -0
- package/dist/source/workspace-target.d.ts +18 -0
- package/dist/source/workspace-target.js +305 -0
- package/dist/source-service.d.ts +147 -170
- package/dist/source-service.js +67 -6116
- package/dist/stage-emitter.d.ts +13 -0
- package/dist/stage-emitter.js +30 -0
- package/dist/stdio-supervisor.d.ts +61 -0
- package/dist/stdio-supervisor.js +326 -9
- package/dist/tool-contract-manifest.d.ts +1 -1
- package/dist/tool-contract-manifest.js +23 -6
- package/dist/tool-guidance.d.ts +82 -0
- package/dist/tool-guidance.js +734 -0
- package/dist/tool-schema-registry.d.ts +16 -0
- package/dist/tool-schema-registry.js +37 -0
- package/dist/tool-schemas.d.ts +3518 -0
- package/dist/tool-schemas.js +813 -0
- package/dist/types.d.ts +36 -0
- package/dist/version-service.js +7 -6
- package/dist/workspace-context-cache.d.ts +32 -0
- package/dist/workspace-context-cache.js +66 -0
- package/dist/workspace-mapping-service.d.ts +16 -0
- package/dist/workspace-mapping-service.js +173 -1
- package/docs/README-ja.md +414 -0
- package/docs/examples.md +483 -0
- package/docs/tool-reference.md +459 -0
- 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[];
|