@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,509 @@
1
+ import { QueueManager } from './queue-manager.js';
2
+ const { mockRequestQueue, mockDataset } = vi.hoisted(() => ({
3
+ mockRequestQueue: {
4
+ drop: vi.fn().mockResolvedValue(undefined),
5
+ addRequest: vi.fn().mockResolvedValue(undefined),
6
+ getInfo: vi.fn().mockResolvedValue({
7
+ pendingRequestCount: 5,
8
+ handledRequestCount: 10,
9
+ totalRequestCount: 15,
10
+ }),
11
+ },
12
+ mockDataset: {
13
+ drop: vi.fn().mockResolvedValue(undefined),
14
+ pushData: vi.fn().mockResolvedValue(undefined),
15
+ },
16
+ }));
17
+ vi.mock('crawlee', () => ({
18
+ RequestQueue: {
19
+ open: vi.fn().mockResolvedValue(mockRequestQueue),
20
+ },
21
+ Dataset: {
22
+ open: vi.fn().mockResolvedValue(mockDataset),
23
+ },
24
+ EnqueueStrategy: {
25
+ SameDomain: 'same-domain',
26
+ },
27
+ }));
28
+ describe('QueueManager', () => {
29
+ let queueManager;
30
+ beforeEach(() => {
31
+ vi.clearAllMocks();
32
+ queueManager = new QueueManager();
33
+ });
34
+ describe('initialize', () => {
35
+ it('should initialize with a URL', async () => {
36
+ await queueManager.initialize('https://example.com/docs');
37
+ expect(mockRequestQueue.drop).toHaveBeenCalled();
38
+ expect(mockRequestQueue.addRequest).toHaveBeenCalledWith({
39
+ url: 'https://example.com/docs',
40
+ uniqueKey: '/docs',
41
+ });
42
+ });
43
+ it('should generate correct unique key from URL', async () => {
44
+ await queueManager.initialize('https://example.com/path/to/page?query=1');
45
+ expect(mockRequestQueue.addRequest).toHaveBeenCalledWith({
46
+ url: 'https://example.com/path/to/page?query=1',
47
+ uniqueKey: '/path/to/page?query=1',
48
+ });
49
+ });
50
+ it('should clear existing dataset', async () => {
51
+ await queueManager.initialize('https://example.com');
52
+ expect(mockDataset.drop).toHaveBeenCalled();
53
+ });
54
+ });
55
+ describe('handleQueueAndLinks', () => {
56
+ const mockLog = {
57
+ info: vi.fn(),
58
+ debug: vi.fn(),
59
+ warning: vi.fn(),
60
+ error: vi.fn(),
61
+ };
62
+ const mockRule = {
63
+ type: 'default',
64
+ extractor: { extractContent: vi.fn() },
65
+ detect: vi.fn().mockResolvedValue(true),
66
+ };
67
+ beforeEach(async () => {
68
+ await queueManager.initialize('https://example.com');
69
+ });
70
+ it('should log queue status', async () => {
71
+ const mockEnqueueLinks = vi.fn().mockResolvedValue({
72
+ processedRequests: [],
73
+ });
74
+ await queueManager.handleQueueAndLinks(mockEnqueueLinks, mockLog, mockRule);
75
+ expect(mockLog.info).toHaveBeenCalledWith('Queue status:', {
76
+ pendingCount: 5,
77
+ handledCount: 10,
78
+ totalCount: 15,
79
+ });
80
+ });
81
+ it('should enqueue links with same-domain strategy', async () => {
82
+ const mockEnqueueLinks = vi.fn().mockResolvedValue({
83
+ processedRequests: [{ uniqueKey: '/page1' }, { uniqueKey: '/page2' }],
84
+ });
85
+ await queueManager.handleQueueAndLinks(mockEnqueueLinks, mockLog, mockRule);
86
+ expect(mockEnqueueLinks).toHaveBeenCalledWith(expect.objectContaining({
87
+ strategy: 'same-domain',
88
+ }));
89
+ });
90
+ it('should use link selectors from rule when provided', async () => {
91
+ const ruleWithSelectors = {
92
+ ...mockRule,
93
+ linkSelectors: ['.nav a', '.content a'],
94
+ };
95
+ const mockEnqueueLinks = vi.fn().mockResolvedValue({
96
+ processedRequests: [],
97
+ });
98
+ await queueManager.handleQueueAndLinks(mockEnqueueLinks, mockLog, ruleWithSelectors);
99
+ expect(mockEnqueueLinks).toHaveBeenCalledWith(expect.objectContaining({
100
+ selector: '.nav a, .content a',
101
+ }));
102
+ });
103
+ it('should log enqueued links count', async () => {
104
+ const mockEnqueueLinks = vi.fn().mockResolvedValue({
105
+ processedRequests: [{ uniqueKey: '/page1' }, { uniqueKey: '/page2' }],
106
+ });
107
+ await queueManager.handleQueueAndLinks(mockEnqueueLinks, mockLog, mockRule);
108
+ expect(mockLog.info).toHaveBeenCalledWith('Enqueued links:', {
109
+ processedCount: 2,
110
+ urls: ['/page1', '/page2'],
111
+ });
112
+ });
113
+ it('should transform request URLs to use pathname as unique key', async () => {
114
+ let capturedOptions = null;
115
+ const mockEnqueueLinks = vi.fn().mockImplementation((options) => {
116
+ capturedOptions = options;
117
+ return Promise.resolve({ processedRequests: [] });
118
+ });
119
+ await queueManager.handleQueueAndLinks(mockEnqueueLinks, mockLog, mockRule);
120
+ expect(capturedOptions).not.toBeNull();
121
+ const transformFn = capturedOptions.transformRequestFunction;
122
+ expect(transformFn).toBeDefined();
123
+ if (transformFn) {
124
+ const transformed = transformFn({
125
+ url: 'https://example.com/new/page?param=1',
126
+ uniqueKey: 'original',
127
+ });
128
+ expect(transformed).toEqual(expect.objectContaining({
129
+ url: 'https://example.com/new/page?param=1',
130
+ uniqueKey: '/new/page?param=1',
131
+ }));
132
+ }
133
+ });
134
+ it('should skip anchor links with hash fragments', async () => {
135
+ let capturedOptions = null;
136
+ const mockEnqueueLinks = vi.fn().mockImplementation((options) => {
137
+ capturedOptions = options;
138
+ return Promise.resolve({ processedRequests: [] });
139
+ });
140
+ await queueManager.handleQueueAndLinks(mockEnqueueLinks, mockLog, mockRule);
141
+ expect(capturedOptions).not.toBeNull();
142
+ const transformFn = capturedOptions.transformRequestFunction;
143
+ expect(transformFn).toBeDefined();
144
+ if (transformFn) {
145
+ // Test that anchor links are filtered out (return false)
146
+ const anchorResult = transformFn({
147
+ url: 'https://example.com/page#section',
148
+ uniqueKey: 'original',
149
+ });
150
+ expect(anchorResult).toBe(false);
151
+ }
152
+ });
153
+ it('should strip hash from URL when returning transformed request', async () => {
154
+ let capturedOptions = null;
155
+ const mockEnqueueLinks = vi.fn().mockImplementation((options) => {
156
+ capturedOptions = options;
157
+ return Promise.resolve({ processedRequests: [] });
158
+ });
159
+ await queueManager.handleQueueAndLinks(mockEnqueueLinks, mockLog, mockRule);
160
+ const transformFn = capturedOptions.transformRequestFunction;
161
+ if (transformFn) {
162
+ // URL without hash should be returned with clean URL
163
+ const transformed = transformFn({
164
+ url: 'https://example.com/page',
165
+ uniqueKey: 'original',
166
+ });
167
+ expect(transformed).toEqual(expect.objectContaining({
168
+ url: 'https://example.com/page',
169
+ uniqueKey: '/page',
170
+ }));
171
+ }
172
+ });
173
+ });
174
+ describe('path prefix filtering', () => {
175
+ const mockLog = {
176
+ info: vi.fn(),
177
+ debug: vi.fn(),
178
+ warning: vi.fn(),
179
+ error: vi.fn(),
180
+ };
181
+ const mockRule = {
182
+ type: 'default',
183
+ extractor: { extractContent: vi.fn() },
184
+ detect: vi.fn().mockResolvedValue(true),
185
+ };
186
+ it('should initialize with path prefix', async () => {
187
+ await queueManager.initialize('https://example.com/docs/api', '/docs/api');
188
+ // The path prefix should be stored (we can verify through transform function behavior)
189
+ expect(queueManager.getFilteredByPathCount()).toBe(0);
190
+ });
191
+ it('should allow URLs within path prefix', async () => {
192
+ await queueManager.initialize('https://example.com/docs', '/docs');
193
+ let capturedOptions = null;
194
+ const mockEnqueueLinks = vi.fn().mockImplementation((options) => {
195
+ capturedOptions = options;
196
+ return Promise.resolve({ processedRequests: [] });
197
+ });
198
+ await queueManager.handleQueueAndLinks(mockEnqueueLinks, mockLog, mockRule);
199
+ const transformFn = capturedOptions.transformRequestFunction;
200
+ if (transformFn) {
201
+ // Exact match
202
+ const exactMatch = transformFn({
203
+ url: 'https://example.com/docs',
204
+ uniqueKey: 'original',
205
+ });
206
+ expect(exactMatch).not.toBe(false);
207
+ // Subpath
208
+ const subpath = transformFn({
209
+ url: 'https://example.com/docs/api/v2',
210
+ uniqueKey: 'original',
211
+ });
212
+ expect(subpath).not.toBe(false);
213
+ }
214
+ });
215
+ it('should filter URLs outside path prefix', async () => {
216
+ await queueManager.initialize('https://example.com/docs', '/docs');
217
+ let capturedOptions = null;
218
+ const mockEnqueueLinks = vi.fn().mockImplementation((options) => {
219
+ capturedOptions = options;
220
+ return Promise.resolve({ processedRequests: [] });
221
+ });
222
+ await queueManager.handleQueueAndLinks(mockEnqueueLinks, mockLog, mockRule);
223
+ const transformFn = capturedOptions.transformRequestFunction;
224
+ if (transformFn) {
225
+ // Different path entirely
226
+ const differentPath = transformFn({
227
+ url: 'https://example.com/blog/post',
228
+ uniqueKey: 'original',
229
+ });
230
+ expect(differentPath).toBe(false);
231
+ }
232
+ });
233
+ it('should not match paths that only start with prefix string', async () => {
234
+ await queueManager.initialize('https://example.com/docs', '/docs');
235
+ let capturedOptions = null;
236
+ const mockEnqueueLinks = vi.fn().mockImplementation((options) => {
237
+ capturedOptions = options;
238
+ return Promise.resolve({ processedRequests: [] });
239
+ });
240
+ await queueManager.handleQueueAndLinks(mockEnqueueLinks, mockLog, mockRule);
241
+ const transformFn = capturedOptions.transformRequestFunction;
242
+ if (transformFn) {
243
+ // /documentation starts with /docs but is NOT a subpath of /docs
244
+ const similarButDifferent = transformFn({
245
+ url: 'https://example.com/documentation',
246
+ uniqueKey: 'original',
247
+ });
248
+ expect(similarButDifferent).toBe(false);
249
+ }
250
+ });
251
+ it('should track filtered URL count', async () => {
252
+ await queueManager.initialize('https://example.com/docs', '/docs');
253
+ let capturedOptions = null;
254
+ const mockEnqueueLinks = vi.fn().mockImplementation((options) => {
255
+ capturedOptions = options;
256
+ return Promise.resolve({ processedRequests: [] });
257
+ });
258
+ await queueManager.handleQueueAndLinks(mockEnqueueLinks, mockLog, mockRule);
259
+ const transformFn = capturedOptions.transformRequestFunction;
260
+ if (transformFn) {
261
+ // Filter some URLs
262
+ transformFn({ url: 'https://example.com/blog', uniqueKey: 'a' });
263
+ transformFn({ url: 'https://example.com/about', uniqueKey: 'b' });
264
+ }
265
+ expect(queueManager.getFilteredByPathCount()).toBe(2);
266
+ });
267
+ it('should strip hash from initial URL', async () => {
268
+ await queueManager.initialize('https://example.com/docs#intro');
269
+ expect(mockRequestQueue.addRequest).toHaveBeenCalledWith({
270
+ url: 'https://example.com/docs',
271
+ uniqueKey: '/docs',
272
+ });
273
+ });
274
+ });
275
+ describe('hostname filtering', () => {
276
+ const mockLog = {
277
+ info: vi.fn(),
278
+ debug: vi.fn(),
279
+ warning: vi.fn(),
280
+ error: vi.fn(),
281
+ };
282
+ const mockRule = {
283
+ type: 'default',
284
+ extractor: { extractContent: vi.fn() },
285
+ detect: vi.fn().mockResolvedValue(true),
286
+ };
287
+ it('should allow URLs with exact hostname match', async () => {
288
+ await queueManager.initialize('https://docs.example.com/api');
289
+ let capturedOptions = null;
290
+ const mockEnqueueLinks = vi.fn().mockImplementation((options) => {
291
+ capturedOptions = options;
292
+ return Promise.resolve({ processedRequests: [] });
293
+ });
294
+ await queueManager.handleQueueAndLinks(mockEnqueueLinks, mockLog, mockRule);
295
+ const transformFn = capturedOptions.transformRequestFunction;
296
+ if (transformFn) {
297
+ const result = transformFn({
298
+ url: 'https://docs.example.com/other',
299
+ uniqueKey: 'original',
300
+ });
301
+ expect(result).not.toBe(false);
302
+ }
303
+ });
304
+ it('should allow URLs with subdomain of starting hostname', async () => {
305
+ await queueManager.initialize('https://docs.example.com/api');
306
+ let capturedOptions = null;
307
+ const mockEnqueueLinks = vi.fn().mockImplementation((options) => {
308
+ capturedOptions = options;
309
+ return Promise.resolve({ processedRequests: [] });
310
+ });
311
+ await queueManager.handleQueueAndLinks(mockEnqueueLinks, mockLog, mockRule);
312
+ const transformFn = capturedOptions.transformRequestFunction;
313
+ if (transformFn) {
314
+ // api.docs.example.com is a subdomain of docs.example.com - should be allowed
315
+ const result = transformFn({
316
+ url: 'https://api.docs.example.com/endpoint',
317
+ uniqueKey: 'original',
318
+ });
319
+ expect(result).not.toBe(false);
320
+ }
321
+ });
322
+ it('should filter URLs with sibling subdomain', async () => {
323
+ await queueManager.initialize('https://docs.example.com/api');
324
+ let capturedOptions = null;
325
+ const mockEnqueueLinks = vi.fn().mockImplementation((options) => {
326
+ capturedOptions = options;
327
+ return Promise.resolve({ processedRequests: [] });
328
+ });
329
+ await queueManager.handleQueueAndLinks(mockEnqueueLinks, mockLog, mockRule);
330
+ const transformFn = capturedOptions.transformRequestFunction;
331
+ if (transformFn) {
332
+ // python.example.com is a sibling subdomain, not a subdomain of docs.example.com
333
+ const result = transformFn({
334
+ url: 'https://python.example.com/guide',
335
+ uniqueKey: 'original',
336
+ });
337
+ expect(result).toBe(false);
338
+ }
339
+ });
340
+ it('should filter URLs with parent domain', async () => {
341
+ await queueManager.initialize('https://docs.example.com/api');
342
+ let capturedOptions = null;
343
+ const mockEnqueueLinks = vi.fn().mockImplementation((options) => {
344
+ capturedOptions = options;
345
+ return Promise.resolve({ processedRequests: [] });
346
+ });
347
+ await queueManager.handleQueueAndLinks(mockEnqueueLinks, mockLog, mockRule);
348
+ const transformFn = capturedOptions.transformRequestFunction;
349
+ if (transformFn) {
350
+ // example.com is the parent domain of docs.example.com
351
+ const result = transformFn({
352
+ url: 'https://example.com/home',
353
+ uniqueKey: 'original',
354
+ });
355
+ expect(result).toBe(false);
356
+ }
357
+ });
358
+ it('should track filtered hostname count', async () => {
359
+ await queueManager.initialize('https://docs.example.com/api');
360
+ let capturedOptions = null;
361
+ const mockEnqueueLinks = vi.fn().mockImplementation((options) => {
362
+ capturedOptions = options;
363
+ return Promise.resolve({ processedRequests: [] });
364
+ });
365
+ await queueManager.handleQueueAndLinks(mockEnqueueLinks, mockLog, mockRule);
366
+ const transformFn = capturedOptions.transformRequestFunction;
367
+ if (transformFn) {
368
+ // Filter some URLs with wrong hostnames
369
+ transformFn({ url: 'https://python.example.com/a', uniqueKey: 'a' });
370
+ transformFn({ url: 'https://other.example.com/b', uniqueKey: 'b' });
371
+ }
372
+ expect(queueManager.getFilteredByHostnameCount()).toBe(2);
373
+ });
374
+ it('should handle case-insensitive hostname matching', async () => {
375
+ await queueManager.initialize('https://Docs.Example.Com/api');
376
+ let capturedOptions = null;
377
+ const mockEnqueueLinks = vi.fn().mockImplementation((options) => {
378
+ capturedOptions = options;
379
+ return Promise.resolve({ processedRequests: [] });
380
+ });
381
+ await queueManager.handleQueueAndLinks(mockEnqueueLinks, mockLog, mockRule);
382
+ const transformFn = capturedOptions.transformRequestFunction;
383
+ if (transformFn) {
384
+ const result = transformFn({
385
+ url: 'https://docs.example.com/other',
386
+ uniqueKey: 'original',
387
+ });
388
+ expect(result).not.toBe(false);
389
+ }
390
+ });
391
+ });
392
+ describe('addResult and processBatch', () => {
393
+ beforeEach(async () => {
394
+ await queueManager.initialize('https://example.com');
395
+ });
396
+ it('should add results', () => {
397
+ const result = {
398
+ url: 'https://example.com/page',
399
+ path: '/page',
400
+ content: 'Test content',
401
+ title: 'Test Page',
402
+ };
403
+ queueManager.addResult(result);
404
+ expect(queueManager.hasEnoughResults()).toBe(false);
405
+ });
406
+ it('should process batch and return results', async () => {
407
+ const result = {
408
+ url: 'https://example.com/page',
409
+ path: '/page',
410
+ content: 'Test content',
411
+ title: 'Test Page',
412
+ };
413
+ queueManager.addResult(result);
414
+ const processed = await queueManager.processBatch();
415
+ expect(processed).toHaveLength(1);
416
+ expect(processed[0]).toEqual(result);
417
+ expect(mockDataset.pushData).toHaveBeenCalled();
418
+ });
419
+ it('should return empty array when no results', async () => {
420
+ const processed = await queueManager.processBatch();
421
+ expect(processed).toHaveLength(0);
422
+ });
423
+ it('should clear results after processing', async () => {
424
+ queueManager.addResult({
425
+ url: 'https://example.com/page',
426
+ path: '/page',
427
+ content: 'Test',
428
+ title: 'Test',
429
+ });
430
+ await queueManager.processBatch();
431
+ const secondBatch = await queueManager.processBatch();
432
+ expect(secondBatch).toHaveLength(0);
433
+ });
434
+ it('should push data to dataset in chunks', async () => {
435
+ // Add 7 results
436
+ for (let i = 0; i < 7; i++) {
437
+ queueManager.addResult({
438
+ url: `https://example.com/page${i}`,
439
+ path: `/page${i}`,
440
+ content: `Content ${i}`,
441
+ title: `Page ${i}`,
442
+ });
443
+ }
444
+ await queueManager.processBatch();
445
+ // Should be pushed in chunks of 5
446
+ expect(mockDataset.pushData).toHaveBeenCalledTimes(2);
447
+ });
448
+ });
449
+ describe('hasEnoughResults', () => {
450
+ beforeEach(async () => {
451
+ await queueManager.initialize('https://example.com');
452
+ });
453
+ it('should return false when below batch size', () => {
454
+ for (let i = 0; i < 10; i++) {
455
+ queueManager.addResult({
456
+ url: `https://example.com/page${i}`,
457
+ path: `/page${i}`,
458
+ content: `Content ${i}`,
459
+ title: `Page ${i}`,
460
+ });
461
+ }
462
+ expect(queueManager.hasEnoughResults()).toBe(false);
463
+ });
464
+ it('should return true when at batch size', () => {
465
+ for (let i = 0; i < 20; i++) {
466
+ queueManager.addResult({
467
+ url: `https://example.com/page${i}`,
468
+ path: `/page${i}`,
469
+ content: `Content ${i}`,
470
+ title: `Page ${i}`,
471
+ });
472
+ }
473
+ expect(queueManager.hasEnoughResults()).toBe(true);
474
+ });
475
+ });
476
+ describe('getRequestQueue', () => {
477
+ it('should return null before initialization', () => {
478
+ expect(queueManager.getRequestQueue()).toBeNull();
479
+ });
480
+ it('should return request queue after initialization', async () => {
481
+ await queueManager.initialize('https://example.com');
482
+ expect(queueManager.getRequestQueue()).toBe(mockRequestQueue);
483
+ });
484
+ });
485
+ describe('cleanup', () => {
486
+ it('should clear results and drop queue', async () => {
487
+ await queueManager.initialize('https://example.com');
488
+ queueManager.addResult({
489
+ url: 'https://example.com/page',
490
+ path: '/page',
491
+ content: 'Test',
492
+ title: 'Test',
493
+ });
494
+ await queueManager.cleanup();
495
+ expect(mockRequestQueue.drop).toHaveBeenCalled();
496
+ expect(queueManager.getRequestQueue()).toBeNull();
497
+ // Results should be cleared
498
+ const processed = await queueManager.processBatch();
499
+ expect(processed).toHaveLength(0);
500
+ });
501
+ it('should handle cleanup errors gracefully', async () => {
502
+ await queueManager.initialize('https://example.com');
503
+ mockRequestQueue.drop.mockRejectedValueOnce(new Error('Drop failed'));
504
+ // Should not throw
505
+ await expect(queueManager.cleanup()).resolves.toBeUndefined();
506
+ });
507
+ });
508
+ });
509
+ //# sourceMappingURL=queue-manager.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queue-manager.test.js","sourceRoot":"","sources":["../../src/crawler/queue-manager.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import { Log } from 'crawlee';
2
+ import { Page } from 'playwright';
3
+ import { ContentExtractor } from './content-extractor-types.js';
4
+ export interface SiteDetectionRule {
5
+ type: string;
6
+ extractor: ContentExtractor;
7
+ detect: (page: Page) => Promise<boolean>;
8
+ prepare?: (page: Page, log: Log) => Promise<void>;
9
+ linkSelectors?: string[];
10
+ }
11
+ export declare const siteRules: SiteDetectionRule[];
@@ -0,0 +1,104 @@
1
+ import { contentExtractors } from './content-extractors.js';
2
+ export const siteRules = [
3
+ {
4
+ type: 'storybook',
5
+ extractor: contentExtractors.storybook,
6
+ detect: async (page) => {
7
+ return page.evaluate(() => {
8
+ return !!(document.querySelector('#storybook-root, .sbdocs, [data-nodetype="root"]') ||
9
+ document.querySelector('meta[name="storybook-version"]') ||
10
+ document.baseURI?.includes('path=/docs/') ||
11
+ document.baseURI?.includes('path=/story/') ||
12
+ window.__STORYBOOK_CLIENT_API__);
13
+ });
14
+ },
15
+ prepare: async (page, log) => {
16
+ await Promise.all([
17
+ page.waitForLoadState('networkidle', { timeout: 5000 }).catch(() => log.debug('Network idle timeout - continuing anyway')),
18
+ page
19
+ .waitForSelector('.sbdocs-content, #docs-root, .docs-story, [class*="story-"]', {
20
+ timeout: 5000,
21
+ })
22
+ .catch(() => log.debug('No Storybook content found in main page')),
23
+ ]);
24
+ // Wait for sidebar to be ready
25
+ await page.waitForSelector('[class*="sidebar"]', { timeout: 5000 }).catch(() => log.debug('No sidebar found'));
26
+ // First expand all section buttons
27
+ await page.evaluate(() => {
28
+ const buttons = Array.from(document.querySelectorAll('button.sidebar-subheading-action'));
29
+ buttons.forEach((button) => button.click());
30
+ });
31
+ // Wait for any new content to appear
32
+ await page.waitForTimeout(500);
33
+ // Then expand any remaining collapsed sections
34
+ await page.evaluate(() => {
35
+ const expandButtons = Array.from(document.querySelectorAll('[aria-expanded="false"]'));
36
+ expandButtons.forEach((button) => button.click());
37
+ });
38
+ // Wait for all animations and content updates to complete
39
+ await page.waitForTimeout(1000);
40
+ // Scroll to bottom to trigger lazy loading of ArgTypes tables
41
+ await page.evaluate(async () => {
42
+ // Find the content iframe
43
+ const iframe = document.querySelector('iframe');
44
+ const contentDoc = iframe?.contentDocument || document;
45
+ // Scroll to bottom to load all content
46
+ const scrollContainer = contentDoc.querySelector('.sbdocs-content, #docs-root, body');
47
+ if (scrollContainer) {
48
+ scrollContainer.scrollTo(0, scrollContainer.scrollHeight);
49
+ await new Promise((resolve) => setTimeout(resolve, 500));
50
+ scrollContainer.scrollTo(0, 0);
51
+ }
52
+ });
53
+ // Click on "Show code" buttons to reveal code examples
54
+ await page.evaluate(() => {
55
+ const showCodeButtons = Array.from(document.querySelectorAll('button')).filter((btn) => btn.textContent?.toLowerCase().includes('show code'));
56
+ showCodeButtons.slice(0, 3).forEach((btn) => btn.click()); // Limit to first 3
57
+ });
58
+ await page.waitForTimeout(500);
59
+ // Expand ArgTypes table rows and "Show more" buttons
60
+ await page.evaluate(async () => {
61
+ const iframe = document.querySelector('iframe');
62
+ const contentDoc = iframe?.contentDocument || document;
63
+ // Click expand buttons in args table
64
+ const expandBtns = contentDoc.querySelectorAll('[class*="argstable"] button[aria-expanded="false"], ' + '[class*="argtable"] button[aria-expanded="false"]');
65
+ expandBtns.forEach((btn) => btn.click());
66
+ // Click all "Show X more..." buttons to reveal full type lists
67
+ const showMoreBtns = Array.from(contentDoc.querySelectorAll('button')).filter((btn) => btn.textContent?.includes('Show') && btn.textContent?.includes('more'));
68
+ for (const btn of showMoreBtns) {
69
+ btn.click();
70
+ await new Promise((resolve) => setTimeout(resolve, 100));
71
+ }
72
+ });
73
+ await page.waitForTimeout(500);
74
+ // Log the number of links found for debugging
75
+ const linkCount = await page.evaluate(() => {
76
+ return document.querySelectorAll('.sidebar-item a, [data-nodetype="story"] a, [data-nodetype="document"] a').length;
77
+ });
78
+ log.debug(`Found ${linkCount} sidebar links after expansion`);
79
+ },
80
+ linkSelectors: [
81
+ '.sidebar-item a',
82
+ '[data-nodetype="root"] a',
83
+ '[data-nodetype="group"] a',
84
+ '[data-nodetype="document"] a',
85
+ '[data-nodetype="story"] a',
86
+ '[data-item-id] a',
87
+ ],
88
+ },
89
+ {
90
+ type: 'github',
91
+ extractor: contentExtractors.github,
92
+ detect: async (page) => {
93
+ return page.evaluate(() => {
94
+ return (window.location.hostname.includes('github.io') && document.querySelector('.markdown-body, .site-footer, .page-header') !== null);
95
+ });
96
+ },
97
+ },
98
+ {
99
+ type: 'default',
100
+ extractor: contentExtractors.default,
101
+ detect: async () => true,
102
+ },
103
+ ];
104
+ //# sourceMappingURL=site-rules.js.map