@j0hanz/superfetch 2.4.0 → 2.4.2
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/dist/cache.d.ts +1 -1
- package/dist/cache.js +25 -6
- package/dist/dom-noise-removal.d.ts +0 -4
- package/dist/dom-noise-removal.js +42 -72
- package/dist/language-detection.js +13 -12
- package/dist/markdown-cleanup.js +16 -0
- package/dist/mcp.js +10 -3
- package/dist/tools.d.ts +6 -10
- package/dist/tools.js +12 -1
- package/dist/transform.js +38 -2
- package/package.json +1 -1
package/dist/cache.d.ts
CHANGED
|
@@ -36,7 +36,7 @@ export declare function get(cacheKey: string | null): CacheEntry | undefined;
|
|
|
36
36
|
export declare function set(cacheKey: string | null, content: string, metadata: CacheEntryMetadata): void;
|
|
37
37
|
export declare function keys(): readonly string[];
|
|
38
38
|
export declare function isEnabled(): boolean;
|
|
39
|
-
export declare function registerCachedContentResource(server: McpServer): void;
|
|
39
|
+
export declare function registerCachedContentResource(server: McpServer, serverIcon?: string): void;
|
|
40
40
|
export declare function generateSafeFilename(url: string, title?: string, hashFallback?: string, extension?: string): string;
|
|
41
41
|
export declare function handleDownload(res: ServerResponse, namespace: string, hash: string): void;
|
|
42
42
|
export {};
|
package/dist/cache.js
CHANGED
|
@@ -112,6 +112,7 @@ class NativeLruCache {
|
|
|
112
112
|
max;
|
|
113
113
|
ttlMs;
|
|
114
114
|
entries = new Map();
|
|
115
|
+
nextPurgeAtMs = 0;
|
|
115
116
|
constructor({ max, ttlMs }) {
|
|
116
117
|
this.max = max;
|
|
117
118
|
this.ttlMs = ttlMs;
|
|
@@ -132,12 +133,13 @@ class NativeLruCache {
|
|
|
132
133
|
set(key, value) {
|
|
133
134
|
if (this.max <= 0 || this.ttlMs <= 0)
|
|
134
135
|
return;
|
|
136
|
+
const now = Date.now();
|
|
135
137
|
this.entries.delete(key);
|
|
136
138
|
this.entries.set(key, {
|
|
137
139
|
value,
|
|
138
|
-
expiresAtMs:
|
|
140
|
+
expiresAtMs: now + this.ttlMs,
|
|
139
141
|
});
|
|
140
|
-
this.
|
|
142
|
+
this.maybePurge(now);
|
|
141
143
|
while (this.entries.size > this.max) {
|
|
142
144
|
const oldestKey = this.entries.keys().next().value;
|
|
143
145
|
if (oldestKey === undefined)
|
|
@@ -146,9 +148,15 @@ class NativeLruCache {
|
|
|
146
148
|
}
|
|
147
149
|
}
|
|
148
150
|
keys() {
|
|
149
|
-
this.
|
|
151
|
+
this.maybePurge(Date.now());
|
|
150
152
|
return [...this.entries.keys()];
|
|
151
153
|
}
|
|
154
|
+
maybePurge(now) {
|
|
155
|
+
if (this.entries.size > this.max || now >= this.nextPurgeAtMs) {
|
|
156
|
+
this.purgeExpired(now);
|
|
157
|
+
this.nextPurgeAtMs = now + this.ttlMs;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
152
160
|
purgeExpired(now) {
|
|
153
161
|
for (const [key, entry] of this.entries) {
|
|
154
162
|
if (this.isExpired(entry, now)) {
|
|
@@ -387,23 +395,34 @@ function notifyResourceUpdate(server, uri, subscriptions) {
|
|
|
387
395
|
});
|
|
388
396
|
});
|
|
389
397
|
}
|
|
390
|
-
export function registerCachedContentResource(server) {
|
|
398
|
+
export function registerCachedContentResource(server, serverIcon) {
|
|
391
399
|
const isInitialized = attachInitializedGate(server);
|
|
392
400
|
const subscriptions = registerResourceSubscriptionHandlers(server);
|
|
393
|
-
registerCacheContentResource(server);
|
|
401
|
+
registerCacheContentResource(server, serverIcon);
|
|
394
402
|
registerCacheUpdateSubscription(server, subscriptions, isInitialized);
|
|
395
403
|
}
|
|
396
404
|
function buildCachedContentResponse(uri, cacheKey) {
|
|
397
405
|
const cached = requireCacheEntry(cacheKey);
|
|
398
406
|
return buildMarkdownContentResponse(uri, cached.content);
|
|
399
407
|
}
|
|
400
|
-
function registerCacheContentResource(server) {
|
|
408
|
+
function registerCacheContentResource(server, serverIcon) {
|
|
401
409
|
server.registerResource('cached-content', new ResourceTemplate('superfetch://cache/{namespace}/{urlHash}', {
|
|
402
410
|
list: listCachedResources,
|
|
403
411
|
}), {
|
|
404
412
|
title: 'Cached Content',
|
|
405
413
|
description: 'Access previously fetched web content from cache. Namespace: markdown. UrlHash: SHA-256 hash of the URL.',
|
|
406
414
|
mimeType: 'text/markdown',
|
|
415
|
+
...(serverIcon
|
|
416
|
+
? {
|
|
417
|
+
icons: [
|
|
418
|
+
{
|
|
419
|
+
src: serverIcon,
|
|
420
|
+
mimeType: 'image/svg+xml',
|
|
421
|
+
sizes: ['any'],
|
|
422
|
+
},
|
|
423
|
+
],
|
|
424
|
+
}
|
|
425
|
+
: {}),
|
|
407
426
|
}, (uri, params) => {
|
|
408
427
|
const { namespace, urlHash } = resolveCacheParams(params);
|
|
409
428
|
const cacheKey = `${namespace}:${urlHash}`;
|
|
@@ -331,7 +331,6 @@ function removeNoiseNodes(nodes, shouldCheckNoise = true) {
|
|
|
331
331
|
removeNoiseFromNodeListLike(nodes, shouldCheckNoise);
|
|
332
332
|
return;
|
|
333
333
|
}
|
|
334
|
-
// Generic iterable: copy to avoid iteration issues while removing.
|
|
335
334
|
const nodeList = Array.from(nodes);
|
|
336
335
|
for (const node of nodeList) {
|
|
337
336
|
if (isElement(node) && (!shouldCheckNoise || isNoiseElement(node))) {
|
|
@@ -340,21 +339,15 @@ function removeNoiseNodes(nodes, shouldCheckNoise = true) {
|
|
|
340
339
|
}
|
|
341
340
|
}
|
|
342
341
|
function stripNoiseNodes(document) {
|
|
343
|
-
// Pass 1: Trusted selectors (Common noise)
|
|
344
|
-
// We trust these selectors match actual noise, so we skip the expensive isNoiseElement check
|
|
345
|
-
// Add user-configured extra selectors
|
|
346
342
|
const targetSelectors = buildNoiseSelector(config.noiseRemoval.extraSelectors);
|
|
347
343
|
const potentialNoiseNodes = document.querySelectorAll(targetSelectors);
|
|
348
344
|
removeNoiseNodes(potentialNoiseNodes, false);
|
|
349
|
-
// Second pass: check remaining elements for noise patterns (promo, fixed positioning, etc.)
|
|
350
345
|
const allElements = document.querySelectorAll(CANDIDATE_NOISE_SELECTOR);
|
|
351
346
|
removeNoiseNodes(allElements, true);
|
|
352
347
|
}
|
|
353
348
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
354
349
|
// URL Resolution
|
|
355
350
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
356
|
-
// Protocol patterns to skip during URL resolution (fragment, mailto, tel, blob, data, javascript)
|
|
357
|
-
// JavaScript protocol is detected to skip it for XSS prevention, not to evaluate it
|
|
358
351
|
const SKIP_URL_PREFIXES = [
|
|
359
352
|
'#',
|
|
360
353
|
'java' + 'script:',
|
|
@@ -363,11 +356,6 @@ const SKIP_URL_PREFIXES = [
|
|
|
363
356
|
'data:',
|
|
364
357
|
'blob:',
|
|
365
358
|
];
|
|
366
|
-
/**
|
|
367
|
-
* Check if a URL scheme should be skipped during resolution.
|
|
368
|
-
* These schemes are either fragment-only (#), protocol handlers (mailto, tel),
|
|
369
|
-
* inline data (data, blob), or javascript: which we skip to avoid XSS.
|
|
370
|
-
*/
|
|
371
359
|
function shouldSkipUrlResolution(url) {
|
|
372
360
|
const normalized = url.trim().toLowerCase();
|
|
373
361
|
return SKIP_URL_PREFIXES.some((prefix) => normalized.startsWith(prefix));
|
|
@@ -383,79 +371,61 @@ function tryResolveUrl(relativeUrl, baseUrl) {
|
|
|
383
371
|
return null;
|
|
384
372
|
}
|
|
385
373
|
}
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
if (href && !shouldSkipUrlResolution(href)) {
|
|
393
|
-
const resolved = tryResolveUrl(href, baseUrl);
|
|
394
|
-
if (resolved)
|
|
395
|
-
anchor.setAttribute('href', resolved);
|
|
396
|
-
}
|
|
374
|
+
function resolveAnchorElement(element, base) {
|
|
375
|
+
const href = element.getAttribute('href');
|
|
376
|
+
if (href && !shouldSkipUrlResolution(href)) {
|
|
377
|
+
const resolved = tryResolveUrl(href, base);
|
|
378
|
+
if (resolved)
|
|
379
|
+
element.setAttribute('href', resolved);
|
|
397
380
|
}
|
|
398
381
|
}
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
if (src && !shouldSkipUrlResolution(src)) {
|
|
406
|
-
const resolved = tryResolveUrl(src, baseUrl);
|
|
407
|
-
if (resolved)
|
|
408
|
-
img.setAttribute('src', resolved);
|
|
409
|
-
}
|
|
382
|
+
function resolveImageElement(element, base) {
|
|
383
|
+
const src = element.getAttribute('src');
|
|
384
|
+
if (src && !shouldSkipUrlResolution(src)) {
|
|
385
|
+
const resolved = tryResolveUrl(src, base);
|
|
386
|
+
if (resolved)
|
|
387
|
+
element.setAttribute('src', resolved);
|
|
410
388
|
}
|
|
411
389
|
}
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
}
|
|
431
|
-
return parts.join(' ');
|
|
432
|
-
})
|
|
433
|
-
.join(', ');
|
|
434
|
-
source.setAttribute('srcset', resolved);
|
|
435
|
-
}
|
|
390
|
+
function resolveSourceElement(element, base) {
|
|
391
|
+
const srcset = element.getAttribute('srcset');
|
|
392
|
+
if (!srcset)
|
|
393
|
+
return;
|
|
394
|
+
const resolved = srcset
|
|
395
|
+
.split(',')
|
|
396
|
+
.map((entry) => {
|
|
397
|
+
const parts = entry.trim().split(/\s+/);
|
|
398
|
+
const url = parts[0];
|
|
399
|
+
if (url) {
|
|
400
|
+
const resolvedUrl = tryResolveUrl(url, base);
|
|
401
|
+
if (resolvedUrl)
|
|
402
|
+
parts[0] = resolvedUrl;
|
|
403
|
+
}
|
|
404
|
+
return parts.join(' ');
|
|
405
|
+
})
|
|
406
|
+
.join(', ');
|
|
407
|
+
element.setAttribute('srcset', resolved);
|
|
436
408
|
}
|
|
437
|
-
/**
|
|
438
|
-
* Resolve relative URLs in anchor and image elements to absolute URLs.
|
|
439
|
-
* Fixes broken links/images in markdown output when the source uses relative paths.
|
|
440
|
-
*/
|
|
441
409
|
function resolveRelativeUrls(document, baseUrl) {
|
|
442
410
|
try {
|
|
443
411
|
const base = new URL(baseUrl);
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
412
|
+
for (const element of document.querySelectorAll('a[href], img[src], source[srcset]')) {
|
|
413
|
+
const tag = element.tagName.toLowerCase();
|
|
414
|
+
if (tag === 'a') {
|
|
415
|
+
resolveAnchorElement(element, base);
|
|
416
|
+
}
|
|
417
|
+
else if (tag === 'img') {
|
|
418
|
+
resolveImageElement(element, base);
|
|
419
|
+
}
|
|
420
|
+
else if (tag === 'source') {
|
|
421
|
+
resolveSourceElement(element, base);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
447
424
|
}
|
|
448
425
|
catch {
|
|
449
426
|
/* invalid base URL - skip resolution */
|
|
450
427
|
}
|
|
451
428
|
}
|
|
452
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
453
|
-
// Main Export
|
|
454
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
455
|
-
/**
|
|
456
|
-
* Remove noise elements from HTML and resolve relative URLs.
|
|
457
|
-
* Used as a preprocessing step before markdown conversion.
|
|
458
|
-
*/
|
|
459
429
|
export function removeNoiseFromHtml(html, document, baseUrl) {
|
|
460
430
|
const shouldParse = isFullDocumentHtml(html) || mayContainNoise(html);
|
|
461
431
|
if (!shouldParse)
|
|
@@ -98,8 +98,8 @@ function matchesPackageManagerVerb(line) {
|
|
|
98
98
|
}
|
|
99
99
|
return false;
|
|
100
100
|
}
|
|
101
|
-
function detectBashIndicators(
|
|
102
|
-
for (const line of
|
|
101
|
+
function detectBashIndicators(lines) {
|
|
102
|
+
for (const line of lines) {
|
|
103
103
|
const trimmed = line.trimStart();
|
|
104
104
|
if (trimmed &&
|
|
105
105
|
(isShellPrefix(trimmed) ||
|
|
@@ -110,8 +110,8 @@ function detectBashIndicators(code) {
|
|
|
110
110
|
}
|
|
111
111
|
return false;
|
|
112
112
|
}
|
|
113
|
-
function detectCssStructure(
|
|
114
|
-
for (const line of
|
|
113
|
+
function detectCssStructure(lines) {
|
|
114
|
+
for (const line of lines) {
|
|
115
115
|
const trimmed = line.trimStart();
|
|
116
116
|
if (!trimmed)
|
|
117
117
|
continue;
|
|
@@ -123,8 +123,8 @@ function detectCssStructure(code) {
|
|
|
123
123
|
}
|
|
124
124
|
return false;
|
|
125
125
|
}
|
|
126
|
-
function detectYamlStructure(
|
|
127
|
-
for (const line of
|
|
126
|
+
function detectYamlStructure(lines) {
|
|
127
|
+
for (const line of lines) {
|
|
128
128
|
const trimmed = line.trim();
|
|
129
129
|
if (!trimmed)
|
|
130
130
|
continue;
|
|
@@ -194,14 +194,14 @@ const LANGUAGE_PATTERNS = [
|
|
|
194
194
|
{
|
|
195
195
|
language: 'bash',
|
|
196
196
|
pattern: {
|
|
197
|
-
custom: (
|
|
197
|
+
custom: (_code, _lower, lines) => detectBashIndicators(lines),
|
|
198
198
|
},
|
|
199
199
|
},
|
|
200
200
|
{
|
|
201
201
|
language: 'css',
|
|
202
202
|
pattern: {
|
|
203
203
|
regex: /@media|@import|@keyframes/,
|
|
204
|
-
custom: (
|
|
204
|
+
custom: (_code, _lower, lines) => detectCssStructure(lines),
|
|
205
205
|
},
|
|
206
206
|
},
|
|
207
207
|
{
|
|
@@ -230,7 +230,7 @@ const LANGUAGE_PATTERNS = [
|
|
|
230
230
|
{
|
|
231
231
|
language: 'yaml',
|
|
232
232
|
pattern: {
|
|
233
|
-
custom: (
|
|
233
|
+
custom: (_code, _lower, lines) => detectYamlStructure(lines),
|
|
234
234
|
},
|
|
235
235
|
},
|
|
236
236
|
{
|
|
@@ -255,7 +255,7 @@ const LANGUAGE_PATTERNS = [
|
|
|
255
255
|
},
|
|
256
256
|
},
|
|
257
257
|
];
|
|
258
|
-
function matchesLanguagePattern(code, lower, pattern) {
|
|
258
|
+
function matchesLanguagePattern(code, lower, lines, pattern) {
|
|
259
259
|
if (pattern.keywords?.some((kw) => lower.includes(kw)))
|
|
260
260
|
return true;
|
|
261
261
|
if (pattern.wordBoundary?.some((w) => containsWord(lower, w)))
|
|
@@ -267,7 +267,7 @@ function matchesLanguagePattern(code, lower, pattern) {
|
|
|
267
267
|
if (pattern.startsWith.some((prefix) => trimmed.startsWith(prefix)))
|
|
268
268
|
return true;
|
|
269
269
|
}
|
|
270
|
-
if (pattern.custom?.(code, lower))
|
|
270
|
+
if (pattern.custom?.(code, lower, lines))
|
|
271
271
|
return true;
|
|
272
272
|
return false;
|
|
273
273
|
}
|
|
@@ -276,8 +276,9 @@ function matchesLanguagePattern(code, lower, pattern) {
|
|
|
276
276
|
*/
|
|
277
277
|
export function detectLanguageFromCode(code) {
|
|
278
278
|
const lower = code.toLowerCase();
|
|
279
|
+
const lines = code.split('\n');
|
|
279
280
|
for (const { language, pattern } of LANGUAGE_PATTERNS) {
|
|
280
|
-
if (matchesLanguagePattern(code, lower, pattern))
|
|
281
|
+
if (matchesLanguagePattern(code, lower, lines, pattern))
|
|
281
282
|
return language;
|
|
282
283
|
}
|
|
283
284
|
return undefined;
|
package/dist/markdown-cleanup.js
CHANGED
|
@@ -149,6 +149,21 @@ function normalizeListsAndSpacing(text) {
|
|
|
149
149
|
// Collapse excessive blank lines
|
|
150
150
|
return text.replace(/\n{3,}/g, '\n\n');
|
|
151
151
|
}
|
|
152
|
+
function fixConcatenatedProperties(text) {
|
|
153
|
+
const quotedValuePattern = /([a-z_][a-z0-9_]{0,30}\??:\s+)([\u0022\u201C][^\u0022\u201C\u201D]*[\u0022\u201D])([a-z_][a-z0-9_]{0,30}\??:)/g;
|
|
154
|
+
let result = text;
|
|
155
|
+
let iterations = 0;
|
|
156
|
+
const maxIterations = 3;
|
|
157
|
+
while (iterations < maxIterations) {
|
|
158
|
+
const before = result;
|
|
159
|
+
result = result.replace(quotedValuePattern, '$1$2\n\n$3');
|
|
160
|
+
if (result === before) {
|
|
161
|
+
break;
|
|
162
|
+
}
|
|
163
|
+
iterations++;
|
|
164
|
+
}
|
|
165
|
+
return result;
|
|
166
|
+
}
|
|
152
167
|
const CLEANUP_STEPS = [
|
|
153
168
|
fixOrphanHeadings,
|
|
154
169
|
removeEmptyHeadings,
|
|
@@ -157,6 +172,7 @@ const CLEANUP_STEPS = [
|
|
|
157
172
|
removeTocBlocks,
|
|
158
173
|
tidyLinksAndEscapes,
|
|
159
174
|
normalizeListsAndSpacing,
|
|
175
|
+
fixConcatenatedProperties,
|
|
160
176
|
];
|
|
161
177
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
162
178
|
// Public API
|
package/dist/mcp.js
CHANGED
|
@@ -22,7 +22,13 @@ function createServerInfo() {
|
|
|
22
22
|
return {
|
|
23
23
|
name: config.server.name,
|
|
24
24
|
version: config.server.version,
|
|
25
|
-
...(localIcon
|
|
25
|
+
...(localIcon
|
|
26
|
+
? {
|
|
27
|
+
icons: [
|
|
28
|
+
{ src: localIcon, mimeType: 'image/svg+xml', sizes: ['any'] },
|
|
29
|
+
],
|
|
30
|
+
}
|
|
31
|
+
: {}),
|
|
26
32
|
};
|
|
27
33
|
}
|
|
28
34
|
function createServerCapabilities() {
|
|
@@ -63,8 +69,9 @@ export function createMcpServer() {
|
|
|
63
69
|
instructions,
|
|
64
70
|
});
|
|
65
71
|
setMcpServer(server);
|
|
66
|
-
|
|
67
|
-
|
|
72
|
+
const localIcon = getLocalIconData();
|
|
73
|
+
registerTools(server, localIcon);
|
|
74
|
+
registerCachedContentResource(server, localIcon);
|
|
68
75
|
registerInstructionsResource(server, instructions);
|
|
69
76
|
return server;
|
|
70
77
|
}
|
package/dist/tools.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import type { CallToolResult, ContentBlock } from '@modelcontextprotocol/sdk/types.js';
|
|
2
3
|
import type { MarkdownTransformResult } from './transform-types.js';
|
|
3
4
|
export interface FetchUrlInput {
|
|
4
5
|
url: string;
|
|
@@ -23,20 +24,15 @@ export interface ToolContentResourceBlock {
|
|
|
23
24
|
text: string;
|
|
24
25
|
};
|
|
25
26
|
}
|
|
26
|
-
export type ToolContentBlockUnion =
|
|
27
|
-
export
|
|
28
|
-
content: ToolContentBlockUnion[];
|
|
27
|
+
export type ToolContentBlockUnion = ContentBlock;
|
|
28
|
+
export type ToolErrorResponse = CallToolResult & {
|
|
29
29
|
structuredContent: {
|
|
30
30
|
error: string;
|
|
31
31
|
url: string;
|
|
32
32
|
};
|
|
33
33
|
isError: true;
|
|
34
|
-
}
|
|
35
|
-
export
|
|
36
|
-
content: ToolContentBlockUnion[];
|
|
37
|
-
structuredContent?: Record<string, unknown>;
|
|
38
|
-
isError?: boolean;
|
|
39
|
-
}
|
|
34
|
+
};
|
|
35
|
+
export type ToolResponseBase = CallToolResult;
|
|
40
36
|
export interface FetchPipelineOptions<T> {
|
|
41
37
|
/** URL to fetch */
|
|
42
38
|
url: string;
|
|
@@ -125,5 +121,5 @@ type MarkdownPipelineResult = MarkdownTransformResult & {
|
|
|
125
121
|
export declare function parseCachedMarkdownResult(cached: string): MarkdownPipelineResult | undefined;
|
|
126
122
|
export declare function fetchUrlToolHandler(input: FetchUrlInput, extra?: ToolHandlerExtra): Promise<ToolResponseBase>;
|
|
127
123
|
export declare function withRequestContextIfMissing<TParams, TResult, TExtra = unknown>(handler: (params: TParams, extra?: TExtra) => Promise<TResult>): (params: TParams, extra?: TExtra) => Promise<TResult>;
|
|
128
|
-
export declare function registerTools(server: McpServer): void;
|
|
124
|
+
export declare function registerTools(server: McpServer, serverIcon?: string): void;
|
|
129
125
|
export {};
|
package/dist/tools.js
CHANGED
|
@@ -482,12 +482,23 @@ function resolveRequestIdFromExtra(extra) {
|
|
|
482
482
|
return String(requestId);
|
|
483
483
|
return undefined;
|
|
484
484
|
}
|
|
485
|
-
export function registerTools(server) {
|
|
485
|
+
export function registerTools(server, serverIcon) {
|
|
486
486
|
server.registerTool(TOOL_DEFINITION.name, {
|
|
487
487
|
title: TOOL_DEFINITION.title,
|
|
488
488
|
description: TOOL_DEFINITION.description,
|
|
489
489
|
inputSchema: TOOL_DEFINITION.inputSchema,
|
|
490
490
|
outputSchema: TOOL_DEFINITION.outputSchema,
|
|
491
491
|
annotations: TOOL_DEFINITION.annotations,
|
|
492
|
+
...(serverIcon
|
|
493
|
+
? {
|
|
494
|
+
icons: [
|
|
495
|
+
{
|
|
496
|
+
src: serverIcon,
|
|
497
|
+
mimeType: 'image/svg+xml',
|
|
498
|
+
sizes: ['any'],
|
|
499
|
+
},
|
|
500
|
+
],
|
|
501
|
+
}
|
|
502
|
+
: {}),
|
|
492
503
|
}, withRequestContextIfMissing(TOOL_DEFINITION.handler));
|
|
493
504
|
}
|
package/dist/transform.js
CHANGED
|
@@ -519,6 +519,36 @@ function createCustomTranslators() {
|
|
|
519
519
|
.join('\n');
|
|
520
520
|
return { content: items ? `\n${items}\n\n` : '' };
|
|
521
521
|
},
|
|
522
|
+
div: (ctx) => {
|
|
523
|
+
if (!isObject(ctx) || !isObject(ctx.node)) {
|
|
524
|
+
return {};
|
|
525
|
+
}
|
|
526
|
+
const node = ctx.node;
|
|
527
|
+
const className = typeof node.attribs?.class === 'string' ? node.attribs.class : '';
|
|
528
|
+
if (!className.includes('type')) {
|
|
529
|
+
return {};
|
|
530
|
+
}
|
|
531
|
+
return {
|
|
532
|
+
postprocess: ({ content }) => {
|
|
533
|
+
const lines = content.split('\n');
|
|
534
|
+
const separated = [];
|
|
535
|
+
for (let i = 0; i < lines.length; i++) {
|
|
536
|
+
const line = lines[i] ?? '';
|
|
537
|
+
const nextLine = i < lines.length - 1 ? (lines[i + 1] ?? '') : '';
|
|
538
|
+
separated.push(line);
|
|
539
|
+
if (line.trim() &&
|
|
540
|
+
nextLine.trim() &&
|
|
541
|
+
line.includes(':') &&
|
|
542
|
+
nextLine.includes(':') &&
|
|
543
|
+
!line.startsWith(' ') &&
|
|
544
|
+
!nextLine.startsWith(' ')) {
|
|
545
|
+
separated.push('');
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
return separated.join('\n');
|
|
549
|
+
},
|
|
550
|
+
};
|
|
551
|
+
},
|
|
522
552
|
kbd: () => ({
|
|
523
553
|
postprocess: ({ content }) => `\`${content}\``,
|
|
524
554
|
}),
|
|
@@ -531,7 +561,8 @@ function createCustomTranslators() {
|
|
|
531
561
|
sup: () => ({
|
|
532
562
|
postprocess: ({ content }) => `^${content}^`,
|
|
533
563
|
}),
|
|
534
|
-
//
|
|
564
|
+
// Note: section translator removed in favor of HTML preprocessing
|
|
565
|
+
// See preprocessPropertySections() for the fix to TypeDoc section spacing
|
|
535
566
|
pre: (ctx) => buildPreTranslator(ctx),
|
|
536
567
|
};
|
|
537
568
|
}
|
|
@@ -548,13 +579,18 @@ function getMarkdownConverter() {
|
|
|
548
579
|
markdownInstance ??= createMarkdownInstance();
|
|
549
580
|
return markdownInstance;
|
|
550
581
|
}
|
|
582
|
+
function preprocessPropertySections(html) {
|
|
583
|
+
const result = html.replace(/<\/section>\s*(<section[^>]*class="[^"]*tsd-panel[^"]*tsd-member[^"]*"[^>]*>)/g, '</section><p> </p>$1');
|
|
584
|
+
return result;
|
|
585
|
+
}
|
|
551
586
|
function translateHtmlToMarkdown(html, url, signal, document, skipNoiseRemoval) {
|
|
552
587
|
throwIfAborted(signal, url, 'markdown:begin');
|
|
553
588
|
const cleanedHtml = skipNoiseRemoval
|
|
554
589
|
? html
|
|
555
590
|
: runTransformStage(url, 'markdown:noise', () => removeNoiseFromHtml(html, document, url));
|
|
556
591
|
throwIfAborted(signal, url, 'markdown:cleaned');
|
|
557
|
-
const
|
|
592
|
+
const preprocessedHtml = runTransformStage(url, 'markdown:preprocess', () => preprocessPropertySections(cleanedHtml));
|
|
593
|
+
const content = runTransformStage(url, 'markdown:translate', () => getMarkdownConverter().translate(preprocessedHtml).trim());
|
|
558
594
|
throwIfAborted(signal, url, 'markdown:translated');
|
|
559
595
|
return cleanupMarkdownArtifacts(content);
|
|
560
596
|
}
|
package/package.json
CHANGED