@cosmocoder/mcp-web-docs 2.0.13 → 2.0.15

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 (57) hide show
  1. package/build/config.d.ts +0 -1
  2. package/build/config.js +0 -5
  3. package/build/config.js.map +1 -1
  4. package/build/config.test.js +1 -25
  5. package/build/config.test.js.map +1 -1
  6. package/build/crawler/base.test.js +1 -0
  7. package/build/crawler/base.test.js.map +1 -1
  8. package/build/crawler/content-extractor-types.d.ts +3 -0
  9. package/build/crawler/crawlee-crawler.js +14 -9
  10. package/build/crawler/crawlee-crawler.js.map +1 -1
  11. package/build/crawler/crawlee-crawler.test.js +58 -7
  12. package/build/crawler/crawlee-crawler.test.js.map +1 -1
  13. package/build/crawler/default-extractor.js +1 -0
  14. package/build/crawler/default-extractor.js.map +1 -1
  15. package/build/crawler/default-extractor.test.js +1 -0
  16. package/build/crawler/default-extractor.test.js.map +1 -1
  17. package/build/crawler/docs-crawler.test.js +18 -10
  18. package/build/crawler/docs-crawler.test.js.map +1 -1
  19. package/build/crawler/github-pages-extractor.js +2 -0
  20. package/build/crawler/github-pages-extractor.js.map +1 -1
  21. package/build/crawler/github-pages-extractor.test.js +2 -0
  22. package/build/crawler/github-pages-extractor.test.js.map +1 -1
  23. package/build/crawler/github.js +1 -0
  24. package/build/crawler/github.js.map +1 -1
  25. package/build/crawler/github.test.js +1 -0
  26. package/build/crawler/github.test.js.map +1 -1
  27. package/build/crawler/queue-manager.test.js +7 -0
  28. package/build/crawler/queue-manager.test.js.map +1 -1
  29. package/build/crawler/storybook-extractor.js +3 -0
  30. package/build/crawler/storybook-extractor.js.map +1 -1
  31. package/build/crawler/storybook-extractor.test.js +2 -0
  32. package/build/crawler/storybook-extractor.test.js.map +1 -1
  33. package/build/index.js +2 -10
  34. package/build/index.js.map +1 -1
  35. package/build/index.test.js +19 -5
  36. package/build/index.test.js.map +1 -1
  37. package/build/processor/markdown.d.ts +15 -6
  38. package/build/processor/markdown.js +7 -30
  39. package/build/processor/markdown.js.map +1 -1
  40. package/build/processor/markdown.test.js +39 -6
  41. package/build/processor/markdown.test.js.map +1 -1
  42. package/build/processor/processor.js +8 -26
  43. package/build/processor/processor.js.map +1 -1
  44. package/build/processor/processor.test.js +118 -172
  45. package/build/processor/processor.test.js.map +1 -1
  46. package/build/storage/storage.js +34 -34
  47. package/build/storage/storage.js.map +1 -1
  48. package/build/storage/storage.test.js +90 -3
  49. package/build/storage/storage.test.js.map +1 -1
  50. package/build/types.d.ts +4 -0
  51. package/package.json +2 -3
  52. package/build/processor/content.d.ts +0 -16
  53. package/build/processor/content.js +0 -287
  54. package/build/processor/content.js.map +0 -1
  55. package/build/processor/content.test.d.ts +0 -1
  56. package/build/processor/content.test.js +0 -369
  57. package/build/processor/content.test.js.map +0 -1
