afterburn-cli 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 (188) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +281 -0
  3. package/dist/ai/gemini-client.d.ts +21 -0
  4. package/dist/ai/gemini-client.js +105 -0
  5. package/dist/ai/gemini-client.js.map +1 -0
  6. package/dist/ai/index.d.ts +1 -0
  7. package/dist/ai/index.js +3 -0
  8. package/dist/ai/index.js.map +1 -0
  9. package/dist/analysis/diagnosis-schema.d.ts +106 -0
  10. package/dist/analysis/diagnosis-schema.js +54 -0
  11. package/dist/analysis/diagnosis-schema.js.map +1 -0
  12. package/dist/analysis/error-analyzer.d.ts +9 -0
  13. package/dist/analysis/error-analyzer.js +573 -0
  14. package/dist/analysis/error-analyzer.js.map +1 -0
  15. package/dist/analysis/index.d.ts +4 -0
  16. package/dist/analysis/index.js +6 -0
  17. package/dist/analysis/index.js.map +1 -0
  18. package/dist/analysis/source-mapper.d.ts +19 -0
  19. package/dist/analysis/source-mapper.js +329 -0
  20. package/dist/analysis/source-mapper.js.map +1 -0
  21. package/dist/analysis/ui-auditor.d.ts +9 -0
  22. package/dist/analysis/ui-auditor.js +104 -0
  23. package/dist/analysis/ui-auditor.js.map +1 -0
  24. package/dist/artifacts/artifact-storage.d.ts +44 -0
  25. package/dist/artifacts/artifact-storage.js +99 -0
  26. package/dist/artifacts/artifact-storage.js.map +1 -0
  27. package/dist/artifacts/index.d.ts +1 -0
  28. package/dist/artifacts/index.js +3 -0
  29. package/dist/artifacts/index.js.map +1 -0
  30. package/dist/browser/browser-manager.d.ts +45 -0
  31. package/dist/browser/browser-manager.js +88 -0
  32. package/dist/browser/browser-manager.js.map +1 -0
  33. package/dist/browser/challenge-detector.d.ts +10 -0
  34. package/dist/browser/challenge-detector.js +58 -0
  35. package/dist/browser/challenge-detector.js.map +1 -0
  36. package/dist/browser/cookie-dismisser.d.ts +18 -0
  37. package/dist/browser/cookie-dismisser.js +76 -0
  38. package/dist/browser/cookie-dismisser.js.map +1 -0
  39. package/dist/browser/index.d.ts +4 -0
  40. package/dist/browser/index.js +6 -0
  41. package/dist/browser/index.js.map +1 -0
  42. package/dist/browser/stealth-browser.d.ts +13 -0
  43. package/dist/browser/stealth-browser.js +59 -0
  44. package/dist/browser/stealth-browser.js.map +1 -0
  45. package/dist/cli/commander-cli.d.ts +2 -0
  46. package/dist/cli/commander-cli.js +150 -0
  47. package/dist/cli/commander-cli.js.map +1 -0
  48. package/dist/cli/doctor.d.ts +34 -0
  49. package/dist/cli/doctor.js +124 -0
  50. package/dist/cli/doctor.js.map +1 -0
  51. package/dist/cli/first-run.d.ts +6 -0
  52. package/dist/cli/first-run.js +58 -0
  53. package/dist/cli/first-run.js.map +1 -0
  54. package/dist/cli/index.d.ts +3 -0
  55. package/dist/cli/index.js +5 -0
  56. package/dist/cli/index.js.map +1 -0
  57. package/dist/cli/progress.d.ts +11 -0
  58. package/dist/cli/progress.js +30 -0
  59. package/dist/cli/progress.js.map +1 -0
  60. package/dist/core/engine.d.ts +33 -0
  61. package/dist/core/engine.js +269 -0
  62. package/dist/core/engine.js.map +1 -0
  63. package/dist/core/index.d.ts +3 -0
  64. package/dist/core/index.js +4 -0
  65. package/dist/core/index.js.map +1 -0
  66. package/dist/core/validation.d.ts +52 -0
  67. package/dist/core/validation.js +228 -0
  68. package/dist/core/validation.js.map +1 -0
  69. package/dist/discovery/crawler.d.ts +58 -0
  70. package/dist/discovery/crawler.js +240 -0
  71. package/dist/discovery/crawler.js.map +1 -0
  72. package/dist/discovery/discovery-pipeline.d.ts +22 -0
  73. package/dist/discovery/discovery-pipeline.js +256 -0
  74. package/dist/discovery/discovery-pipeline.js.map +1 -0
  75. package/dist/discovery/element-mapper.d.ts +21 -0
  76. package/dist/discovery/element-mapper.js +422 -0
  77. package/dist/discovery/element-mapper.js.map +1 -0
  78. package/dist/discovery/index.d.ts +8 -0
  79. package/dist/discovery/index.js +8 -0
  80. package/dist/discovery/index.js.map +1 -0
  81. package/dist/discovery/link-validator.d.ts +15 -0
  82. package/dist/discovery/link-validator.js +137 -0
  83. package/dist/discovery/link-validator.js.map +1 -0
  84. package/dist/discovery/sitemap-builder.d.ts +19 -0
  85. package/dist/discovery/sitemap-builder.js +166 -0
  86. package/dist/discovery/sitemap-builder.js.map +1 -0
  87. package/dist/discovery/spa-detector.d.ts +12 -0
  88. package/dist/discovery/spa-detector.js +271 -0
  89. package/dist/discovery/spa-detector.js.map +1 -0
  90. package/dist/execution/error-detector.d.ts +10 -0
  91. package/dist/execution/error-detector.js +87 -0
  92. package/dist/execution/error-detector.js.map +1 -0
  93. package/dist/execution/evidence-capture.d.ts +8 -0
  94. package/dist/execution/evidence-capture.js +37 -0
  95. package/dist/execution/evidence-capture.js.map +1 -0
  96. package/dist/execution/index.d.ts +5 -0
  97. package/dist/execution/index.js +7 -0
  98. package/dist/execution/index.js.map +1 -0
  99. package/dist/execution/step-handlers.d.ts +48 -0
  100. package/dist/execution/step-handlers.js +349 -0
  101. package/dist/execution/step-handlers.js.map +1 -0
  102. package/dist/execution/test-data.d.ts +50 -0
  103. package/dist/execution/test-data.js +160 -0
  104. package/dist/execution/test-data.js.map +1 -0
  105. package/dist/execution/workflow-executor.d.ts +56 -0
  106. package/dist/execution/workflow-executor.js +331 -0
  107. package/dist/execution/workflow-executor.js.map +1 -0
  108. package/dist/index.d.ts +2 -0
  109. package/dist/index.js +5 -0
  110. package/dist/index.js.map +1 -0
  111. package/dist/mcp/entry.d.ts +2 -0
  112. package/dist/mcp/entry.js +5 -0
  113. package/dist/mcp/entry.js.map +1 -0
  114. package/dist/mcp/index.d.ts +2 -0
  115. package/dist/mcp/index.js +4 -0
  116. package/dist/mcp/index.js.map +1 -0
  117. package/dist/mcp/server.d.ts +3 -0
  118. package/dist/mcp/server.js +19 -0
  119. package/dist/mcp/server.js.map +1 -0
  120. package/dist/mcp/tools.d.ts +2 -0
  121. package/dist/mcp/tools.js +162 -0
  122. package/dist/mcp/tools.js.map +1 -0
  123. package/dist/planning/heuristic-planner.d.ts +7 -0
  124. package/dist/planning/heuristic-planner.js +238 -0
  125. package/dist/planning/heuristic-planner.js.map +1 -0
  126. package/dist/planning/index.d.ts +3 -0
  127. package/dist/planning/index.js +5 -0
  128. package/dist/planning/index.js.map +1 -0
  129. package/dist/planning/plan-schema.d.ts +74 -0
  130. package/dist/planning/plan-schema.js +39 -0
  131. package/dist/planning/plan-schema.js.map +1 -0
  132. package/dist/planning/workflow-planner.d.ts +39 -0
  133. package/dist/planning/workflow-planner.js +211 -0
  134. package/dist/planning/workflow-planner.js.map +1 -0
  135. package/dist/reports/health-scorer.d.ts +14 -0
  136. package/dist/reports/health-scorer.js +88 -0
  137. package/dist/reports/health-scorer.js.map +1 -0
  138. package/dist/reports/html-generator.d.ts +10 -0
  139. package/dist/reports/html-generator.js +155 -0
  140. package/dist/reports/html-generator.js.map +1 -0
  141. package/dist/reports/index.d.ts +4 -0
  142. package/dist/reports/index.js +6 -0
  143. package/dist/reports/index.js.map +1 -0
  144. package/dist/reports/markdown-generator.d.ts +10 -0
  145. package/dist/reports/markdown-generator.js +334 -0
  146. package/dist/reports/markdown-generator.js.map +1 -0
  147. package/dist/reports/priority-ranker.d.ts +22 -0
  148. package/dist/reports/priority-ranker.js +608 -0
  149. package/dist/reports/priority-ranker.js.map +1 -0
  150. package/dist/screenshots/dual-format.d.ts +14 -0
  151. package/dist/screenshots/dual-format.js +59 -0
  152. package/dist/screenshots/dual-format.js.map +1 -0
  153. package/dist/screenshots/index.d.ts +2 -0
  154. package/dist/screenshots/index.js +4 -0
  155. package/dist/screenshots/index.js.map +1 -0
  156. package/dist/screenshots/screenshot-manager.d.ts +33 -0
  157. package/dist/screenshots/screenshot-manager.js +86 -0
  158. package/dist/screenshots/screenshot-manager.js.map +1 -0
  159. package/dist/testing/accessibility-auditor.d.ts +23 -0
  160. package/dist/testing/accessibility-auditor.js +44 -0
  161. package/dist/testing/accessibility-auditor.js.map +1 -0
  162. package/dist/testing/index.d.ts +4 -0
  163. package/dist/testing/index.js +5 -0
  164. package/dist/testing/index.js.map +1 -0
  165. package/dist/testing/meta-auditor.d.ts +16 -0
  166. package/dist/testing/meta-auditor.js +268 -0
  167. package/dist/testing/meta-auditor.js.map +1 -0
  168. package/dist/testing/performance-monitor.d.ts +15 -0
  169. package/dist/testing/performance-monitor.js +64 -0
  170. package/dist/testing/performance-monitor.js.map +1 -0
  171. package/dist/types/artifacts.d.ts +58 -0
  172. package/dist/types/artifacts.js +3 -0
  173. package/dist/types/artifacts.js.map +1 -0
  174. package/dist/types/discovery.d.ts +124 -0
  175. package/dist/types/discovery.js +3 -0
  176. package/dist/types/discovery.js.map +1 -0
  177. package/dist/types/execution.d.ts +154 -0
  178. package/dist/types/execution.js +3 -0
  179. package/dist/types/execution.js.map +1 -0
  180. package/dist/types/index.d.ts +2 -0
  181. package/dist/types/index.js +4 -0
  182. package/dist/types/index.js.map +1 -0
  183. package/dist/utils/sanitizer.d.ts +25 -0
  184. package/dist/utils/sanitizer.js +98 -0
  185. package/dist/utils/sanitizer.js.map +1 -0
  186. package/package.json +86 -0
  187. package/templates/report.hbs +202 -0
  188. package/templates/styles/report.css +607 -0
