@cosmocoder/mcp-web-docs 1.0.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/LICENSE +22 -0
- package/README.md +368 -0
- package/build/__mocks__/embeddings.d.ts +17 -0
- package/build/__mocks__/embeddings.js +66 -0
- package/build/__mocks__/embeddings.js.map +1 -0
- package/build/config.d.ts +44 -0
- package/build/config.js +158 -0
- package/build/config.js.map +1 -0
- package/build/config.test.d.ts +1 -0
- package/build/config.test.js +165 -0
- package/build/config.test.js.map +1 -0
- package/build/crawler/auth.d.ts +128 -0
- package/build/crawler/auth.js +546 -0
- package/build/crawler/auth.js.map +1 -0
- package/build/crawler/auth.test.d.ts +1 -0
- package/build/crawler/auth.test.js +174 -0
- package/build/crawler/auth.test.js.map +1 -0
- package/build/crawler/base.d.ts +24 -0
- package/build/crawler/base.js +149 -0
- package/build/crawler/base.js.map +1 -0
- package/build/crawler/base.test.d.ts +1 -0
- package/build/crawler/base.test.js +234 -0
- package/build/crawler/base.test.js.map +1 -0
- package/build/crawler/browser-config.d.ts +2 -0
- package/build/crawler/browser-config.js +29 -0
- package/build/crawler/browser-config.js.map +1 -0
- package/build/crawler/browser-config.test.d.ts +1 -0
- package/build/crawler/browser-config.test.js +56 -0
- package/build/crawler/browser-config.test.js.map +1 -0
- package/build/crawler/cheerio.d.ts +11 -0
- package/build/crawler/cheerio.js +134 -0
- package/build/crawler/cheerio.js.map +1 -0
- package/build/crawler/chromium.d.ts +21 -0
- package/build/crawler/chromium.js +596 -0
- package/build/crawler/chromium.js.map +1 -0
- package/build/crawler/content-extractor-types.d.ts +25 -0
- package/build/crawler/content-extractor-types.js +2 -0
- package/build/crawler/content-extractor-types.js.map +1 -0
- package/build/crawler/content-extractors.d.ts +9 -0
- package/build/crawler/content-extractors.js +9 -0
- package/build/crawler/content-extractors.js.map +1 -0
- package/build/crawler/content-utils.d.ts +2 -0
- package/build/crawler/content-utils.js +22 -0
- package/build/crawler/content-utils.js.map +1 -0
- package/build/crawler/content-utils.test.d.ts +1 -0
- package/build/crawler/content-utils.test.js +99 -0
- package/build/crawler/content-utils.test.js.map +1 -0
- package/build/crawler/crawlee-crawler.d.ts +63 -0
- package/build/crawler/crawlee-crawler.js +342 -0
- package/build/crawler/crawlee-crawler.js.map +1 -0
- package/build/crawler/crawlee-crawler.test.d.ts +1 -0
- package/build/crawler/crawlee-crawler.test.js +280 -0
- package/build/crawler/crawlee-crawler.test.js.map +1 -0
- package/build/crawler/default-extractor.d.ts +4 -0
- package/build/crawler/default-extractor.js +26 -0
- package/build/crawler/default-extractor.js.map +1 -0
- package/build/crawler/default-extractor.test.d.ts +1 -0
- package/build/crawler/default-extractor.test.js +200 -0
- package/build/crawler/default-extractor.test.js.map +1 -0
- package/build/crawler/default.d.ts +11 -0
- package/build/crawler/default.js +138 -0
- package/build/crawler/default.js.map +1 -0
- package/build/crawler/docs-crawler.d.ts +26 -0
- package/build/crawler/docs-crawler.js +97 -0
- package/build/crawler/docs-crawler.js.map +1 -0
- package/build/crawler/docs-crawler.test.d.ts +1 -0
- package/build/crawler/docs-crawler.test.js +185 -0
- package/build/crawler/docs-crawler.test.js.map +1 -0
- package/build/crawler/factory.d.ts +6 -0
- package/build/crawler/factory.js +83 -0
- package/build/crawler/factory.js.map +1 -0
- package/build/crawler/github-pages-extractor.d.ts +4 -0
- package/build/crawler/github-pages-extractor.js +33 -0
- package/build/crawler/github-pages-extractor.js.map +1 -0
- package/build/crawler/github-pages-extractor.test.d.ts +1 -0
- package/build/crawler/github-pages-extractor.test.js +184 -0
- package/build/crawler/github-pages-extractor.test.js.map +1 -0
- package/build/crawler/github.d.ts +20 -0
- package/build/crawler/github.js +181 -0
- package/build/crawler/github.js.map +1 -0
- package/build/crawler/github.test.d.ts +1 -0
- package/build/crawler/github.test.js +326 -0
- package/build/crawler/github.test.js.map +1 -0
- package/build/crawler/puppeteer.d.ts +16 -0
- package/build/crawler/puppeteer.js +191 -0
- package/build/crawler/puppeteer.js.map +1 -0
- package/build/crawler/queue-manager.d.ts +43 -0
- package/build/crawler/queue-manager.js +169 -0
- package/build/crawler/queue-manager.js.map +1 -0
- package/build/crawler/queue-manager.test.d.ts +1 -0
- package/build/crawler/queue-manager.test.js +509 -0
- package/build/crawler/queue-manager.test.js.map +1 -0
- package/build/crawler/site-rules.d.ts +11 -0
- package/build/crawler/site-rules.js +104 -0
- package/build/crawler/site-rules.js.map +1 -0
- package/build/crawler/site-rules.test.d.ts +1 -0
- package/build/crawler/site-rules.test.js +139 -0
- package/build/crawler/site-rules.test.js.map +1 -0
- package/build/crawler/storybook-extractor.d.ts +34 -0
- package/build/crawler/storybook-extractor.js +767 -0
- package/build/crawler/storybook-extractor.js.map +1 -0
- package/build/crawler/storybook-extractor.test.d.ts +1 -0
- package/build/crawler/storybook-extractor.test.js +491 -0
- package/build/crawler/storybook-extractor.test.js.map +1 -0
- package/build/embeddings/fastembed.d.ts +25 -0
- package/build/embeddings/fastembed.js +188 -0
- package/build/embeddings/fastembed.js.map +1 -0
- package/build/embeddings/fastembed.test.d.ts +1 -0
- package/build/embeddings/fastembed.test.js +307 -0
- package/build/embeddings/fastembed.test.js.map +1 -0
- package/build/embeddings/openai.d.ts +8 -0
- package/build/embeddings/openai.js +56 -0
- package/build/embeddings/openai.js.map +1 -0
- package/build/embeddings/types.d.ts +4 -0
- package/build/embeddings/types.js +2 -0
- package/build/embeddings/types.js.map +1 -0
- package/build/index.d.ts +2 -0
- package/build/index.js +1007 -0
- package/build/index.js.map +1 -0
- package/build/index.test.d.ts +1 -0
- package/build/index.test.js +364 -0
- package/build/index.test.js.map +1 -0
- package/build/indexing/queue-manager.d.ts +36 -0
- package/build/indexing/queue-manager.js +86 -0
- package/build/indexing/queue-manager.js.map +1 -0
- package/build/indexing/queue-manager.test.d.ts +1 -0
- package/build/indexing/queue-manager.test.js +257 -0
- package/build/indexing/queue-manager.test.js.map +1 -0
- package/build/indexing/status.d.ts +39 -0
- package/build/indexing/status.js +207 -0
- package/build/indexing/status.js.map +1 -0
- package/build/indexing/status.test.d.ts +1 -0
- package/build/indexing/status.test.js +246 -0
- package/build/indexing/status.test.js.map +1 -0
- package/build/processor/content.d.ts +16 -0
- package/build/processor/content.js +286 -0
- package/build/processor/content.js.map +1 -0
- package/build/processor/content.test.d.ts +1 -0
- package/build/processor/content.test.js +369 -0
- package/build/processor/content.test.js.map +1 -0
- package/build/processor/markdown.d.ts +11 -0
- package/build/processor/markdown.js +256 -0
- package/build/processor/markdown.js.map +1 -0
- package/build/processor/markdown.test.d.ts +1 -0
- package/build/processor/markdown.test.js +312 -0
- package/build/processor/markdown.test.js.map +1 -0
- package/build/processor/metadata-parser.d.ts +37 -0
- package/build/processor/metadata-parser.js +245 -0
- package/build/processor/metadata-parser.js.map +1 -0
- package/build/processor/metadata-parser.test.d.ts +1 -0
- package/build/processor/metadata-parser.test.js +357 -0
- package/build/processor/metadata-parser.test.js.map +1 -0
- package/build/processor/processor.d.ts +8 -0
- package/build/processor/processor.js +190 -0
- package/build/processor/processor.js.map +1 -0
- package/build/processor/processor.test.d.ts +1 -0
- package/build/processor/processor.test.js +357 -0
- package/build/processor/processor.test.js.map +1 -0
- package/build/rag/cache.d.ts +10 -0
- package/build/rag/cache.js +10 -0
- package/build/rag/cache.js.map +1 -0
- package/build/rag/code-generator.d.ts +11 -0
- package/build/rag/code-generator.js +30 -0
- package/build/rag/code-generator.js.map +1 -0
- package/build/rag/context-assembler.d.ts +23 -0
- package/build/rag/context-assembler.js +113 -0
- package/build/rag/context-assembler.js.map +1 -0
- package/build/rag/docs-search.d.ts +55 -0
- package/build/rag/docs-search.js +380 -0
- package/build/rag/docs-search.js.map +1 -0
- package/build/rag/pipeline.d.ts +26 -0
- package/build/rag/pipeline.js +91 -0
- package/build/rag/pipeline.js.map +1 -0
- package/build/rag/query-processor.d.ts +14 -0
- package/build/rag/query-processor.js +57 -0
- package/build/rag/query-processor.js.map +1 -0
- package/build/rag/reranker.d.ts +55 -0
- package/build/rag/reranker.js +210 -0
- package/build/rag/reranker.js.map +1 -0
- package/build/rag/response-generator.d.ts +20 -0
- package/build/rag/response-generator.js +101 -0
- package/build/rag/response-generator.js.map +1 -0
- package/build/rag/retriever.d.ts +19 -0
- package/build/rag/retriever.js +111 -0
- package/build/rag/retriever.js.map +1 -0
- package/build/rag/validator.d.ts +22 -0
- package/build/rag/validator.js +128 -0
- package/build/rag/validator.js.map +1 -0
- package/build/rag/version-manager.d.ts +23 -0
- package/build/rag/version-manager.js +98 -0
- package/build/rag/version-manager.js.map +1 -0
- package/build/setupTests.d.ts +4 -0
- package/build/setupTests.js +50 -0
- package/build/setupTests.js.map +1 -0
- package/build/storage/storage.d.ts +38 -0
- package/build/storage/storage.js +700 -0
- package/build/storage/storage.js.map +1 -0
- package/build/storage/storage.test.d.ts +1 -0
- package/build/storage/storage.test.js +338 -0
- package/build/storage/storage.test.js.map +1 -0
- package/build/types/rag.d.ts +27 -0
- package/build/types/rag.js +2 -0
- package/build/types/rag.js.map +1 -0
- package/build/types.d.ts +120 -0
- package/build/types.js +2 -0
- package/build/types.js.map +1 -0
- package/build/util/content-utils.d.ts +31 -0
- package/build/util/content-utils.js +120 -0
- package/build/util/content-utils.js.map +1 -0
- package/build/util/content.d.ts +1 -0
- package/build/util/content.js +16 -0
- package/build/util/content.js.map +1 -0
- package/build/util/docs.d.ts +1 -0
- package/build/util/docs.js +26 -0
- package/build/util/docs.js.map +1 -0
- package/build/util/docs.test.d.ts +1 -0
- package/build/util/docs.test.js +49 -0
- package/build/util/docs.test.js.map +1 -0
- package/build/util/favicon.d.ts +6 -0
- package/build/util/favicon.js +88 -0
- package/build/util/favicon.js.map +1 -0
- package/build/util/favicon.test.d.ts +1 -0
- package/build/util/favicon.test.js +140 -0
- package/build/util/favicon.test.js.map +1 -0
- package/build/util/logger.d.ts +17 -0
- package/build/util/logger.js +72 -0
- package/build/util/logger.js.map +1 -0
- package/build/util/logger.test.d.ts +1 -0
- package/build/util/logger.test.js +46 -0
- package/build/util/logger.test.js.map +1 -0
- package/build/util/security.d.ts +312 -0
- package/build/util/security.js +719 -0
- package/build/util/security.js.map +1 -0
- package/build/util/security.test.d.ts +1 -0
- package/build/util/security.test.js +524 -0
- package/build/util/security.test.js.map +1 -0
- package/build/util/site-detector.d.ts +22 -0
- package/build/util/site-detector.js +42 -0
- package/build/util/site-detector.js.map +1 -0
- package/package.json +112 -0
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utilities for cleaning and formatting content
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Cleans text content by normalizing whitespace and line endings
|
|
6
|
+
*/
|
|
7
|
+
export function cleanTextContent(text) {
|
|
8
|
+
return text
|
|
9
|
+
.replace(/\\n/g, '\n') // Convert escaped newlines
|
|
10
|
+
.replace(/\r\n/g, '\n') // Normalize line endings
|
|
11
|
+
.replace(/\t/g, ' ') // Convert tabs to spaces
|
|
12
|
+
.replace(/[^\S\n]+/g, ' ') // Replace multiple spaces with single space (except newlines)
|
|
13
|
+
.split('\n')
|
|
14
|
+
.map(line => line.trim()) // Trim each line
|
|
15
|
+
.join('\n')
|
|
16
|
+
.replace(/\n{3,}/g, '\n\n') // Max 2 consecutive newlines
|
|
17
|
+
.trim();
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Cleans and formats code blocks
|
|
21
|
+
*/
|
|
22
|
+
export function cleanupCode(code) {
|
|
23
|
+
return code
|
|
24
|
+
.replace(/^\s+|\s+$/g, '') // Trim whitespace
|
|
25
|
+
.replace(/\t/g, ' ') // Convert tabs to spaces
|
|
26
|
+
.replace(/\n{3,}/g, '\n\n') // Reduce multiple blank lines
|
|
27
|
+
.replace(/\u00A0/g, ' ') // Replace non-breaking spaces
|
|
28
|
+
.replace(/\r\n/g, '\n') // Normalize line endings
|
|
29
|
+
.replace(/[ \t]+$/gm, '') // Remove trailing spaces
|
|
30
|
+
.replace(/^\n+|\n+$/g, ''); // Trim leading/trailing newlines
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Formats a code block with language specification
|
|
34
|
+
*/
|
|
35
|
+
export function formatCodeBlock(code, language) {
|
|
36
|
+
const cleanCode = cleanupCode(code);
|
|
37
|
+
return `\`\`\`${language}\n${cleanCode}\n\`\`\``;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Checks if an element is visible in the DOM
|
|
41
|
+
*/
|
|
42
|
+
export function isElementVisible(element) {
|
|
43
|
+
const style = window.getComputedStyle(element);
|
|
44
|
+
return style.display !== 'none' &&
|
|
45
|
+
style.visibility !== 'hidden' &&
|
|
46
|
+
style.opacity !== '0';
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Extracts links and inline code from an element, converting to markdown
|
|
50
|
+
*/
|
|
51
|
+
export function extractLinks(element) {
|
|
52
|
+
let content = element.innerHTML;
|
|
53
|
+
// Handle links
|
|
54
|
+
const links = element.querySelectorAll('a');
|
|
55
|
+
links.forEach(link => {
|
|
56
|
+
const text = link.textContent?.trim();
|
|
57
|
+
const href = link.getAttribute('href');
|
|
58
|
+
if (text && href) {
|
|
59
|
+
const linkHtml = link.outerHTML;
|
|
60
|
+
content = content.replace(linkHtml, `[${text}](${href})`);
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
// Handle inline code elements
|
|
64
|
+
const codeElements = element.querySelectorAll('code, [class*="code"], [class*="inline-code"], [class*="monospace"]');
|
|
65
|
+
codeElements.forEach(code => {
|
|
66
|
+
const text = code.textContent?.trim();
|
|
67
|
+
if (text) {
|
|
68
|
+
const codeHtml = code.outerHTML;
|
|
69
|
+
content = content.replace(codeHtml, `\`${text}\``);
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
// Convert HTML to plain text while preserving markdown
|
|
73
|
+
const div = document.createElement('div');
|
|
74
|
+
div.innerHTML = content;
|
|
75
|
+
return div.textContent?.trim() || '';
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Waits for Storybook API to be ready
|
|
79
|
+
*/
|
|
80
|
+
export async function waitForStorybookAPI() {
|
|
81
|
+
const maxAttempts = 5;
|
|
82
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
83
|
+
const hasAPI = !!window.__STORYBOOK_CLIENT_API__;
|
|
84
|
+
if (hasAPI) {
|
|
85
|
+
const api = window.__STORYBOOK_CLIENT_API__;
|
|
86
|
+
if (api.storyStore && typeof api.storyStore.ready === 'function') {
|
|
87
|
+
await api.storyStore.ready();
|
|
88
|
+
}
|
|
89
|
+
break;
|
|
90
|
+
}
|
|
91
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Waits for Storybook content to be loaded
|
|
96
|
+
*/
|
|
97
|
+
export async function waitForStorybookContent(document) {
|
|
98
|
+
const maxAttempts = 10;
|
|
99
|
+
const delay = 500;
|
|
100
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
101
|
+
await new Promise(resolve => setTimeout(resolve, delay));
|
|
102
|
+
const mainArea = document.querySelector('.sbdocs-content, #docs-root');
|
|
103
|
+
if (!mainArea)
|
|
104
|
+
continue;
|
|
105
|
+
const hasContent = mainArea.querySelector('h1') && (mainArea.querySelector('p') ||
|
|
106
|
+
mainArea.querySelector('table') ||
|
|
107
|
+
mainArea.querySelector('ul, ol') ||
|
|
108
|
+
mainArea.querySelector('[class*="docblock"]'));
|
|
109
|
+
if (hasContent) {
|
|
110
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
111
|
+
return mainArea;
|
|
112
|
+
}
|
|
113
|
+
const loadingIndicator = document.querySelector('[class*="loading"], [class*="pending"]');
|
|
114
|
+
if (!loadingIndicator) {
|
|
115
|
+
console.warn('No loading indicator found but content is missing');
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=content-utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"content-utils.js","sourceRoot":"","sources":["../../src/util/content-utils.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function cleanContent(text: string): string;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export function cleanContent(text) {
|
|
2
|
+
return text
|
|
3
|
+
.replace(/\\n/g, '\n') // Convert escaped newlines
|
|
4
|
+
.replace(/\r\n/g, '\n') // Normalize line endings
|
|
5
|
+
.replace(/\t/g, ' ') // Convert tabs to spaces
|
|
6
|
+
.replace(/[^\S\n]+/g, ' ') // Replace multiple spaces with single space (except newlines)
|
|
7
|
+
.replace(/\u00A0/g, ' ') // Replace non-breaking spaces
|
|
8
|
+
.replace(/[ \t]+$/gm, '') // Remove trailing spaces
|
|
9
|
+
.split('\n')
|
|
10
|
+
.map(line => line.trim()) // Trim each line
|
|
11
|
+
.join('\n')
|
|
12
|
+
.replace(/\n{3,}/g, '\n\n') // Max 2 consecutive newlines
|
|
13
|
+
.replace(/^\n+|\n+$/g, '') // Trim leading/trailing newlines
|
|
14
|
+
.trim();
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=content.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"content.js","sourceRoot":"","sources":["../../src/util/content.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,OAAO,IAAI;SACR,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,CAAQ,2BAA2B;SACxD,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,CAAO,yBAAyB;SACtD,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,CAAS,yBAAyB;SACtD,OAAO,CAAC,WAAW,EAAE,GAAG,CAAC,CAAI,8DAA8D;SAC3F,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC,CAAM,8BAA8B;SAC3D,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,CAAK,yBAAyB;SACtD,KAAK,CAAC,IAAI,CAAC;SACX,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAK,iBAAiB;SAC9C,IAAI,CAAC,IAAI,CAAC;SACV,OAAO,CAAC,SAAS,EAAE,MAAM,CAAC,CAAG,6BAA6B;SAC1D,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC,CAAI,iCAAiC;SAC9D,IAAI,EAAE,CAAC;AACZ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function generateDocId(url: string, title: string): string;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export function generateDocId(url, title) {
|
|
2
|
+
const urlObj = new URL(url);
|
|
3
|
+
const pathParts = urlObj.pathname.split('/').filter(Boolean);
|
|
4
|
+
// For GitHub Pages (e.g., jimdo.github.io/ui/latest)
|
|
5
|
+
if (urlObj.hostname.endsWith('github.io')) {
|
|
6
|
+
const org = urlObj.hostname.split('.')[0];
|
|
7
|
+
const repo = pathParts[0];
|
|
8
|
+
return `${org}-${repo}`;
|
|
9
|
+
}
|
|
10
|
+
// For organization packages (e.g., @org/package)
|
|
11
|
+
if (title.includes('/')) {
|
|
12
|
+
return title.toLowerCase().replace(/[@/]/g, '-').replace(/\s+/g, '-');
|
|
13
|
+
}
|
|
14
|
+
// For regular packages, use the first part of the hostname
|
|
15
|
+
const hostParts = urlObj.hostname.split('.');
|
|
16
|
+
if (hostParts.length > 1) {
|
|
17
|
+
const mainPart = hostParts[0] === 'www' ? hostParts[1] : hostParts[0];
|
|
18
|
+
// If there's a specific product/package in the path, include it
|
|
19
|
+
if (pathParts.length > 0 && pathParts[0] !== 'docs') {
|
|
20
|
+
return `${mainPart}-${pathParts[0]}`;
|
|
21
|
+
}
|
|
22
|
+
return mainPart;
|
|
23
|
+
}
|
|
24
|
+
return urlObj.hostname;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=docs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docs.js","sourceRoot":"","sources":["../../src/util/docs.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,aAAa,CAAC,GAAW,EAAE,KAAa;IACtD,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;IAC5B,MAAM,SAAS,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAE7D,qDAAqD;IACrD,IAAI,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;QAC1C,MAAM,GAAG,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QAC1C,MAAM,IAAI,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC;QAC1B,OAAO,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;IAC1B,CAAC;IAED,iDAAiD;IACjD,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QACxB,OAAO,KAAK,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACxE,CAAC;IAED,2DAA2D;IAC3D,MAAM,SAAS,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC7C,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACzB,MAAM,QAAQ,GAAG,SAAS,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;QACtE,gEAAgE;QAChE,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,IAAI,SAAS,CAAC,CAAC,CAAC,KAAK,MAAM,EAAE,CAAC;YACpD,OAAO,GAAG,QAAQ,IAAI,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC;QACvC,CAAC;QACD,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,OAAO,MAAM,CAAC,QAAQ,CAAC;AACzB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { generateDocId } from './docs.js';
|
|
2
|
+
describe('Docs Utilities', () => {
|
|
3
|
+
describe('generateDocId', () => {
|
|
4
|
+
it('should generate ID for GitHub Pages URLs', () => {
|
|
5
|
+
const result = generateDocId('https://jimdo.github.io/ui/latest', 'UI Components');
|
|
6
|
+
expect(result).toBe('jimdo-ui');
|
|
7
|
+
});
|
|
8
|
+
it('should generate ID for GitHub Pages with subdirectory', () => {
|
|
9
|
+
const result = generateDocId('https://myorg.github.io/my-package/docs/guide', 'Guide');
|
|
10
|
+
expect(result).toBe('myorg-my-package');
|
|
11
|
+
});
|
|
12
|
+
it('should generate ID for scoped package titles', () => {
|
|
13
|
+
const result = generateDocId('https://example.com/docs', '@org/package-name');
|
|
14
|
+
expect(result).toBe('-org-package-name');
|
|
15
|
+
});
|
|
16
|
+
it('should generate ID from hostname for regular URLs', () => {
|
|
17
|
+
const result = generateDocId('https://docs.example.com/guide', 'Example Guide');
|
|
18
|
+
// Includes first path part since it's not 'docs'
|
|
19
|
+
expect(result).toBe('docs-guide');
|
|
20
|
+
});
|
|
21
|
+
it('should include path part for non-docs paths', () => {
|
|
22
|
+
const result = generateDocId('https://example.com/react-components', 'Components');
|
|
23
|
+
expect(result).toBe('example-react-components');
|
|
24
|
+
});
|
|
25
|
+
it('should exclude docs from path', () => {
|
|
26
|
+
const result = generateDocId('https://example.com/docs/getting-started', 'Getting Started');
|
|
27
|
+
expect(result).toBe('example');
|
|
28
|
+
});
|
|
29
|
+
it('should handle www prefix', () => {
|
|
30
|
+
const result = generateDocId('https://www.example.com/api', 'API Reference');
|
|
31
|
+
expect(result).toBe('example-api');
|
|
32
|
+
});
|
|
33
|
+
it('should handle simple hostname', () => {
|
|
34
|
+
const result = generateDocId('https://localhost:3000/', 'Local Docs');
|
|
35
|
+
// Port is not included in the hostname parsing
|
|
36
|
+
expect(result).toBe('localhost');
|
|
37
|
+
});
|
|
38
|
+
it('should handle URLs with query parameters', () => {
|
|
39
|
+
const result = generateDocId('https://docs.site.com/api?version=2', 'API v2');
|
|
40
|
+
// Includes path part
|
|
41
|
+
expect(result).toBe('docs-api');
|
|
42
|
+
});
|
|
43
|
+
it('should handle URLs with fragments', () => {
|
|
44
|
+
const result = generateDocId('https://example.com/guide#section', 'Guide');
|
|
45
|
+
expect(result).toBe('example-guide');
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
//# sourceMappingURL=docs.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docs.test.js","sourceRoot":"","sources":["../../src/util/docs.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAE1C,QAAQ,CAAC,gBAAgB,EAAE,GAAG,EAAE;IAC9B,QAAQ,CAAC,eAAe,EAAE,GAAG,EAAE;QAC7B,EAAE,CAAC,0CAA0C,EAAE,GAAG,EAAE;YAClD,MAAM,MAAM,GAAG,aAAa,CAAC,mCAAmC,EAAE,eAAe,CAAC,CAAC;YACnF,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAClC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,uDAAuD,EAAE,GAAG,EAAE;YAC/D,MAAM,MAAM,GAAG,aAAa,CAAC,+CAA+C,EAAE,OAAO,CAAC,CAAC;YACvF,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;QAC1C,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,8CAA8C,EAAE,GAAG,EAAE;YACtD,MAAM,MAAM,GAAG,aAAa,CAAC,0BAA0B,EAAE,mBAAmB,CAAC,CAAC;YAC9E,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;QAC3C,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,mDAAmD,EAAE,GAAG,EAAE;YAC3D,MAAM,MAAM,GAAG,aAAa,CAAC,gCAAgC,EAAE,eAAe,CAAC,CAAC;YAChF,iDAAiD;YACjD,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QACpC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,6CAA6C,EAAE,GAAG,EAAE;YACrD,MAAM,MAAM,GAAG,aAAa,CAAC,sCAAsC,EAAE,YAAY,CAAC,CAAC;YACnF,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC;QAClD,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,+BAA+B,EAAE,GAAG,EAAE;YACvC,MAAM,MAAM,GAAG,aAAa,CAAC,0CAA0C,EAAE,iBAAiB,CAAC,CAAC;YAC5F,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACjC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,0BAA0B,EAAE,GAAG,EAAE;YAClC,MAAM,MAAM,GAAG,aAAa,CAAC,6BAA6B,EAAE,eAAe,CAAC,CAAC;YAC7E,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QACrC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,+BAA+B,EAAE,GAAG,EAAE;YACvC,MAAM,MAAM,GAAG,aAAa,CAAC,yBAAyB,EAAE,YAAY,CAAC,CAAC;YACtE,+CAA+C;YAC/C,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QACnC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,0CAA0C,EAAE,GAAG,EAAE;YAClD,MAAM,MAAM,GAAG,aAAa,CAAC,qCAAqC,EAAE,QAAQ,CAAC,CAAC;YAC9E,qBAAqB;YACrB,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAClC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,mCAAmC,EAAE,GAAG,EAAE;YAC3C,MAAM,MAAM,GAAG,aAAa,CAAC,mCAAmC,EAAE,OAAO,CAAC,CAAC;YAC3E,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;QACvC,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { logger } from './logger.js';
|
|
2
|
+
/** Default timeout for favicon fetch requests (10 seconds) */
|
|
3
|
+
const FETCH_TIMEOUT_MS = 10000;
|
|
4
|
+
/** Maximum favicon file size (1MB) to prevent memory issues */
|
|
5
|
+
const MAX_FAVICON_SIZE = 1024 * 1024;
|
|
6
|
+
/**
|
|
7
|
+
* Fetch with timeout support
|
|
8
|
+
* @param url - URL to fetch
|
|
9
|
+
* @param timeoutMs - Timeout in milliseconds
|
|
10
|
+
* @returns Response object
|
|
11
|
+
*/
|
|
12
|
+
async function fetchWithTimeout(url, timeoutMs = FETCH_TIMEOUT_MS) {
|
|
13
|
+
const controller = new AbortController();
|
|
14
|
+
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
15
|
+
try {
|
|
16
|
+
const response = await fetch(url, { signal: controller.signal });
|
|
17
|
+
return response;
|
|
18
|
+
}
|
|
19
|
+
finally {
|
|
20
|
+
clearTimeout(timeoutId);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Fetch favicon for a URL with timeout and size limits
|
|
25
|
+
* @param url - The URL to fetch favicon for
|
|
26
|
+
* @returns Base64 encoded favicon data URL or undefined
|
|
27
|
+
*/
|
|
28
|
+
export async function fetchFavicon(url) {
|
|
29
|
+
try {
|
|
30
|
+
// Try standard favicon.ico location
|
|
31
|
+
const faviconUrl = new URL('/favicon.ico', url.origin);
|
|
32
|
+
const response = await fetchWithTimeout(faviconUrl.toString());
|
|
33
|
+
if (response.ok) {
|
|
34
|
+
// Check content length before downloading
|
|
35
|
+
const contentLength = response.headers.get('content-length');
|
|
36
|
+
if (contentLength && parseInt(contentLength, 10) > MAX_FAVICON_SIZE) {
|
|
37
|
+
logger.debug(`[Favicon] Favicon too large: ${contentLength} bytes`);
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
const buffer = await response.arrayBuffer();
|
|
41
|
+
if (buffer.byteLength > MAX_FAVICON_SIZE) {
|
|
42
|
+
logger.debug(`[Favicon] Favicon too large: ${buffer.byteLength} bytes`);
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
const base64 = Buffer.from(buffer).toString('base64');
|
|
46
|
+
const mimeType = response.headers.get('content-type') || 'image/x-icon';
|
|
47
|
+
return `data:${mimeType};base64,${base64}`;
|
|
48
|
+
}
|
|
49
|
+
// Try HTML head meta tags
|
|
50
|
+
const pageResponse = await fetchWithTimeout(url.toString());
|
|
51
|
+
const html = await pageResponse.text();
|
|
52
|
+
// Look for favicon in meta tags
|
|
53
|
+
const iconMatch = html.match(/<link[^>]*?rel=["'](?:shortcut )?icon["'][^>]*?href=["']([^"']+)["'][^>]*>/i) ||
|
|
54
|
+
html.match(/<link[^>]*?href=["']([^"']+)["'][^>]*?rel=["'](?:shortcut )?icon["'][^>]*>/i);
|
|
55
|
+
if (iconMatch) {
|
|
56
|
+
const iconUrl = new URL(iconMatch[1], url.origin);
|
|
57
|
+
const iconResponse = await fetchWithTimeout(iconUrl.toString());
|
|
58
|
+
if (iconResponse.ok) {
|
|
59
|
+
// Check content length before downloading
|
|
60
|
+
const contentLength = iconResponse.headers.get('content-length');
|
|
61
|
+
if (contentLength && parseInt(contentLength, 10) > MAX_FAVICON_SIZE) {
|
|
62
|
+
logger.debug(`[Favicon] Icon too large: ${contentLength} bytes`);
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
65
|
+
const buffer = await iconResponse.arrayBuffer();
|
|
66
|
+
if (buffer.byteLength > MAX_FAVICON_SIZE) {
|
|
67
|
+
logger.debug(`[Favicon] Icon too large: ${buffer.byteLength} bytes`);
|
|
68
|
+
return undefined;
|
|
69
|
+
}
|
|
70
|
+
const base64 = Buffer.from(buffer).toString('base64');
|
|
71
|
+
const mimeType = iconResponse.headers.get('content-type') || 'image/x-icon';
|
|
72
|
+
return `data:${mimeType};base64,${base64}`;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return undefined;
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
// Handle abort errors (timeout) differently
|
|
79
|
+
if (error instanceof Error && error.name === 'AbortError') {
|
|
80
|
+
logger.debug(`[Favicon] Timeout fetching favicon for ${url.origin}`);
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
logger.debug(`[Favicon] Error fetching favicon for ${url.origin}:`, error);
|
|
84
|
+
}
|
|
85
|
+
return undefined;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=favicon.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"favicon.js","sourceRoot":"","sources":["../../src/util/favicon.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { fetchFavicon } from './favicon.js';
|
|
2
|
+
describe('Favicon Utilities', () => {
|
|
3
|
+
beforeEach(() => {
|
|
4
|
+
fetchMock.resetMocks();
|
|
5
|
+
});
|
|
6
|
+
describe('fetchFavicon', () => {
|
|
7
|
+
it('should fetch standard favicon.ico', async () => {
|
|
8
|
+
const faviconData = new Uint8Array([0x00, 0x00, 0x01, 0x00]); // ICO header
|
|
9
|
+
fetchMock.mockResponseOnce(async () => ({
|
|
10
|
+
body: Buffer.from(faviconData).toString(),
|
|
11
|
+
headers: { 'content-type': 'image/x-icon' },
|
|
12
|
+
}));
|
|
13
|
+
const result = await fetchFavicon(new URL('https://example.com'));
|
|
14
|
+
expect(result).toBeDefined();
|
|
15
|
+
expect(result).toContain('data:image/x-icon;base64,');
|
|
16
|
+
expect(fetchMock).toHaveBeenCalledWith('https://example.com/favicon.ico', expect.objectContaining({ signal: expect.any(AbortSignal) }));
|
|
17
|
+
});
|
|
18
|
+
it('should return undefined when favicon.ico returns non-OK status and no meta tag found', async () => {
|
|
19
|
+
// favicon.ico returns 404
|
|
20
|
+
fetchMock.mockResponseOnce('', { status: 404 });
|
|
21
|
+
// HTML fallback - no icon link
|
|
22
|
+
fetchMock.mockResponseOnce('<html><head></head><body></body></html>');
|
|
23
|
+
const result = await fetchFavicon(new URL('https://example.com'));
|
|
24
|
+
expect(result).toBeUndefined();
|
|
25
|
+
});
|
|
26
|
+
it('should reject oversized favicons based on content-length header', async () => {
|
|
27
|
+
fetchMock.mockResponseOnce('', {
|
|
28
|
+
status: 200,
|
|
29
|
+
headers: {
|
|
30
|
+
'content-type': 'image/x-icon',
|
|
31
|
+
'content-length': '2000000', // 2MB, exceeds 1MB limit
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
const result = await fetchFavicon(new URL('https://example.com'));
|
|
35
|
+
expect(result).toBeUndefined();
|
|
36
|
+
});
|
|
37
|
+
it('should reject oversized favicons based on actual buffer size', async () => {
|
|
38
|
+
const oversizedData = new Uint8Array(1024 * 1024 + 1); // 1MB + 1 byte
|
|
39
|
+
fetchMock.mockResponseOnce(async () => ({
|
|
40
|
+
body: Buffer.from(oversizedData).toString(),
|
|
41
|
+
headers: { 'content-type': 'image/x-icon' },
|
|
42
|
+
}));
|
|
43
|
+
const result = await fetchFavicon(new URL('https://example.com'));
|
|
44
|
+
expect(result).toBeUndefined();
|
|
45
|
+
});
|
|
46
|
+
it('should fallback to HTML meta tag parsing when favicon.ico fails', async () => {
|
|
47
|
+
const faviconData = new Uint8Array([0x89, 0x50, 0x4e, 0x47]); // PNG header
|
|
48
|
+
// favicon.ico returns 404
|
|
49
|
+
fetchMock.mockResponseOnce('', { status: 404 });
|
|
50
|
+
// HTML page with icon link
|
|
51
|
+
fetchMock.mockResponseOnce('<html><head><link rel="icon" href="/icon.png"></head><body></body></html>');
|
|
52
|
+
// Fetch the icon from meta tag
|
|
53
|
+
fetchMock.mockResponseOnce(async () => ({
|
|
54
|
+
body: Buffer.from(faviconData).toString(),
|
|
55
|
+
headers: { 'content-type': 'image/png' },
|
|
56
|
+
}));
|
|
57
|
+
const result = await fetchFavicon(new URL('https://example.com'));
|
|
58
|
+
expect(result).toBeDefined();
|
|
59
|
+
expect(result).toContain('data:image/png;base64,');
|
|
60
|
+
});
|
|
61
|
+
it('should handle shortcut icon rel value', async () => {
|
|
62
|
+
const faviconData = new Uint8Array([0x00, 0x00, 0x01, 0x00]);
|
|
63
|
+
fetchMock.mockResponseOnce('', { status: 404 });
|
|
64
|
+
fetchMock.mockResponseOnce('<html><head><link rel="shortcut icon" href="/favicon.ico"></head></html>');
|
|
65
|
+
fetchMock.mockResponseOnce(async () => ({
|
|
66
|
+
body: Buffer.from(faviconData).toString(),
|
|
67
|
+
headers: { 'content-type': 'image/x-icon' },
|
|
68
|
+
}));
|
|
69
|
+
const result = await fetchFavicon(new URL('https://example.com'));
|
|
70
|
+
expect(result).toBeDefined();
|
|
71
|
+
});
|
|
72
|
+
it('should handle href before rel attribute order', async () => {
|
|
73
|
+
const faviconData = new Uint8Array([0x00, 0x00, 0x01, 0x00]);
|
|
74
|
+
fetchMock.mockResponseOnce('', { status: 404 });
|
|
75
|
+
fetchMock.mockResponseOnce('<html><head><link href="/my-icon.ico" rel="icon"></head></html>');
|
|
76
|
+
fetchMock.mockResponseOnce(async () => ({
|
|
77
|
+
body: Buffer.from(faviconData).toString(),
|
|
78
|
+
headers: { 'content-type': 'image/x-icon' },
|
|
79
|
+
}));
|
|
80
|
+
const result = await fetchFavicon(new URL('https://example.com'));
|
|
81
|
+
expect(result).toBeDefined();
|
|
82
|
+
});
|
|
83
|
+
it('should return undefined on network error', async () => {
|
|
84
|
+
fetchMock.mockRejectOnce(new Error('Network error'));
|
|
85
|
+
const result = await fetchFavicon(new URL('https://example.com'));
|
|
86
|
+
expect(result).toBeUndefined();
|
|
87
|
+
});
|
|
88
|
+
it('should return undefined on timeout (AbortError)', async () => {
|
|
89
|
+
const abortError = new Error('Aborted');
|
|
90
|
+
abortError.name = 'AbortError';
|
|
91
|
+
fetchMock.mockRejectOnce(abortError);
|
|
92
|
+
const result = await fetchFavicon(new URL('https://example.com'));
|
|
93
|
+
expect(result).toBeUndefined();
|
|
94
|
+
});
|
|
95
|
+
it('should handle response with content-type header', async () => {
|
|
96
|
+
const faviconData = new Uint8Array([0x00, 0x00, 0x01, 0x00]);
|
|
97
|
+
fetchMock.mockResponseOnce(async () => ({
|
|
98
|
+
body: Buffer.from(faviconData).toString(),
|
|
99
|
+
headers: { 'content-type': 'image/vnd.microsoft.icon' },
|
|
100
|
+
}));
|
|
101
|
+
const result = await fetchFavicon(new URL('https://example.com'));
|
|
102
|
+
expect(result).toBeDefined();
|
|
103
|
+
// Should use the content-type from the response
|
|
104
|
+
expect(result).toContain('data:image/vnd.microsoft.icon;base64,');
|
|
105
|
+
});
|
|
106
|
+
it('should handle relative icon URLs correctly', async () => {
|
|
107
|
+
const faviconData = new Uint8Array([0x89, 0x50, 0x4e, 0x47]);
|
|
108
|
+
fetchMock.mockResponseOnce('', { status: 404 });
|
|
109
|
+
fetchMock.mockResponseOnce('<html><head><link rel="icon" href="/assets/favicon.png"></head></html>');
|
|
110
|
+
fetchMock.mockResponseOnce(async () => ({
|
|
111
|
+
body: Buffer.from(faviconData).toString(),
|
|
112
|
+
headers: { 'content-type': 'image/png' },
|
|
113
|
+
}));
|
|
114
|
+
const result = await fetchFavicon(new URL('https://example.com/page'));
|
|
115
|
+
expect(result).toBeDefined();
|
|
116
|
+
expect(fetchMock).toHaveBeenCalledWith('https://example.com/assets/favicon.png', expect.any(Object));
|
|
117
|
+
});
|
|
118
|
+
it('should return undefined when icon from meta tag fails to load', async () => {
|
|
119
|
+
fetchMock.mockResponseOnce('', { status: 404 });
|
|
120
|
+
fetchMock.mockResponseOnce('<html><head><link rel="icon" href="/icon.png"></head></html>');
|
|
121
|
+
fetchMock.mockResponseOnce('', { status: 404 });
|
|
122
|
+
const result = await fetchFavicon(new URL('https://example.com'));
|
|
123
|
+
expect(result).toBeUndefined();
|
|
124
|
+
});
|
|
125
|
+
it('should reject oversized icons from meta tag', async () => {
|
|
126
|
+
fetchMock.mockResponseOnce('', { status: 404 });
|
|
127
|
+
fetchMock.mockResponseOnce('<html><head><link rel="icon" href="/icon.png"></head></html>');
|
|
128
|
+
fetchMock.mockResponseOnce('', {
|
|
129
|
+
status: 200,
|
|
130
|
+
headers: {
|
|
131
|
+
'content-type': 'image/png',
|
|
132
|
+
'content-length': '2000000',
|
|
133
|
+
},
|
|
134
|
+
});
|
|
135
|
+
const result = await fetchFavicon(new URL('https://example.com'));
|
|
136
|
+
expect(result).toBeUndefined();
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
//# sourceMappingURL=favicon.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"favicon.test.js","sourceRoot":"","sources":["../../src/util/favicon.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple logger that outputs to stderr (required for MCP servers)
|
|
3
|
+
* but with proper log level prefixes for clarity.
|
|
4
|
+
*
|
|
5
|
+
* MCP servers must keep stdout clean for JSON-RPC communication,
|
|
6
|
+
* so all logs go to stderr regardless of level.
|
|
7
|
+
*
|
|
8
|
+
* Security: All log output is automatically redacted to remove sensitive
|
|
9
|
+
* information like cookies, tokens, passwords, and API keys.
|
|
10
|
+
*/
|
|
11
|
+
export type LogLevel = 'debug' | 'info' | 'warn' | 'error';
|
|
12
|
+
export declare const logger: {
|
|
13
|
+
debug(message: string, ...args: unknown[]): void;
|
|
14
|
+
info(message: string, ...args: unknown[]): void;
|
|
15
|
+
warn(message: string, ...args: unknown[]): void;
|
|
16
|
+
error(message: string, ...args: unknown[]): void;
|
|
17
|
+
};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple logger that outputs to stderr (required for MCP servers)
|
|
3
|
+
* but with proper log level prefixes for clarity.
|
|
4
|
+
*
|
|
5
|
+
* MCP servers must keep stdout clean for JSON-RPC communication,
|
|
6
|
+
* so all logs go to stderr regardless of level.
|
|
7
|
+
*
|
|
8
|
+
* Security: All log output is automatically redacted to remove sensitive
|
|
9
|
+
* information like cookies, tokens, passwords, and API keys.
|
|
10
|
+
*/
|
|
11
|
+
import { redactForLogging } from './security.js';
|
|
12
|
+
const LOG_LEVEL_PRIORITY = {
|
|
13
|
+
debug: 0,
|
|
14
|
+
info: 1,
|
|
15
|
+
warn: 2,
|
|
16
|
+
error: 3,
|
|
17
|
+
};
|
|
18
|
+
// Default to 'info' level, can be changed via environment variable
|
|
19
|
+
const currentLevel = process.env.LOG_LEVEL || 'info';
|
|
20
|
+
function shouldLog(level) {
|
|
21
|
+
return LOG_LEVEL_PRIORITY[level] >= LOG_LEVEL_PRIORITY[currentLevel];
|
|
22
|
+
}
|
|
23
|
+
function formatArg(arg) {
|
|
24
|
+
if (arg instanceof Error) {
|
|
25
|
+
// Error objects don't serialize well with JSON.stringify
|
|
26
|
+
const errorStr = `${arg.name}: ${arg.message}${arg.stack ? `\n${arg.stack}` : ''}`;
|
|
27
|
+
return redactForLogging(errorStr);
|
|
28
|
+
}
|
|
29
|
+
if (typeof arg === 'object' && arg !== null) {
|
|
30
|
+
try {
|
|
31
|
+
const jsonStr = JSON.stringify(arg, null, 2);
|
|
32
|
+
return redactForLogging(jsonStr);
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return redactForLogging(String(arg));
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return redactForLogging(String(arg));
|
|
39
|
+
}
|
|
40
|
+
function formatMessage(level, message, ...args) {
|
|
41
|
+
const timestamp = new Date().toISOString();
|
|
42
|
+
const prefix = `[${timestamp}] [${level.toUpperCase()}]`;
|
|
43
|
+
// Redact the main message as well
|
|
44
|
+
const safeMessage = redactForLogging(message);
|
|
45
|
+
if (args.length > 0) {
|
|
46
|
+
return `${prefix} ${safeMessage} ${args.map(formatArg).join(' ')}`;
|
|
47
|
+
}
|
|
48
|
+
return `${prefix} ${safeMessage}`;
|
|
49
|
+
}
|
|
50
|
+
export const logger = {
|
|
51
|
+
debug(message, ...args) {
|
|
52
|
+
if (shouldLog('debug')) {
|
|
53
|
+
console.error(formatMessage('debug', message, ...args));
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
info(message, ...args) {
|
|
57
|
+
if (shouldLog('info')) {
|
|
58
|
+
console.error(formatMessage('info', message, ...args));
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
warn(message, ...args) {
|
|
62
|
+
if (shouldLog('warn')) {
|
|
63
|
+
console.error(formatMessage('warn', message, ...args));
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
error(message, ...args) {
|
|
67
|
+
if (shouldLog('error')) {
|
|
68
|
+
console.error(formatMessage('error', message, ...args));
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
//# sourceMappingURL=logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/util/logger.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|