@@ -1,287 +0,0 @@
1
- import { Readability } from '@mozilla/readability';
2
- import { JSDOM } from 'jsdom';
3
- import { logger } from '../util/logger.js';
4
- function cleanText(text) {
5
- return text
6
- .replace(/\s+/g, ' ')
7
- .replace(/\n\s*\n/g, '\n\n')
8
- .trim();
9
- }
10
- /**
11
- * Extract text content from HTML while preserving code blocks with markdown fences.
12
- * This ensures code examples aren't lost when using Readability fallback.
13
- */
14
- function extractTextWithCodeBlocks(html) {
15
- const dom = new JSDOM(html);
16
- const doc = dom.window.document;
17
- // First, wrap all code blocks with markdown fences
18
- const codeElements = doc.querySelectorAll('pre, code');
19
- codeElements.forEach((el) => {
20
- // Skip inline <code> inside <pre> (already handled by parent)
21
- if (el.tagName === 'CODE' && el.parentElement?.tagName === 'PRE') {
22
- return;
23
- }
24
- const code = el.textContent?.trim();
25
- if (code && code.length > 0) {
26
- // Replace the element's content with fenced code
27
- const isBlock = el.tagName === 'PRE' || code.includes('\n');
28
- if (isBlock) {
29
- el.textContent = `\n\`\`\`\n${code}\n\`\`\`\n`;
30
- }
31
- else {
32
- el.textContent = `\`${code}\``;
33
- }
34
- }
35
- });
36
- // Now get the text content which includes our fenced markers
37
- return doc.body?.textContent || '';
38
- }
39
- function findMainContent(doc) {
40
- const selectors = [
41
- // Storybook specific selectors
42
- '[class*="story-content"]',
43
- '[class*="storybook-"]',
44
- '[class*="docs-content"]',
45
- '[class*="sbdocs-"]',
46
- '[class*="docblock-"]',
47
- // Jimdo UI specific selectors
48
- '[class*="docs-"]',
49
- '[class*="documentation"]',
50
- '[class*="content"]',
51
- '[class*="main"]',
52
- // Common documentation selectors
53
- 'main',
54
- '[role="main"]',
55
- '#root',
56
- '#app',
57
- '#__next',
58
- '#storybook-root',
59
- '.documentation',
60
- '.docs-content',
61
- '.markdown-body',
62
- 'article',
63
- '.article',
64
- '.content',
65
- '.page-content',
66
- '.docusaurus-content',
67
- '.vuepress-content',
68
- '.gatsby-content',
69
- '.mdx-content',
70
- '.nextra-content',
71
- '.nuxt-content',
72
- ];
73
- // Try each selector
74
- for (const selector of selectors) {
75
- const elements = Array.from(doc.querySelectorAll(selector));
76
- if (elements.length > 0) {
77
- // If multiple elements found, return the one with most content
78
- return elements.reduce((best, current) => {
79
- const bestLength = best.textContent?.length || 0;
80
- const currentLength = current.textContent?.length || 0;
81
- return currentLength > bestLength ? current : best;
82
- });
83
- }
84
- }
85
- // Fallback: try to find the element with the most content
86
- const candidates = Array.from(doc.body.children);
87
- if (candidates.length === 0) {
88
- return null;
89
- }
90
- return candidates.reduce((best, current) => {
91
- const bestLength = best.textContent?.length || 0;
92
- const currentLength = current.textContent?.length || 0;
93
- return currentLength > bestLength ? current : best;
94
- });
95
- }
96
- function getAllTextContent(element) {
97
- let content = '';
98
- // Handle code blocks specially
99
- if (element.tagName === 'PRE' || element.classList.contains('code')) {
100
- const code = element.textContent?.trim();
101
- if (code) {
102
- return '\n```\n' + code + '\n```\n';
103
- }
104
- return '';
105
- }
106
- // Handle lists specially
107
- if (element.tagName === 'UL' || element.tagName === 'OL') {
108
- const items = Array.from(element.querySelectorAll('li'))
109
- .map((li) => '- ' + li.textContent?.trim())
110
- .filter(Boolean)
111
- .join('\n');
112
- if (items) {
113
- return '\n' + items + '\n';
114
- }
115
- return '';
116
- }
117
- // Handle tables specially
118
- if (element.tagName === 'TABLE') {
119
- const rows = Array.from(element.querySelectorAll('tr'))
120
- .map((tr) => Array.from(tr.querySelectorAll('td, th'))
121
- .map((cell) => cell.textContent?.trim() || '')
122
- .join(' | '))
123
- .filter(Boolean)
124
- .join('\n');
125
- if (rows) {
126
- return '\n' + rows + '\n';
127
- }
128
- return '';
129
- }
130
- // Skip unwanted elements
131
- if (['SCRIPT', 'STYLE', 'NAV', 'HEADER', 'FOOTER'].includes(element.tagName)) {
132
- return '';
133
- }
134
- // Get text content of this element
135
- const text = element.textContent?.trim();
136
- if (text) {
137
- content += text + '\n';
138
- }
139
- return content;
140
- }
141
- function extractContentBetweenElements(start, end) {
142
- let content = '';
143
- let current = start;
144
- // Process all elements between start and end
145
- while (current && current !== end) {
146
- content += getAllTextContent(current);
147
- // Check children first (depth-first)
148
- if (current.firstElementChild && current !== start) {
149
- current = current.firstElementChild;
150
- }
151
- // Then try next sibling
152
- else if (current.nextElementSibling) {
153
- current = current.nextElementSibling;
154
- }
155
- // Finally try parent's next sibling
156
- else {
157
- let parent = current.parentElement;
158
- while (parent && parent !== end && !parent.nextElementSibling) {
159
- parent = parent.parentElement;
160
- }
161
- if (parent && parent !== end) {
162
- current = parent.nextElementSibling;
163
- }
164
- else {
165
- break;
166
- }
167
- }
168
- }
169
- return cleanText(content);
170
- }
171
- function extractSections(mainContent) {
172
- const headerSelectors = [
173
- 'h1',
174
- 'h2',
175
- 'h3',
176
- 'h4',
177
- '[class*="heading"]',
178
- '[class*="title"]',
179
- '[class*="sbdocs-h"]',
180
- '[class*="story-title"]',
181
- '[class*="docblock-title"]',
182
- '[class*="docs-title"]',
183
- ];
184
- const headers = Array.from(mainContent.querySelectorAll(headerSelectors.join(','))).filter((header) => {
185
- const text = header.textContent?.trim();
186
- return text && text.length > 0;
187
- });
188
- if (headers.length === 0) {
189
- // No headers found, treat entire content as one section
190
- const title = mainContent.querySelector('h1, [class*="title"]')?.textContent?.trim() || 'Content';
191
- const body = getAllTextContent(mainContent);
192
- if (body.length > 0) {
193
- return [{ title, body: cleanText(body) }];
194
- }
195
- return [];
196
- }
197
- const components = [];
198
- // Process content before first header
199
- if (headers[0].previousElementSibling) {
200
- const introContent = extractContentBetweenElements(mainContent.firstElementChild, headers[0]);
201
- if (introContent.length > 0) {
202
- components.push({
203
- title: 'Introduction',
204
- body: introContent,
205
- });
206
- }
207
- }
208
- // Process sections between headers
209
- headers.forEach((header, index) => {
210
- const nextHeader = headers[index + 1];
211
- const title = header.textContent?.trim() || '';
212
- const body = extractContentBetweenElements(header, nextHeader);
213
- if (body.length > 0) {
214
- components.push({ title, body });
215
- }
216
- });
217
- // Filter out empty components and normalize
218
- return components
219
- .filter((comp) => comp.body.length > 0)
220
- .map((comp) => ({
221
- title: comp.title,
222
- body: cleanText(comp.body),
223
- }));
224
- }
225
- export async function processHtmlContent(page) {
226
- try {
227
- logger.debug(`[ContentProcessor] Processing content for ${page.url}`);
228
- const dom = new JSDOM(page.content);
229
- const doc = dom.window.document;
230
- // Try to find main content first
231
- const mainContent = findMainContent(doc);
232
- // If no main content found, use Readability
233
- if (!mainContent) {
234
- logger.debug('[ContentProcessor] No main content found, trying Readability');
235
- const reader = new Readability(doc);
236
- const readability = reader.parse();
237
- if (!readability) {
238
- logger.debug(`[ContentProcessor] No content could be extracted from ${page.url}`);
239
- return undefined;
240
- }
241
- // Use HTML content to preserve code blocks with markdown fences
242
- // This prevents code examples from triggering false positive security detections
243
- const contentWithCodeBlocks = readability.content ? extractTextWithCodeBlocks(readability.content) : readability.textContent || '';
244
- return {
245
- article: {
246
- url: page.url,
247
- path: page.path,
248
- title: readability.title || page.path,
249
- components: [
250
- {
251
- title: readability.title || 'Content',
252
- body: cleanText(contentWithCodeBlocks),
253
- },
254
- ],
255
- },
256
- content: cleanText(contentWithCodeBlocks),
257
- };
258
- }
259
- logger.debug('[ContentProcessor] Found main content, extracting sections');
260
- // Extract sections from main content
261
- const components = extractSections(mainContent);
262
- if (components.length === 0) {
263
- logger.debug(`[ContentProcessor] No valid content sections found in ${page.url}`);
264
- return undefined;
265
- }
266
- logger.debug(`[ContentProcessor] Extracted ${components.length} sections`);
267
- const article = {
268
- url: page.url,
269
- path: page.path,
270
- title: page.title || components[0].title,
271
- components,
272
- };
273
- return {
274
- article,
275
- content: components
276
- .map((comp) => `${comp.title}\n\n${comp.body}`)
277
- .join('\n\n')
278
- .trim(),
279
- };
280
- }
281
- catch (error) {
282
- logger.debug('[ContentProcessor] Error processing HTML content:', error);
283
- logger.debug('[ContentProcessor] Error details:', error instanceof Error ? error.stack : error);
284
- return undefined;
285
- }
286
- }
287
- //# sourceMappingURL=content.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"content.js","sourceRoot":"","sources":["../../src/processor/content.ts"],"names":[],"mappings":"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"}
@@ -1 +0,0 @@
1
- export {};
@@ -1,369 +0,0 @@
1
- import { processHtmlContent } from './content.js';
2
- describe('HTML Content Processor', () => {
3
- describe('processHtmlContent', () => {
4
- it('should process simple HTML content', async () => {
5
- const page = {
6
- url: 'https://example.com/docs/page',
7
- path: '/docs/page',
8
- title: 'Documentation Page',
9
- content: `
10
- <!DOCTYPE html>
11
- <html>
12
- <head><title>Page Title</title></head>
13
- <body>
14
- <main>
15
- <h1>Welcome to the Documentation</h1>
16
- <p>This is the introduction paragraph.</p>
17
- <h2>Getting Started</h2>
18
- <p>Here's how to get started.</p>
19
- </main>
20
- </body>
21
- </html>
22
- `,
23
- };
24
- const result = await processHtmlContent(page);
25
- expect(result).toBeDefined();
26
- expect(result?.article.url).toBe(page.url);
27
- expect(result?.article.components.length).toBeGreaterThan(0);
28
- });
29
- it('should extract content from article tags', async () => {
30
- const page = {
31
- url: 'https://example.com/blog/post',
32
- path: '/blog/post',
33
- title: 'Blog Post',
34
- content: `
35
- <html>
36
- <body>
37
- <nav>Navigation items</nav>
38
- <article>
39
- <h1>Article Title</h1>
40
- <p>Article content goes here.</p>
41
- <h2>Section One</h2>
42
- <p>More content.</p>
43
- </article>
44
- <footer>Footer content</footer>
45
- </body>
46
- </html>
47
- `,
48
- };
49
- const result = await processHtmlContent(page);
50
- expect(result).toBeDefined();
51
- expect(result?.content).toContain('Article content');
52
- // Should not include nav/footer content
53
- expect(result?.content).not.toContain('Navigation items');
54
- });
55
- it('should handle documentation-specific selectors', async () => {
56
- const page = {
57
- url: 'https://example.com/docs',
58
- path: '/docs',
59
- title: 'Docs',
60
- content: `
61
- <html>
62
- <body>
63
- <div class="sidebar">Sidebar</div>
64
- <div class="markdown-body">
65
- <h1>Documentation</h1>
66
- <p>Main documentation content.</p>
67
- </div>
68
- </body>
69
- </html>
70
- `,
71
- };
72
- const result = await processHtmlContent(page);
73
- expect(result).toBeDefined();
74
- expect(result?.content).toContain('Main documentation content');
75
- });
76
- it('should preserve code blocks', async () => {
77
- const page = {
78
- url: 'https://example.com/code',
79
- path: '/code',
80
- title: 'Code',
81
- content: `
82
- <html>
83
- <body>
84
- <main>
85
- <h1>Code Examples</h1>
86
- <pre><code>function example() {
87
- return "Hello";
88
- }</code></pre>
89
- </main>
90
- </body>
91
- </html>
92
- `,
93
- };
94
- const result = await processHtmlContent(page);
95
- expect(result).toBeDefined();
96
- expect(result?.content).toContain('function example');
97
- });
98
- it('should handle lists properly', async () => {
99
- const page = {
100
- url: 'https://example.com/list',
101
- path: '/list',
102
- title: 'List',
103
- content: `
104
- <html>
105
- <body>
106
- <main>
107
- <h1>Features</h1>
108
- <ul>
109
- <li>Feature one</li>
110
- <li>Feature two</li>
111
- <li>Feature three</li>
112
- </ul>
113
- </main>
114
- </body>
115
- </html>
116
- `,
117
- };
118
- const result = await processHtmlContent(page);
119
- expect(result).toBeDefined();
120
- expect(result?.content).toContain('Feature one');
121
- expect(result?.content).toContain('Feature two');
122
- });
123
- it('should handle tables', async () => {
124
- const page = {
125
- url: 'https://example.com/table',
126
- path: '/table',
127
- title: 'Table',
128
- content: `
129
- <html>
130
- <body>
131
- <main>
132
- <h1>API Reference</h1>
133
- <table>
134
- <tr><th>Method</th><th>Description</th></tr>
135
- <tr><td>GET</td><td>Retrieve data</td></tr>
136
- <tr><td>POST</td><td>Create data</td></tr>
137
- </table>
138
- </main>
139
- </body>
140
- </html>
141
- `,
142
- };
143
- const result = await processHtmlContent(page);
144
- expect(result).toBeDefined();
145
- expect(result?.content).toContain('GET');
146
- expect(result?.content).toContain('POST');
147
- });
148
- it('should skip script and style tags', async () => {
149
- const page = {
150
- url: 'https://example.com/scripts',
151
- path: '/scripts',
152
- title: 'Scripts',
153
- content: `
154
- <html>
155
- <head>
156
- <style>.class { color: red; }</style>
157
- </head>
158
- <body>
159
- <script>alert('hello');</script>
160
- <main>
161
- <h1>Content</h1>
162
- <p>Actual content here.</p>
163
- </main>
164
- <script>console.log('end');</script>
165
- </body>
166
- </html>
167
- `,
168
- };
169
- const result = await processHtmlContent(page);
170
- expect(result).toBeDefined();
171
- expect(result?.content).not.toContain('alert');
172
- expect(result?.content).not.toContain('color: red');
173
- expect(result?.content).toContain('Actual content');
174
- });
175
- it('should use Readability as fallback', async () => {
176
- const page = {
177
- url: 'https://example.com/blog',
178
- path: '/blog',
179
- title: 'Blog',
180
- content: `
181
- <html>
182
- <head><title>Blog Post</title></head>
183
- <body>
184
- <div>
185
- <p>This is a paragraph of content.</p>
186
- <p>Another paragraph with more information.</p>
187
- <p>Yet another paragraph to provide enough content for Readability.</p>
188
- <p>Additional paragraph for proper content extraction.</p>
189
- <p>Final paragraph of meaningful content.</p>
190
- </div>
191
- </body>
192
- </html>
193
- `,
194
- };
195
- const result = await processHtmlContent(page);
196
- // Should still extract something even without clear main content
197
- expect(result).toBeDefined();
198
- });
199
- it('should return undefined for pages without extractable content', async () => {
200
- const page = {
201
- url: 'https://example.com/empty',
202
- path: '/empty',
203
- title: 'Empty',
204
- content: `
205
- <html>
206
- <body>
207
- <nav>Just navigation</nav>
208
- </body>
209
- </html>
210
- `,
211
- };
212
- const result = await processHtmlContent(page);
213
- // May return undefined or minimal content depending on parser
214
- if (result) {
215
- expect(result.content.length).toBeLessThan(100);
216
- }
217
- });
218
- it('should handle Storybook-specific classes', async () => {
219
- const page = {
220
- url: 'https://storybook.example.com/docs',
221
- path: '/docs',
222
- title: 'Storybook',
223
- content: `
224
- <html>
225
- <body>
226
- <div class="sbdocs-wrapper">
227
- <div class="sbdocs-content">
228
- <h1 class="sbdocs-h1">Component Documentation</h1>
229
- <div class="docblock-description">
230
- <p>Description of the component.</p>
231
- </div>
232
- </div>
233
- </div>
234
- </body>
235
- </html>
236
- `,
237
- };
238
- const result = await processHtmlContent(page);
239
- expect(result).toBeDefined();
240
- expect(result?.content).toContain('Component Documentation');
241
- });
242
- it('should handle React app root containers', async () => {
243
- const page = {
244
- url: 'https://example.com/react-app',
245
- path: '/react-app',
246
- title: 'React App',
247
- content: `
248
- <html>
249
- <body>
250
- <div id="root">
251
- <div>
252
- <h1>React App Content</h1>
253
- <p>This is rendered by React.</p>
254
- </div>
255
- </div>
256
- </body>
257
- </html>
258
- `,
259
- };
260
- const result = await processHtmlContent(page);
261
- expect(result).toBeDefined();
262
- expect(result?.content).toContain('React App Content');
263
- });
264
- it('should handle Next.js containers', async () => {
265
- const page = {
266
- url: 'https://example.com/nextjs',
267
- path: '/nextjs',
268
- title: 'Next.js',
269
- content: `
270
- <html>
271
- <body>
272
- <div id="__next">
273
- <main>
274
- <h1>Next.js Page</h1>
275
- <p>Content from Next.js.</p>
276
- </main>
277
- </div>
278
- </body>
279
- </html>
280
- `,
281
- };
282
- const result = await processHtmlContent(page);
283
- expect(result).toBeDefined();
284
- expect(result?.content).toContain('Next.js Page');
285
- });
286
- it('should handle multiple heading levels for section extraction', async () => {
287
- const page = {
288
- url: 'https://example.com/headings',
289
- path: '/headings',
290
- title: 'Headings',
291
- content: `
292
- <html>
293
- <body>
294
- <main>
295
- <h1>Main Title</h1>
296
- <p>Intro text.</p>
297
- <h2>Section 1</h2>
298
- <p>Section 1 content.</p>
299
- <h3>Subsection 1.1</h3>
300
- <p>Subsection content.</p>
301
- <h2>Section 2</h2>
302
- <p>Section 2 content.</p>
303
- <h4>Deep Section</h4>
304
- <p>Deep content.</p>
305
- </main>
306
- </body>
307
- </html>
308
- `,
309
- };
310
- const result = await processHtmlContent(page);
311
- expect(result).toBeDefined();
312
- expect(result?.article.components.length).toBeGreaterThan(3);
313
- });
314
- it('should handle malformed HTML gracefully', async () => {
315
- const page = {
316
- url: 'https://example.com/malformed',
317
- path: '/malformed',
318
- title: 'Malformed',
319
- content: `
320
- <html>
321
- <body>
322
- <div>
323
- <p>Unclosed paragraph
324
- <p>Another unclosed
325
- <span>Nested <b>incorrectly</span></b>
326
- <main>
327
- <h1>Still Works</h1>
328
- <p>Content here.</p>
329
- </main>
330
- </div>
331
- </body>
332
- </html>
333
- `,
334
- };
335
- // Should not throw
336
- const result = await processHtmlContent(page);
337
- expect(result).toBeDefined();
338
- });
339
- it('should clean text and remove extra whitespace', async () => {
340
- const page = {
341
- url: 'https://example.com/whitespace',
342
- path: '/whitespace',
343
- title: 'Whitespace',
344
- content: `
345
- <html>
346
- <body>
347
- <main>
348
- <h1>Title</h1>
349
- <p>Text with extra spaces.</p>
350
- <p>
351
-
352
-
353
- Multiple newlines here.
354
-
355
-
356
- </p>
357
- </main>
358
- </body>
359
- </html>
360
- `,
361
- };
362
- const result = await processHtmlContent(page);
363
- expect(result).toBeDefined();
364
- // Should normalize whitespace
365
- expect(result?.content).not.toMatch(/\s{3,}/);
366
- });
367
- });
368
- });
369
- //# sourceMappingURL=content.test.js.map