@@ -0,0 +1,422 @@
1
+ // Interactive element discovery including hidden elements (modals, dropdowns, mobile nav)
2
+ // Destructive action denylist - skip elements with these keywords
3
+ const DESTRUCTIVE_KEYWORDS = [
4
+ 'delete',
5
+ 'remove',
6
+ 'destroy',
7
+ 'reset',
8
+ 'clear',
9
+ 'drop',
10
+ 'purge',
11
+ 'revoke',
12
+ 'terminate',
13
+ 'unsubscribe',
14
+ 'cancel-account',
15
+ 'close-account',
16
+ 'logout',
17
+ 'log out',
18
+ 'sign out',
19
+ ];
20
+ /**
21
+ * Check if text matches destructive action patterns
22
+ */
23
+ function isDestructiveAction(text) {
24
+ const lowerText = text.toLowerCase().trim();
25
+ return DESTRUCTIVE_KEYWORDS.some(keyword => lowerText.includes(keyword));
26
+ }
27
+ function escapeSelectorText(value) {
28
+ return value.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
29
+ }
30
+ /**
31
+ * Discover all visible interactive elements on a page
32
+ */
33
+ export async function discoverElements(page, pageUrl) {
34
+ const elements = {
35
+ forms: [],
36
+ buttons: [],
37
+ links: [],
38
+ menus: [],
39
+ otherInteractive: [],
40
+ };
41
+ const pageHostname = new URL(pageUrl).hostname;
42
+ // Discover all forms with field inventory
43
+ const forms = await page.locator('form').all();
44
+ for (let i = 0; i < forms.length; i++) {
45
+ const form = forms[i];
46
+ try {
47
+ const action = (await form.getAttribute('action')) || '';
48
+ const method = ((await form.getAttribute('method')) || 'GET').toUpperCase();
49
+ // Build selector for the form
50
+ const formId = await form.getAttribute('id');
51
+ const formName = await form.getAttribute('name');
52
+ let selector = 'form';
53
+ if (formId) {
54
+ selector = `form#${formId}`;
55
+ }
56
+ else if (formName) {
57
+ selector = `form[name="${formName}"]`;
58
+ }
59
+ else {
60
+ selector = `form:nth-of-type(${i + 1})`;
61
+ }
62
+ // Resolve action URL
63
+ const resolvedAction = action ? new URL(action, pageUrl).href : pageUrl;
64
+ // Find all input, textarea, select within the form
65
+ const fields = await form.locator('input, textarea, select').all();
66
+ const formFields = [];
67
+ for (const field of fields) {
68
+ try {
69
+ const type = (await field.getAttribute('type')) || 'text';
70
+ const name = (await field.getAttribute('name')) || '';
71
+ const required = (await field.getAttribute('required')) !== null;
72
+ const placeholder = (await field.getAttribute('placeholder')) || '';
73
+ // Try to find associated label
74
+ let label = (await field.getAttribute('aria-label')) || '';
75
+ if (!label) {
76
+ const fieldId = await field.getAttribute('id');
77
+ if (fieldId) {
78
+ // Look for label with matching "for" attribute
79
+ const labelElement = await page.locator(`label[for="${fieldId}"]`).first();
80
+ const labelText = await labelElement.textContent().catch(() => '');
81
+ label = labelText?.trim() || '';
82
+ }
83
+ }
84
+ // If still no label, check if field is inside a label
85
+ if (!label) {
86
+ const parentLabel = await field.locator('xpath=ancestor::label[1]').first();
87
+ const labelText = await parentLabel.textContent().catch(() => '');
88
+ label = labelText?.trim() || '';
89
+ }
90
+ formFields.push({
91
+ type,
92
+ name,
93
+ label,
94
+ required,
95
+ placeholder,
96
+ });
97
+ }
98
+ catch {
99
+ // Failed to extract field info, skip
100
+ }
101
+ }
102
+ elements.forms.push({
103
+ action: resolvedAction,
104
+ method,
105
+ selector,
106
+ fields: formFields,
107
+ });
108
+ }
109
+ catch {
110
+ // Failed to extract form info, skip
111
+ }
112
+ }
113
+ // Discover all buttons
114
+ const buttons = await page.getByRole('button').all();
115
+ for (let i = 0; i < buttons.length; i++) {
116
+ const button = buttons[i];
117
+ try {
118
+ const text = (await button.textContent()) || '';
119
+ const ariaLabel = (await button.getAttribute('aria-label')) || '';
120
+ const type = (await button.getAttribute('type')) || 'button';
121
+ const disabled = (await button.getAttribute('disabled')) !== null;
122
+ const isVisible = await button.isVisible();
123
+ // Build selector - prefer has-text when text is unique
124
+ let selector = 'button';
125
+ if (text.trim()) {
126
+ selector = `button:has-text("${escapeSelectorText(text.trim())}")`;
127
+ }
128
+ else if (ariaLabel) {
129
+ selector = `button[aria-label="${escapeSelectorText(ariaLabel)}"]`;
130
+ }
131
+ else {
132
+ // Fallback to nth-of-type
133
+ selector = `button:nth-of-type(${i + 1})`;
134
+ }
135
+ elements.buttons.push({
136
+ type: 'button',
137
+ selector,
138
+ text: text.trim() || ariaLabel,
139
+ visible: isVisible,
140
+ attributes: {
141
+ type,
142
+ disabled: disabled.toString(),
143
+ },
144
+ });
145
+ }
146
+ catch {
147
+ // Failed to extract button info, skip
148
+ }
149
+ }
150
+ // Discover all links with href
151
+ const links = await page.locator('a[href]').all();
152
+ const seenHrefs = new Set();
153
+ for (const link of links) {
154
+ try {
155
+ const href = await link.getAttribute('href');
156
+ if (!href)
157
+ continue;
158
+ // Resolve to absolute URL
159
+ const absoluteUrl = new URL(href, pageUrl).href;
160
+ // Deduplicate by href
161
+ if (seenHrefs.has(absoluteUrl))
162
+ continue;
163
+ seenHrefs.add(absoluteUrl);
164
+ const text = (await link.textContent())?.trim() || '';
165
+ // Determine if internal
166
+ const linkHostname = new URL(absoluteUrl).hostname;
167
+ const isInternal = linkHostname === pageHostname;
168
+ elements.links.push({
169
+ href: absoluteUrl,
170
+ text,
171
+ isInternal,
172
+ });
173
+ }
174
+ catch {
175
+ // Failed to extract link info or invalid URL, skip
176
+ }
177
+ }
178
+ // Discover navigation menus
179
+ const navContainers = await page
180
+ .locator('nav, [role="navigation"], [role="menubar"]')
181
+ .all();
182
+ for (let i = 0; i < navContainers.length; i++) {
183
+ const nav = navContainers[i];
184
+ try {
185
+ const text = (await nav.textContent())?.trim() || '';
186
+ const ariaLabel = (await nav.getAttribute('aria-label')) || '';
187
+ const isVisible = await nav.isVisible();
188
+ elements.menus.push({
189
+ type: 'menu',
190
+ selector: `nav:nth-of-type(${i + 1})`,
191
+ text: ariaLabel || text.substring(0, 50) || `Menu ${i + 1}`,
192
+ visible: isVisible,
193
+ attributes: {},
194
+ });
195
+ }
196
+ catch {
197
+ // Failed to extract menu info, skip
198
+ }
199
+ }
200
+ // Discover other interactive elements
201
+ const interactiveRoles = ['tab', 'tabpanel', 'dialog', 'combobox', 'listbox'];
202
+ for (const role of interactiveRoles) {
203
+ const roleElements = await page.locator(`[role="${role}"]`).all();
204
+ for (let i = 0; i < roleElements.length; i++) {
205
+ const element = roleElements[i];
206
+ try {
207
+ const text = (await element.textContent())?.trim() || '';
208
+ const ariaLabel = (await element.getAttribute('aria-label')) || '';
209
+ const isVisible = await element.isVisible();
210
+ elements.otherInteractive.push({
211
+ type: role, // TypeScript will coerce to InteractiveElement['type']
212
+ selector: `[role="${role}"]:nth-of-type(${i + 1})`,
213
+ text: ariaLabel || text.substring(0, 50) || `${role} ${i + 1}`,
214
+ visible: isVisible,
215
+ attributes: { role },
216
+ });
217
+ }
218
+ catch {
219
+ // Failed to extract element info, skip
220
+ }
221
+ }
222
+ }
223
+ return elements;
224
+ }
225
+ /**
226
+ * Discover hidden elements by triggering interactions (buttons, menus, modals)
227
+ * Returns only NEWLY discovered elements (not present in initial page load)
228
+ */
229
+ export async function discoverHiddenElements(page, pageUrl) {
230
+ // Take "before" snapshot of visible element count
231
+ const beforeCounts = {
232
+ buttons: (await page.getByRole('button').all()).length,
233
+ links: (await page.locator('a[href]').all()).length,
234
+ forms: (await page.locator('form').all()).length,
235
+ };
236
+ const newElements = {
237
+ forms: [],
238
+ buttons: [],
239
+ links: [],
240
+ menus: [],
241
+ otherInteractive: [],
242
+ };
243
+ // Find buttons that might trigger hidden content
244
+ const triggerSelectors = [
245
+ 'button[aria-expanded]',
246
+ 'button[aria-haspopup]',
247
+ 'button[data-toggle]',
248
+ 'button[data-bs-toggle]',
249
+ '[class*="hamburger"]',
250
+ '[class*="menu-toggle"]',
251
+ '[class*="navbar-toggler"]',
252
+ ];
253
+ // Also find buttons with trigger-like text
254
+ const triggerTexts = ['menu', 'nav', 'more', 'show', 'open', 'expand'];
255
+ const allButtons = await page.getByRole('button').all();
256
+ const triggerButtons = [];
257
+ // Collect explicit trigger buttons
258
+ for (const selector of triggerSelectors) {
259
+ const buttons = await page.locator(selector).all();
260
+ triggerButtons.push(...buttons);
261
+ }
262
+ // Collect buttons with trigger-like text
263
+ for (const button of allButtons) {
264
+ try {
265
+ const text = ((await button.textContent()) || '').toLowerCase();
266
+ if (triggerTexts.some((word) => text.includes(word))) {
267
+ triggerButtons.push(button);
268
+ }
269
+ }
270
+ catch {
271
+ // Skip
272
+ }
273
+ }
274
+ // Click each trigger button and check for new elements (cap at 10 to avoid perf explosion)
275
+ for (const trigger of triggerButtons.slice(0, 10)) {
276
+ try {
277
+ // Check if button is visible and enabled
278
+ const isVisible = await trigger.isVisible();
279
+ if (!isVisible)
280
+ continue;
281
+ // Check for destructive actions
282
+ const buttonText = (await trigger.textContent()) || '';
283
+ const ariaLabel = (await trigger.getAttribute('aria-label')) || '';
284
+ const combinedText = `${buttonText} ${ariaLabel}`;
285
+ if (isDestructiveAction(combinedText)) {
286
+ continue; // Skip destructive buttons
287
+ }
288
+ // Click the trigger
289
+ await trigger.click({ timeout: 2000 });
290
+ // Wait for animations
291
+ await page.waitForTimeout(500);
292
+ // Check for newly visible elements
293
+ const modalSelectors = [
294
+ '[role="dialog"]',
295
+ '[role="menu"]',
296
+ '.modal',
297
+ '.dropdown-menu',
298
+ '.nav-menu',
299
+ '[aria-hidden="false"]',
300
+ ];
301
+ let foundNewContent = false;
302
+ for (const selector of modalSelectors) {
303
+ const modalElements = await page.locator(selector).all();
304
+ for (const modal of modalElements) {
305
+ try {
306
+ const isVisible = await modal.isVisible();
307
+ if (isVisible) {
308
+ foundNewContent = true;
309
+ // Run discoverElements on the new content
310
+ const modalElements = await discoverElements(page, pageUrl);
311
+ // Merge with newElements (deduplicate by selector)
312
+ for (const form of modalElements.forms) {
313
+ if (!newElements.forms.some((f) => f.selector === form.selector)) {
314
+ newElements.forms.push(form);
315
+ }
316
+ }
317
+ for (const button of modalElements.buttons) {
318
+ if (!newElements.buttons.some((b) => b.selector === button.selector)) {
319
+ newElements.buttons.push(button);
320
+ }
321
+ }
322
+ for (const link of modalElements.links) {
323
+ if (!newElements.links.some((l) => l.href === link.href)) {
324
+ newElements.links.push(link);
325
+ }
326
+ }
327
+ for (const menu of modalElements.menus) {
328
+ if (!newElements.menus.some((m) => m.selector === menu.selector)) {
329
+ newElements.menus.push(menu);
330
+ }
331
+ }
332
+ for (const other of modalElements.otherInteractive) {
333
+ if (!newElements.otherInteractive.some((o) => o.selector === other.selector)) {
334
+ newElements.otherInteractive.push(other);
335
+ }
336
+ }
337
+ }
338
+ }
339
+ catch {
340
+ // Skip
341
+ }
342
+ }
343
+ }
344
+ // Try to close/reset
345
+ if (foundNewContent) {
346
+ try {
347
+ // Try clicking trigger again to toggle off
348
+ await trigger.click({ timeout: 1000 });
349
+ await page.waitForTimeout(300);
350
+ }
351
+ catch {
352
+ // Try pressing Escape
353
+ try {
354
+ await page.keyboard.press('Escape');
355
+ await page.waitForTimeout(300);
356
+ }
357
+ catch {
358
+ // If can't close, reload the page to reset state
359
+ try {
360
+ await page.reload({ timeout: 5000, waitUntil: 'domcontentloaded' });
361
+ }
362
+ catch {
363
+ // Can't reload, break loop
364
+ break;
365
+ }
366
+ }
367
+ }
368
+ }
369
+ }
370
+ catch {
371
+ // Failed to interact with trigger, continue
372
+ continue;
373
+ }
374
+ }
375
+ // Also try hovering on nav items to discover dropdowns
376
+ const navItems = await page.locator('nav li, [role="menubar"] > *').all();
377
+ for (const item of navItems) {
378
+ try {
379
+ const isVisible = await item.isVisible();
380
+ if (!isVisible)
381
+ continue;
382
+ // Hover on the item
383
+ await item.hover({ timeout: 1000 });
384
+ await page.waitForTimeout(300);
385
+ // Check for dropdown menus
386
+ const dropdowns = await page.locator('.dropdown-menu, [role="menu"]').all();
387
+ for (const dropdown of dropdowns) {
388
+ try {
389
+ const isDropdownVisible = await dropdown.isVisible();
390
+ if (isDropdownVisible) {
391
+ // Discover elements in dropdown
392
+ const dropdownElements = await discoverElements(page, pageUrl);
393
+ // Merge new links
394
+ for (const link of dropdownElements.links) {
395
+ if (!newElements.links.some((l) => l.href === link.href)) {
396
+ newElements.links.push(link);
397
+ }
398
+ }
399
+ // Merge new buttons
400
+ for (const button of dropdownElements.buttons) {
401
+ if (!newElements.buttons.some((b) => b.selector === button.selector)) {
402
+ newElements.buttons.push(button);
403
+ }
404
+ }
405
+ }
406
+ }
407
+ catch {
408
+ // Skip
409
+ }
410
+ }
411
+ }
412
+ catch {
413
+ // Failed to hover, continue
414
+ continue;
415
+ }
416
+ }
417
+ // Filter to return only NEWLY discovered elements (subtract "before" snapshot)
418
+ // Since we already deduplicated during collection, and we're discovering from hidden content,
419
+ // most of these should be new. But let's do a final pass to be safe.
420
+ return newElements;
421
+ }
422
+ //# sourceMappingURL=element-mapper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"element-mapper.js","sourceRoot":"","sources":["../../src/discovery/element-mapper.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ export { SiteCrawler } from './crawler.js';
2
+ export type { CrawlerOptions } from './crawler.js';
3
+ export { detectSPAFramework, interceptRouteChanges } from './spa-detector.js';
4
+ export { discoverElements, discoverHiddenElements } from './element-mapper.js';
5
+ export { validateLinks, createLinkValidationState } from './link-validator.js';
6
+ export { buildSitemap, printSitemapTree } from './sitemap-builder.js';
7
+ export { runDiscovery } from './discovery-pipeline.js';
8
+ export type { DiscoveryOptions } from './discovery-pipeline.js';
@@ -0,0 +1,8 @@
1
+ // Barrel export for discovery module
2
+ export { SiteCrawler } from './crawler.js';
3
+ export { detectSPAFramework, interceptRouteChanges } from './spa-detector.js';
4
+ export { discoverElements, discoverHiddenElements } from './element-mapper.js';
5
+ export { validateLinks, createLinkValidationState } from './link-validator.js';
6
+ export { buildSitemap, printSitemapTree } from './sitemap-builder.js';
7
+ export { runDiscovery } from './discovery-pipeline.js';
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/discovery/index.ts"],"names":[],"mappings":"AAAA,qCAAqC;AACrC,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAE3C,OAAO,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAC9E,OAAO,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAC/E,OAAO,EAAE,aAAa,EAAE,yBAAyB,EAAE,MAAM,qBAAqB,CAAC;AAC/E,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACtE,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC"}
@@ -0,0 +1,15 @@
1
+ import type { Page } from 'playwright';
2
+ import type { BrokenLink, LinkInfo } from '../types/discovery.js';
3
+ export interface LinkValidationState {
4
+ checkedCount: number;
5
+ }
6
+ export declare function createLinkValidationState(): LinkValidationState;
7
+ /**
8
+ * Validates internal links by checking their HTTP status codes.
9
+ * External links are skipped to avoid rate-limiting third-party sites.
10
+ *
11
+ * @param links - All links discovered on a page
12
+ * @param page - Playwright Page instance (maintains session context for auth)
13
+ * @returns Array of broken links (4xx, 5xx, network errors)
14
+ */
15
+ export declare function validateLinks(links: LinkInfo[], page: Page, state?: LinkValidationState): Promise<BrokenLink[]>;
@@ -0,0 +1,137 @@
1
+ import { ensurePublicHostname } from '../core/validation.js';
2
+ // Security caps for link validation
3
+ const MAX_LINKS_PER_PAGE = 50;
4
+ const MAX_TOTAL_LINKS = 500;
5
+ export function createLinkValidationState() {
6
+ return { checkedCount: 0 };
7
+ }
8
+ /**
9
+ * Validates internal links by checking their HTTP status codes.
10
+ * External links are skipped to avoid rate-limiting third-party sites.
11
+ *
12
+ * @param links - All links discovered on a page
13
+ * @param page - Playwright Page instance (maintains session context for auth)
14
+ * @returns Array of broken links (4xx, 5xx, network errors)
15
+ */
16
+ export async function validateLinks(links, page, state = createLinkValidationState()) {
17
+ const brokenLinks = [];
18
+ const sourceUrl = page.url();
19
+ const hostnameSafetyCache = new Map();
20
+ const assertHostnameIsPublic = async (hostname) => {
21
+ const normalized = hostname.toLowerCase();
22
+ if (hostnameSafetyCache.has(normalized)) {
23
+ const cachedError = hostnameSafetyCache.get(normalized);
24
+ if (cachedError) {
25
+ throw new Error(cachedError);
26
+ }
27
+ return;
28
+ }
29
+ try {
30
+ await ensurePublicHostname(normalized);
31
+ hostnameSafetyCache.set(normalized, null);
32
+ }
33
+ catch (error) {
34
+ const message = error instanceof Error ? error.message : String(error);
35
+ hostnameSafetyCache.set(normalized, message);
36
+ throw error;
37
+ }
38
+ };
39
+ // Filter to internal links only
40
+ const internalLinks = links.filter(link => link.isInternal);
41
+ // Deduplicate by href (normalize query strings)
42
+ const uniqueUrls = new Map();
43
+ for (const link of internalLinks) {
44
+ try {
45
+ const url = new URL(link.href);
46
+ // Normalize: remove trailing slash, lowercase hostname
47
+ const normalizedHref = `${url.origin}${url.pathname.replace(/\/$/, '')}${url.search}`;
48
+ if (!uniqueUrls.has(normalizedHref)) {
49
+ uniqueUrls.set(normalizedHref, { ...link, href: normalizedHref });
50
+ }
51
+ }
52
+ catch {
53
+ // Invalid URL, skip
54
+ continue;
55
+ }
56
+ }
57
+ // Filter out non-HTTP links
58
+ const httpLinks = Array.from(uniqueUrls.values()).filter(link => {
59
+ const href = link.href.toLowerCase();
60
+ return !href.startsWith('mailto:') &&
61
+ !href.startsWith('tel:') &&
62
+ !href.startsWith('javascript:') &&
63
+ !href.startsWith('data:') &&
64
+ !href.startsWith('#');
65
+ });
66
+ // Apply per-page cap
67
+ const cappedLinks = httpLinks.slice(0, MAX_LINKS_PER_PAGE);
68
+ if (httpLinks.length > MAX_LINKS_PER_PAGE) {
69
+ console.warn(`Capping link validation to ${MAX_LINKS_PER_PAGE} links per page (${httpLinks.length} found)`);
70
+ }
71
+ // Apply global cap
72
+ const remainingGlobalCapacity = MAX_TOTAL_LINKS - state.checkedCount;
73
+ const linksToCheck = cappedLinks.slice(0, remainingGlobalCapacity);
74
+ if (linksToCheck.length < cappedLinks.length) {
75
+ console.warn(`Global link cap reached (${MAX_TOTAL_LINKS} total). Skipping ${cappedLinks.length - linksToCheck.length} links.`);
76
+ }
77
+ state.checkedCount += linksToCheck.length;
78
+ // Check links with concurrency limit (max 10 concurrent)
79
+ const batchSize = 10;
80
+ for (let i = 0; i < linksToCheck.length; i += batchSize) {
81
+ const batch = linksToCheck.slice(i, i + batchSize);
82
+ const results = await Promise.allSettled(batch.map(async (link) => {
83
+ try {
84
+ const initialParsedUrl = new URL(link.href);
85
+ await assertHostnameIsPublic(initialParsedUrl.hostname);
86
+ // Use page.request to maintain browser session context (cookies, auth)
87
+ const response = await page.request.get(link.href, {
88
+ timeout: 5000,
89
+ // Follow redirects by default - only final status matters
90
+ });
91
+ // SSRF protection: check final redirect destination
92
+ const finalUrl = response.url();
93
+ if (finalUrl !== link.href) {
94
+ try {
95
+ const finalParsed = new URL(finalUrl);
96
+ await assertHostnameIsPublic(finalParsed.hostname);
97
+ }
98
+ catch (error) {
99
+ return {
100
+ url: link.href,
101
+ sourceUrl,
102
+ statusCode: 0,
103
+ statusText: error instanceof Error ? error.message : 'SSRF protection: Redirect destination failed validation',
104
+ };
105
+ }
106
+ }
107
+ // Check if response is broken (4xx, 5xx)
108
+ if (response.status() >= 400) {
109
+ return {
110
+ url: link.href,
111
+ sourceUrl,
112
+ statusCode: response.status(),
113
+ statusText: response.statusText(),
114
+ };
115
+ }
116
+ return null; // Link is OK
117
+ }
118
+ catch (error) {
119
+ // Network error or timeout
120
+ return {
121
+ url: link.href,
122
+ sourceUrl,
123
+ statusCode: 0,
124
+ statusText: error instanceof Error ? error.message : 'Network error',
125
+ };
126
+ }
127
+ }));
128
+ // Collect broken links from this batch
129
+ for (const result of results) {
130
+ if (result.status === 'fulfilled' && result.value !== null) {
131
+ brokenLinks.push(result.value);
132
+ }
133
+ }
134
+ }
135
+ return brokenLinks;
136
+ }
137
+ //# sourceMappingURL=link-validator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"link-validator.js","sourceRoot":"","sources":["../../src/discovery/link-validator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import type { SitemapNode, PageData } from '../types/discovery.js';
2
+ /**
3
+ * Builds a hierarchical sitemap tree from flat page data based on URL path structure.
4
+ * Creates intermediate placeholder nodes for uncrawled parent paths.
5
+ *
6
+ * @param pages - Flat array of crawled pages
7
+ * @param rootUrl - Root URL of the site (e.g., https://example.com)
8
+ * @returns Root node of the sitemap tree
9
+ */
10
+ export declare function buildSitemap(pages: PageData[], rootUrl: string): SitemapNode;
11
+ /**
12
+ * Renders a sitemap tree as human-readable indented text.
13
+ * Format: Title (path) with 2 spaces per depth level.
14
+ *
15
+ * @param node - Root or any node in the tree
16
+ * @param indent - Current indentation level (internal use)
17
+ * @returns Multi-line string representation of the tree
18
+ */
19
+ export declare function printSitemapTree(node: SitemapNode, indent?: number): string;