@mantine/mcp-server 9.6.3 → 9.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -4
- package/cjs/data-client.cjs +203 -73
- package/cjs/data-client.cjs.map +1 -1
- package/cjs/search.cjs +133 -0
- package/cjs/search.cjs.map +1 -0
- package/cjs/server.cjs +11 -234
- package/cjs/server.cjs.map +1 -1
- package/cjs/tools.cjs +108 -0
- package/cjs/tools.cjs.map +1 -0
- package/esm/data-client.mjs +203 -73
- package/esm/data-client.mjs.map +1 -1
- package/esm/search.mjs +132 -0
- package/esm/search.mjs.map +1 -0
- package/esm/server.mjs +11 -232
- package/esm/server.mjs.map +1 -1
- package/esm/tools.mjs +106 -0
- package/esm/tools.mjs.map +1 -0
- package/lib/data-client.d.ts +114 -35
- package/lib/search.d.ts +17 -0
- package/lib/server.d.ts +1 -0
- package/lib/tools.d.ts +4 -0
- package/lib/types.d.ts +51 -17
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -15,7 +15,12 @@ npx @mantine/mcp-server
|
|
|
15
15
|
|
|
16
16
|
## Tools
|
|
17
17
|
|
|
18
|
-
- `
|
|
19
|
-
- `get_item_doc`
|
|
20
|
-
- `get_item_props`
|
|
21
|
-
- `
|
|
18
|
+
- `search_docs` – search all documentation; returns ranked matches, often pointing at a specific section
|
|
19
|
+
- `get_item_doc` – outline by default, one section with `section`, whole page with `full: true`
|
|
20
|
+
- `get_item_props` – component props, or a hook's TypeScript signature
|
|
21
|
+
- `list_items` – filter by `kind`, `group`, `category`, `package` or `query`
|
|
22
|
+
- `get_api` – resolve a symbol (`useForm`, `useDisclosure`) to its signature
|
|
23
|
+
|
|
24
|
+
## Coverage
|
|
25
|
+
|
|
26
|
+
406 documents: components, hooks, `@mantine/form`, styling, theming, framework and migration guides, and FAQ pages. Changelogs are not indexed.
|
package/cjs/data-client.cjs
CHANGED
|
@@ -1,127 +1,257 @@
|
|
|
1
1
|
"use client";
|
|
2
|
+
const require_search = require("./search.cjs");
|
|
2
3
|
//#region packages/@mantine/mcp-server/src/data-client.ts
|
|
3
|
-
function
|
|
4
|
+
function normalize(value) {
|
|
4
5
|
return value.trim().toLowerCase();
|
|
5
6
|
}
|
|
6
7
|
function clampLimit(limit, fallback, max) {
|
|
7
8
|
if (typeof limit !== "number" || Number.isNaN(limit)) return fallback;
|
|
8
9
|
return Math.max(1, Math.min(max, Math.floor(limit)));
|
|
9
10
|
}
|
|
11
|
+
const INDEX_REVALIDATE_INTERVAL_MS = 3e5;
|
|
12
|
+
/** Fetches and caches the static MCP data published alongside the docs site */
|
|
10
13
|
var MantineMcpDataClient = class {
|
|
11
14
|
constructor(baseUrl = "https://mantine.dev/mcp", timeoutMs = 1e4) {
|
|
12
15
|
this.indexCache = null;
|
|
13
|
-
this.
|
|
16
|
+
this.indexEtag = null;
|
|
17
|
+
this.indexCheckedAt = 0;
|
|
18
|
+
this.sectionsCache = null;
|
|
19
|
+
this.docCache = /* @__PURE__ */ new Map();
|
|
20
|
+
this.scorer = null;
|
|
14
21
|
this.baseUrl = baseUrl.replace(/\/+$/, "");
|
|
15
22
|
this.timeoutMs = timeoutMs;
|
|
16
23
|
}
|
|
17
|
-
async fetchJson(relativePath) {
|
|
24
|
+
async fetchJson(relativePath, etag) {
|
|
18
25
|
const controller = new AbortController();
|
|
19
26
|
const timer = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
20
27
|
try {
|
|
21
28
|
const url = `${this.baseUrl}/${relativePath.replace(/^\/+/, "")}`;
|
|
22
|
-
const
|
|
29
|
+
const headers = etag ? { "If-None-Match": etag } : void 0;
|
|
30
|
+
const response = await fetch(url, {
|
|
31
|
+
signal: controller.signal,
|
|
32
|
+
headers
|
|
33
|
+
});
|
|
34
|
+
if (response.status === 304) return {
|
|
35
|
+
data: null,
|
|
36
|
+
etag
|
|
37
|
+
};
|
|
23
38
|
if (!response.ok) throw new Error(`Failed to fetch ${url}: ${response.status}`);
|
|
24
|
-
return
|
|
39
|
+
return {
|
|
40
|
+
data: await response.json(),
|
|
41
|
+
etag: response.headers.get("etag")
|
|
42
|
+
};
|
|
25
43
|
} finally {
|
|
26
44
|
clearTimeout(timer);
|
|
27
45
|
}
|
|
28
46
|
}
|
|
47
|
+
/**
|
|
48
|
+
* Re-fetches the index if the published copy changed since it was cached.
|
|
49
|
+
*
|
|
50
|
+
* Best-effort: a revalidation failure (network blip, non-ok response) is logged and
|
|
51
|
+
* swallowed so the still-good cached index keeps serving the in-flight request instead of
|
|
52
|
+
* failing it outright.
|
|
53
|
+
*/
|
|
54
|
+
async revalidateIndex() {
|
|
55
|
+
try {
|
|
56
|
+
const { data, etag } = await this.fetchJson("index.json", this.indexEtag);
|
|
57
|
+
if (data) {
|
|
58
|
+
this.indexCache = data;
|
|
59
|
+
this.indexEtag = etag ?? null;
|
|
60
|
+
this.sectionsCache = null;
|
|
61
|
+
this.docCache.clear();
|
|
62
|
+
this.scorer = null;
|
|
63
|
+
}
|
|
64
|
+
} catch (error) {
|
|
65
|
+
console.error("Failed to revalidate Mantine MCP index, serving cached data", error);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
29
68
|
async getIndex() {
|
|
30
|
-
if (this.indexCache)
|
|
31
|
-
|
|
69
|
+
if (!this.indexCache) {
|
|
70
|
+
const { data, etag } = await this.fetchJson("index.json");
|
|
71
|
+
this.indexCache = data ?? [];
|
|
72
|
+
this.indexEtag = etag ?? null;
|
|
73
|
+
this.indexCheckedAt = Date.now();
|
|
74
|
+
return this.indexCache;
|
|
75
|
+
}
|
|
76
|
+
const now = Date.now();
|
|
77
|
+
if (now - this.indexCheckedAt >= INDEX_REVALIDATE_INTERVAL_MS) {
|
|
78
|
+
this.indexCheckedAt = now;
|
|
79
|
+
await this.revalidateIndex();
|
|
80
|
+
}
|
|
32
81
|
return this.indexCache;
|
|
33
82
|
}
|
|
83
|
+
async getSections() {
|
|
84
|
+
if (this.sectionsCache) return this.sectionsCache;
|
|
85
|
+
const { data } = await this.fetchJson("sections.json");
|
|
86
|
+
this.sectionsCache = data ?? [];
|
|
87
|
+
return this.sectionsCache;
|
|
88
|
+
}
|
|
89
|
+
async getScorer() {
|
|
90
|
+
if (this.scorer) return this.scorer;
|
|
91
|
+
const index = await this.getIndex();
|
|
92
|
+
const sections = await this.getSections();
|
|
93
|
+
this.scorer = require_search.createSearchScorer(index, sections);
|
|
94
|
+
return this.scorer;
|
|
95
|
+
}
|
|
96
|
+
async getDocData(id) {
|
|
97
|
+
const cached = this.docCache.get(id);
|
|
98
|
+
if (cached) return cached;
|
|
99
|
+
const { data } = await this.fetchJson(`docs/${id}.json`);
|
|
100
|
+
if (!data) throw new Error(`Missing document data for ${id}`);
|
|
101
|
+
this.docCache.set(id, data);
|
|
102
|
+
return data;
|
|
103
|
+
}
|
|
34
104
|
async listItems(args = {}) {
|
|
35
|
-
const
|
|
36
|
-
const query = args.query ?
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
105
|
+
const limit = clampLimit(args.limit, 30, 500);
|
|
106
|
+
const query = args.query ? normalize(args.query) : null;
|
|
107
|
+
return (await this.getIndex()).filter((item) => args.kind ? item.kind === args.kind : true).filter((item) => args.group ? item.group === args.group : true).filter((item) => args.category ? normalize(item.category) === normalize(args.category) : true).filter((item) => args.package ? normalize(item.package || "") === normalize(args.package) : true).filter((item) => query ? item.searchText.includes(query) : true).slice(0, limit);
|
|
108
|
+
}
|
|
109
|
+
async findItem(args) {
|
|
110
|
+
const requested = normalize(args.name);
|
|
111
|
+
const index = await this.getIndex();
|
|
112
|
+
const candidates = args.kind ? index.filter((item) => item.kind === args.kind) : index;
|
|
113
|
+
return candidates.find((item) => normalize(item.id) === requested) || candidates.find((item) => normalize(item.name) === requested) || candidates.find((item) => normalize(item.name).startsWith(requested)) || candidates.find((item) => item.searchText.includes(requested)) || null;
|
|
43
114
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
if (normalizedName === normalizedQuery) return 100;
|
|
48
|
-
if (normalizedName.startsWith(normalizedQuery)) return 80;
|
|
49
|
-
if (normalizedName.includes(normalizedQuery)) return 70;
|
|
50
|
-
if (normalizeValue(item.searchText).includes(normalizedQuery)) return 40;
|
|
51
|
-
const tokens = normalizedQuery.split(/\s+/).filter(Boolean);
|
|
52
|
-
const matchedTokens = tokens.filter((token) => normalizeValue(item.searchText).includes(token));
|
|
53
|
-
if (tokens.length > 0 && matchedTokens.length > 0) return 10 + matchedTokens.length / tokens.length * 20;
|
|
54
|
-
return 0;
|
|
115
|
+
/** Names close to a failed lookup, for a helpful error message */
|
|
116
|
+
async suggestFor(name) {
|
|
117
|
+
return require_search.suggestNames(await this.getIndex(), name);
|
|
55
118
|
}
|
|
56
|
-
async
|
|
57
|
-
const kind = args.kind || "component";
|
|
119
|
+
async search(args) {
|
|
58
120
|
const limit = clampLimit(args.limit, 20, 100);
|
|
59
121
|
const query = args.query.trim();
|
|
60
122
|
if (!query) return [];
|
|
61
|
-
|
|
123
|
+
const index = await this.getIndex();
|
|
124
|
+
const sections = await this.getSections();
|
|
125
|
+
const scorer = await this.getScorer();
|
|
126
|
+
const allowed = index.filter((item) => args.kind ? item.kind === args.kind : true).filter((item) => args.package ? normalize(item.package || "") === normalize(args.package) : true);
|
|
127
|
+
const allowedIds = new Map(allowed.map((item) => [item.id, item]));
|
|
128
|
+
const sectionHits = sections.filter((section) => allowedIds.has(section.id)).map((section) => ({
|
|
129
|
+
section,
|
|
130
|
+
score: scorer.scoreSection(section, query)
|
|
131
|
+
})).filter((entry) => entry.score > 0);
|
|
132
|
+
const pageHits = allowed.map((item) => ({
|
|
62
133
|
item,
|
|
63
|
-
score:
|
|
64
|
-
})).filter((entry) => entry.score > 0)
|
|
134
|
+
score: scorer.scoreItem(item, query)
|
|
135
|
+
})).filter((entry) => entry.score > 0);
|
|
136
|
+
const merged = /* @__PURE__ */ new Map();
|
|
137
|
+
pageHits.forEach((hit) => {
|
|
138
|
+
merged.set(hit.item.id, {
|
|
139
|
+
item: hit.item,
|
|
140
|
+
score: hit.score
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
sectionHits.forEach((hit) => {
|
|
144
|
+
const item = allowedIds.get(hit.section.id);
|
|
145
|
+
const key = `${hit.section.id}#${hit.section.slug}`;
|
|
146
|
+
merged.set(key, {
|
|
147
|
+
item,
|
|
148
|
+
section: hit.section,
|
|
149
|
+
score: hit.score
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
return Array.from(merged.values()).sort((a, b) => b.score - a.score).slice(0, limit).map((entry) => ({
|
|
65
153
|
id: entry.item.id,
|
|
66
154
|
name: entry.item.name,
|
|
67
155
|
kind: entry.item.kind,
|
|
68
156
|
package: entry.item.package,
|
|
69
157
|
route: entry.item.route,
|
|
158
|
+
docsUrl: entry.item.docsUrl,
|
|
70
159
|
description: entry.item.description,
|
|
160
|
+
section: entry.section?.slug,
|
|
161
|
+
heading: entry.section?.heading,
|
|
162
|
+
snippet: entry.section?.snippet,
|
|
71
163
|
score: Number(entry.score.toFixed(2))
|
|
72
164
|
}));
|
|
73
165
|
}
|
|
74
|
-
async
|
|
75
|
-
const cached = this.itemCache.get(id);
|
|
76
|
-
if (cached) return cached;
|
|
77
|
-
const data = await this.fetchJson(`components/${id}.json`);
|
|
78
|
-
this.itemCache.set(id, data);
|
|
79
|
-
return data;
|
|
80
|
-
}
|
|
81
|
-
async findItem(args) {
|
|
82
|
-
const kind = args.kind || "component";
|
|
83
|
-
const requested = normalizeValue(args.name);
|
|
84
|
-
const byKind = (await this.getIndex()).filter((item) => item.kind === kind);
|
|
85
|
-
const exactId = byKind.find((item) => normalizeValue(item.id) === requested);
|
|
86
|
-
if (exactId) return exactId;
|
|
87
|
-
const exactName = byKind.find((item) => normalizeValue(item.name) === requested);
|
|
88
|
-
if (exactName) return exactName;
|
|
89
|
-
const prefixName = byKind.find((item) => normalizeValue(item.name).startsWith(requested));
|
|
90
|
-
if (prefixName) return prefixName;
|
|
91
|
-
return byKind.find((item) => normalizeValue(item.searchText).includes(requested)) || null;
|
|
92
|
-
}
|
|
93
|
-
async getItemDoc(args) {
|
|
166
|
+
async getDoc(args) {
|
|
94
167
|
const item = await this.findItem(args);
|
|
95
168
|
if (!item) return null;
|
|
96
|
-
const data = await this.
|
|
169
|
+
const data = await this.getDocData(item.id);
|
|
170
|
+
if (args.full) return {
|
|
171
|
+
item,
|
|
172
|
+
mode: "full",
|
|
173
|
+
markdown: [data.intro, ...data.sections.map((s) => `## ${s.heading}\n\n${s.body}`)].join("\n\n")
|
|
174
|
+
};
|
|
175
|
+
if (args.section) {
|
|
176
|
+
const requested = normalize(args.section);
|
|
177
|
+
const section = data.sections.find((s) => s.slug === requested) || data.sections.find((s) => normalize(s.heading) === requested);
|
|
178
|
+
if (!section) return {
|
|
179
|
+
item,
|
|
180
|
+
mode: "section-not-found",
|
|
181
|
+
availableSections: data.sections.map((s) => s.slug)
|
|
182
|
+
};
|
|
183
|
+
return {
|
|
184
|
+
item,
|
|
185
|
+
mode: "section",
|
|
186
|
+
heading: section.heading,
|
|
187
|
+
markdown: section.body
|
|
188
|
+
};
|
|
189
|
+
}
|
|
97
190
|
return {
|
|
98
|
-
item
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
markdown: data.markdown
|
|
191
|
+
item,
|
|
192
|
+
mode: "outline",
|
|
193
|
+
intro: data.intro,
|
|
194
|
+
sections: data.sections.map((s) => ({
|
|
195
|
+
slug: s.slug,
|
|
196
|
+
heading: s.heading
|
|
197
|
+
})),
|
|
198
|
+
source: data.source,
|
|
199
|
+
docs: data.docs
|
|
108
200
|
};
|
|
109
201
|
}
|
|
110
|
-
|
|
202
|
+
/** The section that most plausibly documents a page's API, when no structured props or signature exist */
|
|
203
|
+
findApiSection(sections) {
|
|
204
|
+
return sections.find((s) => /api|return type|overview/i.test(s.heading));
|
|
205
|
+
}
|
|
206
|
+
async getProps(args) {
|
|
111
207
|
const item = await this.findItem(args);
|
|
112
208
|
if (!item) return null;
|
|
113
|
-
const data = await this.
|
|
114
|
-
return {
|
|
115
|
-
item
|
|
116
|
-
|
|
117
|
-
name: data.name,
|
|
118
|
-
kind: data.kind,
|
|
119
|
-
package: data.package,
|
|
120
|
-
route: data.route,
|
|
121
|
-
description: data.description
|
|
122
|
-
},
|
|
209
|
+
const data = await this.getDocData(item.id);
|
|
210
|
+
if (data.props.length > 0) return {
|
|
211
|
+
item,
|
|
212
|
+
kind: "props",
|
|
123
213
|
props: data.props
|
|
124
214
|
};
|
|
215
|
+
if (data.signature) return {
|
|
216
|
+
item,
|
|
217
|
+
kind: "signature",
|
|
218
|
+
signature: data.signature,
|
|
219
|
+
exportedTypeNames: data.exportedTypeNames
|
|
220
|
+
};
|
|
221
|
+
const apiSection = this.findApiSection(data.sections);
|
|
222
|
+
return {
|
|
223
|
+
item,
|
|
224
|
+
kind: "sections",
|
|
225
|
+
message: `${item.name} has no generated props or signature. Use get_item_doc with a section instead.`,
|
|
226
|
+
suggestedSection: apiSection?.slug,
|
|
227
|
+
availableSections: data.sections.map((s) => s.slug)
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
async getApi(symbol) {
|
|
231
|
+
const index = await this.getIndex();
|
|
232
|
+
const requested = normalize(symbol);
|
|
233
|
+
const candidates = index.filter((item) => item.hasSignature || item.propsCount > 0 || item.kind === "hook");
|
|
234
|
+
const match = candidates.find((item) => normalize(item.name).replace(/-/g, "") === requested.replace(/-/g, "")) || candidates.find((item) => item.searchText.includes(requested));
|
|
235
|
+
if (!match) return null;
|
|
236
|
+
const data = await this.getDocData(match.id);
|
|
237
|
+
const hasExportedTypeNames = !!data.exportedTypeNames && data.exportedTypeNames.length > 0;
|
|
238
|
+
if (!data.signature && !hasExportedTypeNames) {
|
|
239
|
+
const apiSection = this.findApiSection(data.sections);
|
|
240
|
+
return {
|
|
241
|
+
item: match,
|
|
242
|
+
signature: data.signature,
|
|
243
|
+
exportedTypeNames: data.exportedTypeNames,
|
|
244
|
+
propsCount: data.props.length,
|
|
245
|
+
suggestedSection: apiSection?.slug,
|
|
246
|
+
availableSections: data.sections.map((s) => s.slug)
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
return {
|
|
250
|
+
item: match,
|
|
251
|
+
signature: data.signature,
|
|
252
|
+
exportedTypeNames: data.exportedTypeNames,
|
|
253
|
+
propsCount: data.props.length
|
|
254
|
+
};
|
|
125
255
|
}
|
|
126
256
|
};
|
|
127
257
|
//#endregion
|
package/cjs/data-client.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"data-client.cjs","names":[],"sources":["../src/data-client.ts"],"sourcesContent":["import { GetItemArgs, IndexItem, ItemData, ItemKind, ListItemsArgs, SearchDocsArgs } from './types';\n\nfunction normalizeValue(value: string) {\n return value.trim().toLowerCase();\n}\n\nfunction clampLimit(limit: number | undefined, fallback: number, max: number) {\n if (typeof limit !== 'number' || Number.isNaN(limit)) {\n return fallback;\n }\n\n return Math.max(1, Math.min(max, Math.floor(limit)));\n}\n\nexport class MantineMcpDataClient {\n private readonly baseUrl: string;\n private readonly timeoutMs: number;\n private indexCache: IndexItem[] | null = null;\n private itemCache = new Map<string, ItemData>();\n\n constructor(baseUrl = 'https://mantine.dev/mcp', timeoutMs = 10000) {\n this.baseUrl = baseUrl.replace(/\\/+$/, '');\n this.timeoutMs = timeoutMs;\n }\n\n private async fetchJson<T>(relativePath: string): Promise<T> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.timeoutMs);\n\n try {\n const url = `${this.baseUrl}/${relativePath.replace(/^\\/+/, '')}`;\n const response = await fetch(url, { signal: controller.signal });\n\n if (!response.ok) {\n throw new Error(`Failed to fetch ${url}: ${response.status}`);\n }\n\n return (await response.json()) as T;\n } finally {\n clearTimeout(timer);\n }\n }\n\n async getIndex() {\n if (this.indexCache) {\n return this.indexCache;\n }\n\n this.indexCache = await this.fetchJson<IndexItem[]>('index.json');\n return this.indexCache;\n }\n\n async listItems(args: ListItemsArgs = {}) {\n const kind = args.kind || 'component';\n const query = args.query ? normalizeValue(args.query) : null;\n const packageName = args.package ? normalizeValue(args.package) : null;\n const limit = clampLimit(args.limit, 30, 200);\n\n const index = await this.getIndex();\n\n return index\n .filter((item) => item.kind === kind)\n .filter((item) => (packageName ? normalizeValue(item.package) === packageName : true))\n .filter((item) => {\n if (!query) {\n return true;\n }\n\n return (\n normalizeValue(item.name).includes(query) ||\n normalizeValue(item.description).includes(query) ||\n normalizeValue(item.searchText).includes(query)\n );\n })\n .slice(0, limit);\n }\n\n private scoreMatch(item: IndexItem, query: string) {\n const normalizedQuery = normalizeValue(query);\n const normalizedName = normalizeValue(item.name);\n\n if (normalizedName === normalizedQuery) {\n return 100;\n }\n\n if (normalizedName.startsWith(normalizedQuery)) {\n return 80;\n }\n\n if (normalizedName.includes(normalizedQuery)) {\n return 70;\n }\n\n if (normalizeValue(item.searchText).includes(normalizedQuery)) {\n return 40;\n }\n\n const tokens = normalizedQuery.split(/\\s+/).filter(Boolean);\n const matchedTokens = tokens.filter((token) => normalizeValue(item.searchText).includes(token));\n\n if (tokens.length > 0 && matchedTokens.length > 0) {\n return 10 + (matchedTokens.length / tokens.length) * 20;\n }\n\n return 0;\n }\n\n async searchDocs(args: SearchDocsArgs) {\n const kind: ItemKind = args.kind || 'component';\n const limit = clampLimit(args.limit, 20, 100);\n const query = args.query.trim();\n\n if (!query) {\n return [];\n }\n\n const index = await this.getIndex();\n\n return index\n .filter((item) => item.kind === kind)\n .map((item) => ({ item, score: this.scoreMatch(item, query) }))\n .filter((entry) => entry.score > 0)\n .sort((a, b) => b.score - a.score)\n .slice(0, limit)\n .map((entry) => ({\n id: entry.item.id,\n name: entry.item.name,\n kind: entry.item.kind,\n package: entry.item.package,\n route: entry.item.route,\n description: entry.item.description,\n score: Number(entry.score.toFixed(2)),\n }));\n }\n\n private async getItemDataById(id: string): Promise<ItemData> {\n const cached = this.itemCache.get(id);\n if (cached) {\n return cached;\n }\n\n const data = await this.fetchJson<ItemData>(`components/${id}.json`);\n this.itemCache.set(id, data);\n return data;\n }\n\n async findItem(args: GetItemArgs) {\n const kind = args.kind || 'component';\n const requested = normalizeValue(args.name);\n const index = await this.getIndex();\n\n const byKind = index.filter((item) => item.kind === kind);\n\n const exactId = byKind.find((item) => normalizeValue(item.id) === requested);\n if (exactId) {\n return exactId;\n }\n\n const exactName = byKind.find((item) => normalizeValue(item.name) === requested);\n if (exactName) {\n return exactName;\n }\n\n const prefixName = byKind.find((item) => normalizeValue(item.name).startsWith(requested));\n if (prefixName) {\n return prefixName;\n }\n\n return byKind.find((item) => normalizeValue(item.searchText).includes(requested)) || null;\n }\n\n async getItemDoc(args: GetItemArgs) {\n const item = await this.findItem(args);\n if (!item) {\n return null;\n }\n\n const data = await this.getItemDataById(item.id);\n\n return {\n item: {\n id: data.id,\n name: data.name,\n kind: data.kind,\n package: data.package,\n route: data.route,\n description: data.description,\n llmUrl: data.llmUrl,\n },\n markdown: data.markdown,\n };\n }\n\n async getItemProps(args: GetItemArgs) {\n const item = await this.findItem(args);\n if (!item) {\n return null;\n }\n\n const data = await this.getItemDataById(item.id);\n\n return {\n item: {\n id: data.id,\n name: data.name,\n kind: data.kind,\n package: data.package,\n route: data.route,\n description: data.description,\n },\n props: data.props,\n };\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"data-client.cjs","names":["createSearchScorer","suggestNames"],"sources":["../src/data-client.ts"],"sourcesContent":["import { createSearchScorer, SearchScorer, suggestNames } from './search';\nimport {\n DocData,\n GetItemArgs,\n IndexItem,\n ListItemsArgs,\n SearchDocsArgs,\n SectionRecord,\n} from './types';\n\nfunction normalize(value: string): string {\n return value.trim().toLowerCase();\n}\n\nfunction clampLimit(limit: number | undefined, fallback: number, max: number): number {\n if (typeof limit !== 'number' || Number.isNaN(limit)) {\n return fallback;\n }\n\n return Math.max(1, Math.min(max, Math.floor(limit)));\n}\n\nconst INDEX_REVALIDATE_INTERVAL_MS = 5 * 60 * 1000;\n\n/** Fetches and caches the static MCP data published alongside the docs site */\nexport class MantineMcpDataClient {\n private readonly baseUrl: string;\n private readonly timeoutMs: number;\n private indexCache: IndexItem[] | null = null;\n private indexEtag: string | null = null;\n private indexCheckedAt = 0;\n private sectionsCache: SectionRecord[] | null = null;\n private docCache = new Map<string, DocData>();\n private scorer: SearchScorer | null = null;\n\n constructor(baseUrl = 'https://mantine.dev/mcp', timeoutMs = 10000) {\n this.baseUrl = baseUrl.replace(/\\/+$/, '');\n this.timeoutMs = timeoutMs;\n }\n\n private async fetchJson<T>(relativePath: string, etag?: string | null) {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.timeoutMs);\n\n try {\n const url = `${this.baseUrl}/${relativePath.replace(/^\\/+/, '')}`;\n const headers = etag ? { 'If-None-Match': etag } : undefined;\n const response = await fetch(url, { signal: controller.signal, headers });\n\n if (response.status === 304) {\n return { data: null as T | null, etag };\n }\n\n if (!response.ok) {\n throw new Error(`Failed to fetch ${url}: ${response.status}`);\n }\n\n return { data: (await response.json()) as T, etag: response.headers.get('etag') };\n } finally {\n clearTimeout(timer);\n }\n }\n\n /**\n * Re-fetches the index if the published copy changed since it was cached.\n *\n * Best-effort: a revalidation failure (network blip, non-ok response) is logged and\n * swallowed so the still-good cached index keeps serving the in-flight request instead of\n * failing it outright.\n */\n private async revalidateIndex(): Promise<void> {\n try {\n const { data, etag } = await this.fetchJson<IndexItem[]>('index.json', this.indexEtag);\n\n if (data) {\n this.indexCache = data;\n this.indexEtag = etag ?? null;\n this.sectionsCache = null;\n this.docCache.clear();\n this.scorer = null;\n }\n } catch (error) {\n // oxlint-disable-next-line no-console\n console.error('Failed to revalidate Mantine MCP index, serving cached data', error);\n }\n }\n\n async getIndex(): Promise<IndexItem[]> {\n if (!this.indexCache) {\n const { data, etag } = await this.fetchJson<IndexItem[]>('index.json');\n this.indexCache = data ?? [];\n this.indexEtag = etag ?? null;\n this.indexCheckedAt = Date.now();\n return this.indexCache;\n }\n\n const now = Date.now();\n if (now - this.indexCheckedAt >= INDEX_REVALIDATE_INTERVAL_MS) {\n this.indexCheckedAt = now;\n await this.revalidateIndex();\n }\n\n return this.indexCache;\n }\n\n async getSections(): Promise<SectionRecord[]> {\n if (this.sectionsCache) {\n return this.sectionsCache;\n }\n\n const { data } = await this.fetchJson<SectionRecord[]>('sections.json');\n this.sectionsCache = data ?? [];\n return this.sectionsCache;\n }\n\n private async getScorer(): Promise<SearchScorer> {\n if (this.scorer) {\n return this.scorer;\n }\n\n const index = await this.getIndex();\n const sections = await this.getSections();\n this.scorer = createSearchScorer(index, sections);\n return this.scorer;\n }\n\n private async getDocData(id: string): Promise<DocData> {\n const cached = this.docCache.get(id);\n if (cached) {\n return cached;\n }\n\n const { data } = await this.fetchJson<DocData>(`docs/${id}.json`);\n if (!data) {\n throw new Error(`Missing document data for ${id}`);\n }\n\n this.docCache.set(id, data);\n return data;\n }\n\n async listItems(args: ListItemsArgs = {}): Promise<IndexItem[]> {\n const limit = clampLimit(args.limit, 30, 500);\n const query = args.query ? normalize(args.query) : null;\n const index = await this.getIndex();\n\n return index\n .filter((item) => (args.kind ? item.kind === args.kind : true))\n .filter((item) => (args.group ? item.group === args.group : true))\n .filter((item) =>\n args.category ? normalize(item.category) === normalize(args.category) : true\n )\n .filter((item) =>\n args.package ? normalize(item.package || '') === normalize(args.package) : true\n )\n .filter((item) => (query ? item.searchText.includes(query) : true))\n .slice(0, limit);\n }\n\n async findItem(args: GetItemArgs): Promise<IndexItem | null> {\n const requested = normalize(args.name);\n const index = await this.getIndex();\n const candidates = args.kind ? index.filter((item) => item.kind === args.kind) : index;\n\n return (\n candidates.find((item) => normalize(item.id) === requested) ||\n candidates.find((item) => normalize(item.name) === requested) ||\n candidates.find((item) => normalize(item.name).startsWith(requested)) ||\n candidates.find((item) => item.searchText.includes(requested)) ||\n null\n );\n }\n\n /** Names close to a failed lookup, for a helpful error message */\n async suggestFor(name: string): Promise<string[]> {\n return suggestNames(await this.getIndex(), name);\n }\n\n async search(args: SearchDocsArgs) {\n const limit = clampLimit(args.limit, 20, 100);\n const query = args.query.trim();\n\n if (!query) {\n return [];\n }\n\n const index = await this.getIndex();\n const sections = await this.getSections();\n const scorer = await this.getScorer();\n\n const allowed = index\n .filter((item) => (args.kind ? item.kind === args.kind : true))\n .filter((item) =>\n args.package ? normalize(item.package || '') === normalize(args.package) : true\n );\n const allowedIds = new Map(allowed.map((item) => [item.id, item]));\n\n const sectionHits = sections\n .filter((section) => allowedIds.has(section.id))\n .map((section) => ({ section, score: scorer.scoreSection(section, query) }))\n .filter((entry) => entry.score > 0);\n\n const pageHits = allowed\n .map((item) => ({ item, score: scorer.scoreItem(item, query) }))\n .filter((entry) => entry.score > 0);\n\n const merged = new Map<string, { item: IndexItem; section?: SectionRecord; score: number }>();\n\n pageHits.forEach((hit) => {\n merged.set(hit.item.id, { item: hit.item, score: hit.score });\n });\n\n sectionHits.forEach((hit) => {\n const item = allowedIds.get(hit.section.id)!;\n const key = `${hit.section.id}#${hit.section.slug}`;\n merged.set(key, { item, section: hit.section, score: hit.score });\n });\n\n return Array.from(merged.values())\n .sort((a, b) => b.score - a.score)\n .slice(0, limit)\n .map((entry) => ({\n id: entry.item.id,\n name: entry.item.name,\n kind: entry.item.kind,\n package: entry.item.package,\n route: entry.item.route,\n docsUrl: entry.item.docsUrl,\n description: entry.item.description,\n section: entry.section?.slug,\n heading: entry.section?.heading,\n snippet: entry.section?.snippet,\n score: Number(entry.score.toFixed(2)),\n }));\n }\n\n async getDoc(args: GetItemArgs) {\n const item = await this.findItem(args);\n if (!item) {\n return null;\n }\n\n const data = await this.getDocData(item.id);\n\n if (args.full) {\n const body = [data.intro, ...data.sections.map((s) => `## ${s.heading}\\n\\n${s.body}`)].join(\n '\\n\\n'\n );\n return { item, mode: 'full' as const, markdown: body };\n }\n\n if (args.section) {\n const requested = normalize(args.section);\n const section =\n data.sections.find((s) => s.slug === requested) ||\n data.sections.find((s) => normalize(s.heading) === requested);\n\n if (!section) {\n return {\n item,\n mode: 'section-not-found' as const,\n availableSections: data.sections.map((s) => s.slug),\n };\n }\n\n return { item, mode: 'section' as const, heading: section.heading, markdown: section.body };\n }\n\n return {\n item,\n mode: 'outline' as const,\n intro: data.intro,\n sections: data.sections.map((s) => ({ slug: s.slug, heading: s.heading })),\n source: data.source,\n docs: data.docs,\n };\n }\n\n /** The section that most plausibly documents a page's API, when no structured props or signature exist */\n private findApiSection(sections: DocData['sections']) {\n return sections.find((s) => /api|return type|overview/i.test(s.heading));\n }\n\n async getProps(args: GetItemArgs) {\n const item = await this.findItem(args);\n if (!item) {\n return null;\n }\n\n const data = await this.getDocData(item.id);\n\n if (data.props.length > 0) {\n return { item, kind: 'props' as const, props: data.props };\n }\n\n if (data.signature) {\n return {\n item,\n kind: 'signature' as const,\n signature: data.signature,\n exportedTypeNames: data.exportedTypeNames,\n };\n }\n\n const apiSection = this.findApiSection(data.sections);\n\n return {\n item,\n kind: 'sections' as const,\n message: `${item.name} has no generated props or signature. Use get_item_doc with a section instead.`,\n suggestedSection: apiSection?.slug,\n availableSections: data.sections.map((s) => s.slug),\n };\n }\n\n async getApi(symbol: string) {\n const index = await this.getIndex();\n const requested = normalize(symbol);\n\n const candidates = index.filter(\n (item) => item.hasSignature || item.propsCount > 0 || item.kind === 'hook'\n );\n\n const match =\n candidates.find(\n (item) => normalize(item.name).replace(/-/g, '') === requested.replace(/-/g, '')\n ) || candidates.find((item) => item.searchText.includes(requested));\n\n if (!match) {\n return null;\n }\n\n const data = await this.getDocData(match.id);\n const hasExportedTypeNames = !!data.exportedTypeNames && data.exportedTypeNames.length > 0;\n\n if (!data.signature && !hasExportedTypeNames) {\n const apiSection = this.findApiSection(data.sections);\n\n return {\n item: match,\n signature: data.signature,\n exportedTypeNames: data.exportedTypeNames,\n propsCount: data.props.length,\n suggestedSection: apiSection?.slug,\n availableSections: data.sections.map((s) => s.slug),\n };\n }\n\n return {\n item: match,\n signature: data.signature,\n exportedTypeNames: data.exportedTypeNames,\n propsCount: data.props.length,\n };\n }\n}\n"],"mappings":"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"}
|
package/cjs/search.cjs
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
//#region packages/@mantine/mcp-server/src/search.ts
|
|
3
|
+
const STOPWORDS = /* @__PURE__ */ new Set([
|
|
4
|
+
"a",
|
|
5
|
+
"an",
|
|
6
|
+
"the",
|
|
7
|
+
"and",
|
|
8
|
+
"or",
|
|
9
|
+
"of",
|
|
10
|
+
"to",
|
|
11
|
+
"in",
|
|
12
|
+
"on",
|
|
13
|
+
"for",
|
|
14
|
+
"with",
|
|
15
|
+
"how",
|
|
16
|
+
"do",
|
|
17
|
+
"i",
|
|
18
|
+
"my",
|
|
19
|
+
"is",
|
|
20
|
+
"it",
|
|
21
|
+
"can",
|
|
22
|
+
"use",
|
|
23
|
+
"using",
|
|
24
|
+
"when",
|
|
25
|
+
"what",
|
|
26
|
+
"that",
|
|
27
|
+
"this",
|
|
28
|
+
"be",
|
|
29
|
+
"as",
|
|
30
|
+
"at",
|
|
31
|
+
"by",
|
|
32
|
+
"from",
|
|
33
|
+
"are",
|
|
34
|
+
"get"
|
|
35
|
+
]);
|
|
36
|
+
function normalize(value) {
|
|
37
|
+
return value.trim().toLowerCase();
|
|
38
|
+
}
|
|
39
|
+
function tokenize(value) {
|
|
40
|
+
return normalize(value).split(/[^a-z0-9.-]+/).filter(Boolean);
|
|
41
|
+
}
|
|
42
|
+
function queryTokens(query) {
|
|
43
|
+
const raw = tokenize(query);
|
|
44
|
+
const filtered = raw.filter((token) => token.length > 2 && !STOPWORDS.has(token));
|
|
45
|
+
return filtered.length > 0 ? filtered : raw;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Builds an IDF-weighted scorer over a fixed corpus.
|
|
49
|
+
*
|
|
50
|
+
* Document frequency is computed lazily per token and cached for the lifetime of the
|
|
51
|
+
* returned scorer, so a common token like "a" contributes almost nothing to a match while
|
|
52
|
+
* a rare token like "zod" dominates it. Callers should rebuild the scorer whenever `index`
|
|
53
|
+
* or `sections` actually change.
|
|
54
|
+
*/
|
|
55
|
+
function createSearchScorer(index, sections) {
|
|
56
|
+
const sectionDocs = sections.map((section) => normalize(`${section.heading} ${section.snippet}`));
|
|
57
|
+
const itemDocs = index.map((item) => normalize(item.searchText));
|
|
58
|
+
const corpus = [...sectionDocs, ...itemDocs];
|
|
59
|
+
const corpusSize = corpus.length;
|
|
60
|
+
const documentFrequencyCache = /* @__PURE__ */ new Map();
|
|
61
|
+
function documentFrequency(token) {
|
|
62
|
+
const cached = documentFrequencyCache.get(token);
|
|
63
|
+
if (cached !== void 0) return cached;
|
|
64
|
+
let count = 0;
|
|
65
|
+
for (const doc of corpus) if (doc.includes(token)) count += 1;
|
|
66
|
+
documentFrequencyCache.set(token, count);
|
|
67
|
+
return count;
|
|
68
|
+
}
|
|
69
|
+
function idf(token) {
|
|
70
|
+
return Math.log((corpusSize + 1) / (documentFrequency(token) + 1));
|
|
71
|
+
}
|
|
72
|
+
function coverage(haystack, tokens) {
|
|
73
|
+
if (tokens.length === 0) return 0;
|
|
74
|
+
let matchedWeight = 0;
|
|
75
|
+
let totalWeight = 0;
|
|
76
|
+
for (const token of tokens) {
|
|
77
|
+
const weight = idf(token);
|
|
78
|
+
totalWeight += weight;
|
|
79
|
+
if (haystack.includes(token)) matchedWeight += weight;
|
|
80
|
+
}
|
|
81
|
+
return totalWeight > 0 ? matchedWeight / totalWeight : 0;
|
|
82
|
+
}
|
|
83
|
+
return {
|
|
84
|
+
scoreItem(item, query) {
|
|
85
|
+
const q = normalize(query);
|
|
86
|
+
if (!q) return 0;
|
|
87
|
+
const tokens = queryTokens(query);
|
|
88
|
+
const name = normalize(item.name);
|
|
89
|
+
const coverageScore = coverage(normalize(item.searchText), tokens);
|
|
90
|
+
let bonus = 0;
|
|
91
|
+
if (name === q) bonus = 60;
|
|
92
|
+
else if (name.startsWith(q)) bonus = 40;
|
|
93
|
+
else if (name.includes(q)) bonus = 25;
|
|
94
|
+
if (tokens.some((token) => name.includes(token))) bonus += 15;
|
|
95
|
+
return coverageScore * 55 + bonus;
|
|
96
|
+
},
|
|
97
|
+
scoreSection(section, query) {
|
|
98
|
+
const q = normalize(query);
|
|
99
|
+
if (!q) return 0;
|
|
100
|
+
const tokens = queryTokens(query);
|
|
101
|
+
const heading = normalize(section.heading);
|
|
102
|
+
const coverageScore = coverage(normalize(`${section.heading} ${section.snippet}`), tokens);
|
|
103
|
+
let bonus = 0;
|
|
104
|
+
if (heading === q) bonus += 40;
|
|
105
|
+
else if (heading.includes(q)) bonus += 25;
|
|
106
|
+
if (tokens.some((token) => heading.includes(token))) bonus += 15;
|
|
107
|
+
return coverageScore * 60 + bonus;
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
function editDistance(a, b) {
|
|
112
|
+
const rows = Array.from({ length: a.length + 1 }, (_, i) => [i, ...Array(b.length).fill(0)]);
|
|
113
|
+
for (let j = 0; j <= b.length; j += 1) rows[0][j] = j;
|
|
114
|
+
for (let i = 1; i <= a.length; i += 1) for (let j = 1; j <= b.length; j += 1) {
|
|
115
|
+
const cost = a[i - 1] === b[j - 1] ? 0 : 1;
|
|
116
|
+
rows[i][j] = Math.min(rows[i - 1][j] + 1, rows[i][j - 1] + 1, rows[i - 1][j - 1] + cost);
|
|
117
|
+
}
|
|
118
|
+
return rows[a.length][b.length];
|
|
119
|
+
}
|
|
120
|
+
/** Returns names close enough to a failed lookup to be worth suggesting */
|
|
121
|
+
function suggestNames(index, query, limit = 3) {
|
|
122
|
+
const q = normalize(query);
|
|
123
|
+
const threshold = Math.max(2, Math.floor(q.length / 3));
|
|
124
|
+
return index.map((item) => ({
|
|
125
|
+
name: item.name,
|
|
126
|
+
distance: editDistance(q, normalize(item.name))
|
|
127
|
+
})).filter((entry) => entry.distance <= threshold).sort((a, b) => a.distance - b.distance).slice(0, limit).map((entry) => entry.name);
|
|
128
|
+
}
|
|
129
|
+
//#endregion
|
|
130
|
+
exports.createSearchScorer = createSearchScorer;
|
|
131
|
+
exports.suggestNames = suggestNames;
|
|
132
|
+
|
|
133
|
+
//# sourceMappingURL=search.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search.cjs","names":[],"sources":["../src/search.ts"],"sourcesContent":["import { IndexItem, SectionRecord } from './types';\n\nconst STOPWORDS = new Set([\n 'a',\n 'an',\n 'the',\n 'and',\n 'or',\n 'of',\n 'to',\n 'in',\n 'on',\n 'for',\n 'with',\n 'how',\n 'do',\n 'i',\n 'my',\n 'is',\n 'it',\n 'can',\n 'use',\n 'using',\n 'when',\n 'what',\n 'that',\n 'this',\n 'be',\n 'as',\n 'at',\n 'by',\n 'from',\n 'are',\n 'get',\n]);\n\nfunction normalize(value: string): string {\n return value.trim().toLowerCase();\n}\n\nfunction tokenize(value: string): string[] {\n return normalize(value)\n .split(/[^a-z0-9.-]+/)\n .filter(Boolean);\n}\n\nfunction queryTokens(query: string): string[] {\n const raw = tokenize(query);\n const filtered = raw.filter((token) => token.length > 2 && !STOPWORDS.has(token));\n return filtered.length > 0 ? filtered : raw;\n}\n\n/** A scoring function pair built against a fixed corpus's document-frequency statistics */\nexport interface SearchScorer {\n scoreItem(item: IndexItem, query: string): number;\n scoreSection(section: SectionRecord, query: string): number;\n}\n\n/**\n * Builds an IDF-weighted scorer over a fixed corpus.\n *\n * Document frequency is computed lazily per token and cached for the lifetime of the\n * returned scorer, so a common token like \"a\" contributes almost nothing to a match while\n * a rare token like \"zod\" dominates it. Callers should rebuild the scorer whenever `index`\n * or `sections` actually change.\n */\nexport function createSearchScorer(index: IndexItem[], sections: SectionRecord[]): SearchScorer {\n const sectionDocs = sections.map((section) => normalize(`${section.heading} ${section.snippet}`));\n const itemDocs = index.map((item) => normalize(item.searchText));\n const corpus = [...sectionDocs, ...itemDocs];\n const corpusSize = corpus.length;\n const documentFrequencyCache = new Map<string, number>();\n\n function documentFrequency(token: string): number {\n const cached = documentFrequencyCache.get(token);\n if (cached !== undefined) {\n return cached;\n }\n\n let count = 0;\n for (const doc of corpus) {\n if (doc.includes(token)) {\n count += 1;\n }\n }\n\n documentFrequencyCache.set(token, count);\n return count;\n }\n\n function idf(token: string): number {\n return Math.log((corpusSize + 1) / (documentFrequency(token) + 1));\n }\n\n function coverage(haystack: string, tokens: string[]): number {\n if (tokens.length === 0) {\n return 0;\n }\n\n let matchedWeight = 0;\n let totalWeight = 0;\n\n for (const token of tokens) {\n const weight = idf(token);\n totalWeight += weight;\n\n if (haystack.includes(token)) {\n matchedWeight += weight;\n }\n }\n\n return totalWeight > 0 ? matchedWeight / totalWeight : 0;\n }\n\n return {\n scoreItem(item: IndexItem, query: string): number {\n const q = normalize(query);\n\n if (!q) {\n return 0;\n }\n\n const tokens = queryTokens(query);\n const name = normalize(item.name);\n const haystack = normalize(item.searchText);\n const coverageScore = coverage(haystack, tokens);\n\n let bonus = 0;\n if (name === q) {\n bonus = 60;\n } else if (name.startsWith(q)) {\n bonus = 40;\n } else if (name.includes(q)) {\n bonus = 25;\n }\n\n if (tokens.some((token) => name.includes(token))) {\n bonus += 15;\n }\n\n return coverageScore * 55 + bonus;\n },\n\n scoreSection(section: SectionRecord, query: string): number {\n const q = normalize(query);\n\n if (!q) {\n return 0;\n }\n\n const tokens = queryTokens(query);\n const heading = normalize(section.heading);\n const haystack = normalize(`${section.heading} ${section.snippet}`);\n const coverageScore = coverage(haystack, tokens);\n\n let bonus = 0;\n if (heading === q) {\n bonus += 40;\n } else if (heading.includes(q)) {\n bonus += 25;\n }\n\n if (tokens.some((token) => heading.includes(token))) {\n bonus += 15;\n }\n\n return coverageScore * 60 + bonus;\n },\n };\n}\n\nfunction editDistance(a: string, b: string): number {\n const rows = Array.from({ length: a.length + 1 }, (_, i) => [i, ...Array(b.length).fill(0)]);\n\n for (let j = 0; j <= b.length; j += 1) {\n rows[0][j] = j;\n }\n\n for (let i = 1; i <= a.length; i += 1) {\n for (let j = 1; j <= b.length; j += 1) {\n const cost = a[i - 1] === b[j - 1] ? 0 : 1;\n rows[i][j] = Math.min(rows[i - 1][j] + 1, rows[i][j - 1] + 1, rows[i - 1][j - 1] + cost);\n }\n }\n\n return rows[a.length][b.length];\n}\n\n/** Returns names close enough to a failed lookup to be worth suggesting */\nexport function suggestNames(index: IndexItem[], query: string, limit = 3): string[] {\n const q = normalize(query);\n const threshold = Math.max(2, Math.floor(q.length / 3));\n\n return index\n .map((item) => ({ name: item.name, distance: editDistance(q, normalize(item.name)) }))\n .filter((entry) => entry.distance <= threshold)\n .sort((a, b) => a.distance - b.distance)\n .slice(0, limit)\n .map((entry) => entry.name);\n}\n"],"mappings":"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"}
|