@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.
Files changed (240) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +368 -0
  3. package/build/__mocks__/embeddings.d.ts +17 -0
  4. package/build/__mocks__/embeddings.js +66 -0
  5. package/build/__mocks__/embeddings.js.map +1 -0
  6. package/build/config.d.ts +44 -0
  7. package/build/config.js +158 -0
  8. package/build/config.js.map +1 -0
  9. package/build/config.test.d.ts +1 -0
  10. package/build/config.test.js +165 -0
  11. package/build/config.test.js.map +1 -0
  12. package/build/crawler/auth.d.ts +128 -0
  13. package/build/crawler/auth.js +546 -0
  14. package/build/crawler/auth.js.map +1 -0
  15. package/build/crawler/auth.test.d.ts +1 -0
  16. package/build/crawler/auth.test.js +174 -0
  17. package/build/crawler/auth.test.js.map +1 -0
  18. package/build/crawler/base.d.ts +24 -0
  19. package/build/crawler/base.js +149 -0
  20. package/build/crawler/base.js.map +1 -0
  21. package/build/crawler/base.test.d.ts +1 -0
  22. package/build/crawler/base.test.js +234 -0
  23. package/build/crawler/base.test.js.map +1 -0
  24. package/build/crawler/browser-config.d.ts +2 -0
  25. package/build/crawler/browser-config.js +29 -0
  26. package/build/crawler/browser-config.js.map +1 -0
  27. package/build/crawler/browser-config.test.d.ts +1 -0
  28. package/build/crawler/browser-config.test.js +56 -0
  29. package/build/crawler/browser-config.test.js.map +1 -0
  30. package/build/crawler/cheerio.d.ts +11 -0
  31. package/build/crawler/cheerio.js +134 -0
  32. package/build/crawler/cheerio.js.map +1 -0
  33. package/build/crawler/chromium.d.ts +21 -0
  34. package/build/crawler/chromium.js +596 -0
  35. package/build/crawler/chromium.js.map +1 -0
  36. package/build/crawler/content-extractor-types.d.ts +25 -0
  37. package/build/crawler/content-extractor-types.js +2 -0
  38. package/build/crawler/content-extractor-types.js.map +1 -0
  39. package/build/crawler/content-extractors.d.ts +9 -0
  40. package/build/crawler/content-extractors.js +9 -0
  41. package/build/crawler/content-extractors.js.map +1 -0
  42. package/build/crawler/content-utils.d.ts +2 -0
  43. package/build/crawler/content-utils.js +22 -0
  44. package/build/crawler/content-utils.js.map +1 -0
  45. package/build/crawler/content-utils.test.d.ts +1 -0
  46. package/build/crawler/content-utils.test.js +99 -0
  47. package/build/crawler/content-utils.test.js.map +1 -0
  48. package/build/crawler/crawlee-crawler.d.ts +63 -0
  49. package/build/crawler/crawlee-crawler.js +342 -0
  50. package/build/crawler/crawlee-crawler.js.map +1 -0
  51. package/build/crawler/crawlee-crawler.test.d.ts +1 -0
  52. package/build/crawler/crawlee-crawler.test.js +280 -0
  53. package/build/crawler/crawlee-crawler.test.js.map +1 -0
  54. package/build/crawler/default-extractor.d.ts +4 -0
  55. package/build/crawler/default-extractor.js +26 -0
  56. package/build/crawler/default-extractor.js.map +1 -0
  57. package/build/crawler/default-extractor.test.d.ts +1 -0
  58. package/build/crawler/default-extractor.test.js +200 -0
  59. package/build/crawler/default-extractor.test.js.map +1 -0
  60. package/build/crawler/default.d.ts +11 -0
  61. package/build/crawler/default.js +138 -0
  62. package/build/crawler/default.js.map +1 -0
  63. package/build/crawler/docs-crawler.d.ts +26 -0
  64. package/build/crawler/docs-crawler.js +97 -0
  65. package/build/crawler/docs-crawler.js.map +1 -0
  66. package/build/crawler/docs-crawler.test.d.ts +1 -0
  67. package/build/crawler/docs-crawler.test.js +185 -0
  68. package/build/crawler/docs-crawler.test.js.map +1 -0
  69. package/build/crawler/factory.d.ts +6 -0
  70. package/build/crawler/factory.js +83 -0
  71. package/build/crawler/factory.js.map +1 -0
  72. package/build/crawler/github-pages-extractor.d.ts +4 -0
  73. package/build/crawler/github-pages-extractor.js +33 -0
  74. package/build/crawler/github-pages-extractor.js.map +1 -0
  75. package/build/crawler/github-pages-extractor.test.d.ts +1 -0
  76. package/build/crawler/github-pages-extractor.test.js +184 -0
  77. package/build/crawler/github-pages-extractor.test.js.map +1 -0
  78. package/build/crawler/github.d.ts +20 -0
  79. package/build/crawler/github.js +181 -0
  80. package/build/crawler/github.js.map +1 -0
  81. package/build/crawler/github.test.d.ts +1 -0
  82. package/build/crawler/github.test.js +326 -0
  83. package/build/crawler/github.test.js.map +1 -0
  84. package/build/crawler/puppeteer.d.ts +16 -0
  85. package/build/crawler/puppeteer.js +191 -0
  86. package/build/crawler/puppeteer.js.map +1 -0
  87. package/build/crawler/queue-manager.d.ts +43 -0
  88. package/build/crawler/queue-manager.js +169 -0
  89. package/build/crawler/queue-manager.js.map +1 -0
  90. package/build/crawler/queue-manager.test.d.ts +1 -0
  91. package/build/crawler/queue-manager.test.js +509 -0
  92. package/build/crawler/queue-manager.test.js.map +1 -0
  93. package/build/crawler/site-rules.d.ts +11 -0
  94. package/build/crawler/site-rules.js +104 -0
  95. package/build/crawler/site-rules.js.map +1 -0
  96. package/build/crawler/site-rules.test.d.ts +1 -0
  97. package/build/crawler/site-rules.test.js +139 -0
  98. package/build/crawler/site-rules.test.js.map +1 -0
  99. package/build/crawler/storybook-extractor.d.ts +34 -0
  100. package/build/crawler/storybook-extractor.js +767 -0
  101. package/build/crawler/storybook-extractor.js.map +1 -0
  102. package/build/crawler/storybook-extractor.test.d.ts +1 -0
  103. package/build/crawler/storybook-extractor.test.js +491 -0
  104. package/build/crawler/storybook-extractor.test.js.map +1 -0
  105. package/build/embeddings/fastembed.d.ts +25 -0
  106. package/build/embeddings/fastembed.js +188 -0
  107. package/build/embeddings/fastembed.js.map +1 -0
  108. package/build/embeddings/fastembed.test.d.ts +1 -0
  109. package/build/embeddings/fastembed.test.js +307 -0
  110. package/build/embeddings/fastembed.test.js.map +1 -0
  111. package/build/embeddings/openai.d.ts +8 -0
  112. package/build/embeddings/openai.js +56 -0
  113. package/build/embeddings/openai.js.map +1 -0
  114. package/build/embeddings/types.d.ts +4 -0
  115. package/build/embeddings/types.js +2 -0
  116. package/build/embeddings/types.js.map +1 -0
  117. package/build/index.d.ts +2 -0
  118. package/build/index.js +1007 -0
  119. package/build/index.js.map +1 -0
  120. package/build/index.test.d.ts +1 -0
  121. package/build/index.test.js +364 -0
  122. package/build/index.test.js.map +1 -0
  123. package/build/indexing/queue-manager.d.ts +36 -0
  124. package/build/indexing/queue-manager.js +86 -0
  125. package/build/indexing/queue-manager.js.map +1 -0
  126. package/build/indexing/queue-manager.test.d.ts +1 -0
  127. package/build/indexing/queue-manager.test.js +257 -0
  128. package/build/indexing/queue-manager.test.js.map +1 -0
  129. package/build/indexing/status.d.ts +39 -0
  130. package/build/indexing/status.js +207 -0
  131. package/build/indexing/status.js.map +1 -0
  132. package/build/indexing/status.test.d.ts +1 -0
  133. package/build/indexing/status.test.js +246 -0
  134. package/build/indexing/status.test.js.map +1 -0
  135. package/build/processor/content.d.ts +16 -0
  136. package/build/processor/content.js +286 -0
  137. package/build/processor/content.js.map +1 -0
  138. package/build/processor/content.test.d.ts +1 -0
  139. package/build/processor/content.test.js +369 -0
  140. package/build/processor/content.test.js.map +1 -0
  141. package/build/processor/markdown.d.ts +11 -0
  142. package/build/processor/markdown.js +256 -0
  143. package/build/processor/markdown.js.map +1 -0
  144. package/build/processor/markdown.test.d.ts +1 -0
  145. package/build/processor/markdown.test.js +312 -0
  146. package/build/processor/markdown.test.js.map +1 -0
  147. package/build/processor/metadata-parser.d.ts +37 -0
  148. package/build/processor/metadata-parser.js +245 -0
  149. package/build/processor/metadata-parser.js.map +1 -0
  150. package/build/processor/metadata-parser.test.d.ts +1 -0
  151. package/build/processor/metadata-parser.test.js +357 -0
  152. package/build/processor/metadata-parser.test.js.map +1 -0
  153. package/build/processor/processor.d.ts +8 -0
  154. package/build/processor/processor.js +190 -0
  155. package/build/processor/processor.js.map +1 -0
  156. package/build/processor/processor.test.d.ts +1 -0
  157. package/build/processor/processor.test.js +357 -0
  158. package/build/processor/processor.test.js.map +1 -0
  159. package/build/rag/cache.d.ts +10 -0
  160. package/build/rag/cache.js +10 -0
  161. package/build/rag/cache.js.map +1 -0
  162. package/build/rag/code-generator.d.ts +11 -0
  163. package/build/rag/code-generator.js +30 -0
  164. package/build/rag/code-generator.js.map +1 -0
  165. package/build/rag/context-assembler.d.ts +23 -0
  166. package/build/rag/context-assembler.js +113 -0
  167. package/build/rag/context-assembler.js.map +1 -0
  168. package/build/rag/docs-search.d.ts +55 -0
  169. package/build/rag/docs-search.js +380 -0
  170. package/build/rag/docs-search.js.map +1 -0
  171. package/build/rag/pipeline.d.ts +26 -0
  172. package/build/rag/pipeline.js +91 -0
  173. package/build/rag/pipeline.js.map +1 -0
  174. package/build/rag/query-processor.d.ts +14 -0
  175. package/build/rag/query-processor.js +57 -0
  176. package/build/rag/query-processor.js.map +1 -0
  177. package/build/rag/reranker.d.ts +55 -0
  178. package/build/rag/reranker.js +210 -0
  179. package/build/rag/reranker.js.map +1 -0
  180. package/build/rag/response-generator.d.ts +20 -0
  181. package/build/rag/response-generator.js +101 -0
  182. package/build/rag/response-generator.js.map +1 -0
  183. package/build/rag/retriever.d.ts +19 -0
  184. package/build/rag/retriever.js +111 -0
  185. package/build/rag/retriever.js.map +1 -0
  186. package/build/rag/validator.d.ts +22 -0
  187. package/build/rag/validator.js +128 -0
  188. package/build/rag/validator.js.map +1 -0
  189. package/build/rag/version-manager.d.ts +23 -0
  190. package/build/rag/version-manager.js +98 -0
  191. package/build/rag/version-manager.js.map +1 -0
  192. package/build/setupTests.d.ts +4 -0
  193. package/build/setupTests.js +50 -0
  194. package/build/setupTests.js.map +1 -0
  195. package/build/storage/storage.d.ts +38 -0
  196. package/build/storage/storage.js +700 -0
  197. package/build/storage/storage.js.map +1 -0
  198. package/build/storage/storage.test.d.ts +1 -0
  199. package/build/storage/storage.test.js +338 -0
  200. package/build/storage/storage.test.js.map +1 -0
  201. package/build/types/rag.d.ts +27 -0
  202. package/build/types/rag.js +2 -0
  203. package/build/types/rag.js.map +1 -0
  204. package/build/types.d.ts +120 -0
  205. package/build/types.js +2 -0
  206. package/build/types.js.map +1 -0
  207. package/build/util/content-utils.d.ts +31 -0
  208. package/build/util/content-utils.js +120 -0
  209. package/build/util/content-utils.js.map +1 -0
  210. package/build/util/content.d.ts +1 -0
  211. package/build/util/content.js +16 -0
  212. package/build/util/content.js.map +1 -0
  213. package/build/util/docs.d.ts +1 -0
  214. package/build/util/docs.js +26 -0
  215. package/build/util/docs.js.map +1 -0
  216. package/build/util/docs.test.d.ts +1 -0
  217. package/build/util/docs.test.js +49 -0
  218. package/build/util/docs.test.js.map +1 -0
  219. package/build/util/favicon.d.ts +6 -0
  220. package/build/util/favicon.js +88 -0
  221. package/build/util/favicon.js.map +1 -0
  222. package/build/util/favicon.test.d.ts +1 -0
  223. package/build/util/favicon.test.js +140 -0
  224. package/build/util/favicon.test.js.map +1 -0
  225. package/build/util/logger.d.ts +17 -0
  226. package/build/util/logger.js +72 -0
  227. package/build/util/logger.js.map +1 -0
  228. package/build/util/logger.test.d.ts +1 -0
  229. package/build/util/logger.test.js +46 -0
  230. package/build/util/logger.test.js.map +1 -0
  231. package/build/util/security.d.ts +312 -0
  232. package/build/util/security.js +719 -0
  233. package/build/util/security.js.map +1 -0
  234. package/build/util/security.test.d.ts +1 -0
  235. package/build/util/security.test.js +524 -0
  236. package/build/util/security.test.js.map +1 -0
  237. package/build/util/site-detector.d.ts +22 -0
  238. package/build/util/site-detector.js +42 -0
  239. package/build/util/site-detector.js.map +1 -0
  240. 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,6 @@
1
+ /**
2
+ * Fetch favicon for a URL with timeout and size limits
3
+ * @param url - The URL to fetch favicon for
4
+ * @returns Base64 encoded favicon data URL or undefined
5
+ */
6
+ export declare function fetchFavicon(url: URL): Promise<string | undefined>;
@@ -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 {};