@taodigital/crawler 0.0.0-stage → 1.2.1

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 (494) hide show
  1. package/Dockerfile +82 -0
  2. package/README.md +10 -2
  3. package/dist/accessibility-auditor.d.ts +29 -0
  4. package/dist/accessibility-auditor.js +57 -0
  5. package/dist/accessibility-auditor.js.map +7 -0
  6. package/dist/accessibility-tree-crawler.d.ts +44 -0
  7. package/dist/accessibility-tree-crawler.js +156 -0
  8. package/dist/accessibility-tree-crawler.js.map +7 -0
  9. package/dist/advanced-ui-probes.d.ts +47 -0
  10. package/dist/advanced-ui-probes.js +310 -0
  11. package/dist/advanced-ui-probes.js.map +7 -0
  12. package/dist/ai-login.d.ts +32 -0
  13. package/dist/ai-login.js +250 -0
  14. package/dist/ai-login.js.map +7 -0
  15. package/dist/auth-token-rotator.d.ts +47 -0
  16. package/dist/auth-token-rotator.js +58 -0
  17. package/dist/auth-token-rotator.js.map +7 -0
  18. package/dist/auth.d.ts +20 -0
  19. package/dist/auth.js +348 -0
  20. package/dist/auth.js.map +7 -0
  21. package/dist/browser-extension.d.ts +37 -0
  22. package/dist/browser-extension.js +113 -0
  23. package/dist/browser-extension.js.map +7 -0
  24. package/dist/captcha-solver.d.ts +16 -0
  25. package/dist/captcha-solver.js +105 -0
  26. package/dist/captcha-solver.js.map +7 -0
  27. package/dist/color-contrast.d.ts +30 -0
  28. package/dist/color-contrast.js +78 -0
  29. package/dist/color-contrast.js.map +7 -0
  30. package/dist/content-chunker.d.ts +18 -0
  31. package/dist/content-chunker.js +152 -0
  32. package/dist/content-chunker.js.map +7 -0
  33. package/dist/content-dedup.d.ts +23 -0
  34. package/dist/content-dedup.js +60 -0
  35. package/dist/content-dedup.js.map +7 -0
  36. package/dist/cookie-consent.d.ts +25 -0
  37. package/dist/cookie-consent.js +78 -0
  38. package/dist/cookie-consent.js.map +7 -0
  39. package/dist/cookie-jar.d.ts +20 -0
  40. package/dist/cookie-jar.js +61 -0
  41. package/dist/cookie-jar.js.map +7 -0
  42. package/dist/crawl-session-manager.d.ts +44 -0
  43. package/dist/crawl-session-manager.js +145 -0
  44. package/dist/crawl-session-manager.js.map +7 -0
  45. package/dist/crawler.d.ts +233 -0
  46. package/dist/crawler.js +6239 -0
  47. package/dist/crawler.js.map +7 -0
  48. package/dist/crud-interaction.d.ts +31 -0
  49. package/dist/crud-interaction.js +264 -0
  50. package/dist/crud-interaction.js.map +7 -0
  51. package/dist/custom-js-injector.d.ts +42 -0
  52. package/dist/custom-js-injector.js +50 -0
  53. package/dist/custom-js-injector.js.map +7 -0
  54. package/dist/dark-mode-capture.d.ts +23 -0
  55. package/dist/dark-mode-capture.js +75 -0
  56. package/dist/dark-mode-capture.js.map +7 -0
  57. package/dist/dead-link-detector.d.ts +37 -0
  58. package/dist/dead-link-detector.js +50 -0
  59. package/dist/dead-link-detector.js.map +7 -0
  60. package/dist/dropdown-enumerator.d.ts +20 -0
  61. package/dist/dropdown-enumerator.js +230 -0
  62. package/dist/dropdown-enumerator.js.map +7 -0
  63. package/dist/element-hash.d.ts +9 -0
  64. package/dist/element-hash.js +9 -0
  65. package/dist/element-hash.js.map +7 -0
  66. package/dist/email-otp.d.ts +42 -0
  67. package/dist/email-otp.js +177 -0
  68. package/dist/email-otp.js.map +7 -0
  69. package/dist/engine-router.d.ts +47 -0
  70. package/dist/engine-router.js +94 -0
  71. package/dist/engine-router.js.map +7 -0
  72. package/dist/export/bulk-export.d.ts +26 -0
  73. package/dist/export/bulk-export.js +76 -0
  74. package/dist/export/bulk-export.js.map +7 -0
  75. package/dist/export/vector-export.d.ts +76 -0
  76. package/dist/export/vector-export.js +85 -0
  77. package/dist/export/vector-export.js.map +7 -0
  78. package/dist/extractors/advanced.d.ts +64 -0
  79. package/dist/extractors/advanced.js +265 -0
  80. package/dist/extractors/advanced.js.map +7 -0
  81. package/dist/extractors/artifact-envelope.d.ts +63 -0
  82. package/dist/extractors/artifact-envelope.js +63 -0
  83. package/dist/extractors/artifact-envelope.js.map +7 -0
  84. package/dist/extractors/branding-extractor.d.ts +46 -0
  85. package/dist/extractors/branding-extractor.js +104 -0
  86. package/dist/extractors/branding-extractor.js.map +7 -0
  87. package/dist/extractors/summary-extractor.d.ts +10 -0
  88. package/dist/extractors/summary-extractor.js +36 -0
  89. package/dist/extractors/summary-extractor.js.map +7 -0
  90. package/dist/failure-analysis-agent.d.ts +16 -0
  91. package/dist/failure-analysis-agent.js +42 -0
  92. package/dist/failure-analysis-agent.js.map +7 -0
  93. package/dist/feature-flag-detector.d.ts +30 -0
  94. package/dist/feature-flag-detector.js +55 -0
  95. package/dist/feature-flag-detector.js.map +7 -0
  96. package/dist/filter-pagination-crawler.d.ts +52 -0
  97. package/dist/filter-pagination-crawler.js +299 -0
  98. package/dist/filter-pagination-crawler.js.map +7 -0
  99. package/dist/form-detector.d.ts +34 -0
  100. package/dist/form-detector.js +58 -0
  101. package/dist/form-detector.js.map +7 -0
  102. package/dist/frontier.d.ts +40 -0
  103. package/dist/frontier.js +103 -0
  104. package/dist/frontier.js.map +7 -0
  105. package/dist/gap-pass.d.ts +36 -0
  106. package/dist/gap-pass.js +115 -0
  107. package/dist/gap-pass.js.map +7 -0
  108. package/dist/graphql-detector.d.ts +39 -0
  109. package/dist/graphql-detector.js +86 -0
  110. package/dist/graphql-detector.js.map +7 -0
  111. package/dist/http-engine.d.ts +80 -0
  112. package/dist/http-engine.js +318 -0
  113. package/dist/http-engine.js.map +7 -0
  114. package/dist/incremental-diff.d.ts +116 -0
  115. package/dist/incremental-diff.js +137 -0
  116. package/dist/incremental-diff.js.map +7 -0
  117. package/dist/index.d.ts +52 -0
  118. package/dist/index.js +83 -0
  119. package/dist/index.js.map +7 -0
  120. package/dist/input-fuzzer.d.ts +30 -0
  121. package/dist/input-fuzzer.js +105 -0
  122. package/dist/input-fuzzer.js.map +7 -0
  123. package/dist/interactive-probe.d.ts +144 -0
  124. package/dist/interactive-probe.js +865 -0
  125. package/dist/interactive-probe.js.map +7 -0
  126. package/dist/job-queue.d.ts +297 -0
  127. package/dist/job-queue.js +3812 -0
  128. package/dist/job-queue.js.map +7 -0
  129. package/dist/js-coverage.d.ts +29 -0
  130. package/dist/js-coverage.js +56 -0
  131. package/dist/js-coverage.js.map +7 -0
  132. package/dist/js-router-detector.d.ts +17 -0
  133. package/dist/js-router-detector.js +90 -0
  134. package/dist/js-router-detector.js.map +7 -0
  135. package/dist/language-detector.d.ts +22 -0
  136. package/dist/language-detector.js +57 -0
  137. package/dist/language-detector.js.map +7 -0
  138. package/dist/link-classifier.d.ts +23 -0
  139. package/dist/link-classifier.js +40 -0
  140. package/dist/link-classifier.js.map +7 -0
  141. package/dist/live-view.d.ts +36 -0
  142. package/dist/live-view.js +105 -0
  143. package/dist/live-view.js.map +7 -0
  144. package/dist/locator-extractor.d.ts +46 -0
  145. package/dist/locator-extractor.js +220 -0
  146. package/dist/locator-extractor.js.map +7 -0
  147. package/dist/logger.d.ts +2 -0
  148. package/dist/logger.js +10 -0
  149. package/dist/logger.js.map +7 -0
  150. package/dist/markdown-extract.d.ts +8 -0
  151. package/dist/markdown-extract.js +54 -0
  152. package/dist/markdown-extract.js.map +7 -0
  153. package/dist/multi-context-handler.d.ts +55 -0
  154. package/dist/multi-context-handler.js +182 -0
  155. package/dist/multi-context-handler.js.map +7 -0
  156. package/dist/native-crawler.d.ts +69 -0
  157. package/dist/native-crawler.js +839 -0
  158. package/dist/native-crawler.js.map +7 -0
  159. package/dist/nested-window-crawler.d.ts +41 -0
  160. package/dist/nested-window-crawler.js +172 -0
  161. package/dist/nested-window-crawler.js.map +7 -0
  162. package/dist/network-action-correlator.d.ts +47 -0
  163. package/dist/network-action-correlator.js +209 -0
  164. package/dist/network-action-correlator.js.map +7 -0
  165. package/dist/network-observer.d.ts +49 -0
  166. package/dist/network-observer.js +149 -0
  167. package/dist/network-observer.js.map +7 -0
  168. package/dist/network-throttle.d.ts +25 -0
  169. package/dist/network-throttle.js +34 -0
  170. package/dist/network-throttle.js.map +7 -0
  171. package/dist/openapi-generator.d.ts +49 -0
  172. package/dist/openapi-generator.js +89 -0
  173. package/dist/openapi-generator.js.map +7 -0
  174. package/dist/pdf-extractor.d.ts +28 -0
  175. package/dist/pdf-extractor.js +81 -0
  176. package/dist/pdf-extractor.js.map +7 -0
  177. package/dist/performance-metrics-collector.d.ts +50 -0
  178. package/dist/performance-metrics-collector.js +155 -0
  179. package/dist/performance-metrics-collector.js.map +7 -0
  180. package/dist/performance-metrics.d.ts +52 -0
  181. package/dist/performance-metrics.js +83 -0
  182. package/dist/performance-metrics.js.map +7 -0
  183. package/dist/phash.d.ts +50 -0
  184. package/dist/phash.js +136 -0
  185. package/dist/phash.js.map +7 -0
  186. package/dist/pii-scrubber.d.ts +21 -0
  187. package/dist/pii-scrubber.js +75 -0
  188. package/dist/pii-scrubber.js.map +7 -0
  189. package/dist/playwright-recorder.d.ts +39 -0
  190. package/dist/playwright-recorder.js +123 -0
  191. package/dist/playwright-recorder.js.map +7 -0
  192. package/dist/policies/crawl-safety.d.ts +9 -0
  193. package/dist/policies/crawl-safety.js +41 -0
  194. package/dist/policies/crawl-safety.js.map +7 -0
  195. package/dist/profiles/domain-profile.d.ts +19 -0
  196. package/dist/profiles/domain-profile.js +160 -0
  197. package/dist/profiles/domain-profile.js.map +7 -0
  198. package/dist/proxy.d.ts +50 -0
  199. package/dist/proxy.js +165 -0
  200. package/dist/proxy.js.map +7 -0
  201. package/dist/rate-limiter.d.ts +31 -0
  202. package/dist/rate-limiter.js +64 -0
  203. package/dist/rate-limiter.js.map +7 -0
  204. package/dist/redirect-tracker.d.ts +37 -0
  205. package/dist/redirect-tracker.js +65 -0
  206. package/dist/redirect-tracker.js.map +7 -0
  207. package/dist/redis-frontier.d.ts +48 -0
  208. package/dist/redis-frontier.js +126 -0
  209. package/dist/redis-frontier.js.map +7 -0
  210. package/dist/redis-queue.d.ts +48 -0
  211. package/dist/redis-queue.js +165 -0
  212. package/dist/redis-queue.js.map +7 -0
  213. package/dist/response-cache.d.ts +38 -0
  214. package/dist/response-cache.js +140 -0
  215. package/dist/response-cache.js.map +7 -0
  216. package/dist/responsive-detector.d.ts +29 -0
  217. package/dist/responsive-detector.js +46 -0
  218. package/dist/responsive-detector.js.map +7 -0
  219. package/dist/retry-policy.d.ts +20 -0
  220. package/dist/retry-policy.js +54 -0
  221. package/dist/retry-policy.js.map +7 -0
  222. package/dist/robots-txt.d.ts +9 -0
  223. package/dist/robots-txt.js +77 -0
  224. package/dist/robots-txt.js.map +7 -0
  225. package/dist/screen-reader.d.ts +15 -0
  226. package/dist/screen-reader.js +48 -0
  227. package/dist/screen-reader.js.map +7 -0
  228. package/dist/screenshot-masker.d.ts +28 -0
  229. package/dist/screenshot-masker.js +62 -0
  230. package/dist/screenshot-masker.js.map +7 -0
  231. package/dist/seo-checker.d.ts +33 -0
  232. package/dist/seo-checker.js +91 -0
  233. package/dist/seo-checker.js.map +7 -0
  234. package/dist/service-worker-interceptor.d.ts +36 -0
  235. package/dist/service-worker-interceptor.js +136 -0
  236. package/dist/service-worker-interceptor.js.map +7 -0
  237. package/dist/session-capture.d.ts +39 -0
  238. package/dist/session-capture.js +274 -0
  239. package/dist/session-capture.js.map +7 -0
  240. package/dist/session-export.d.ts +49 -0
  241. package/dist/session-export.js +117 -0
  242. package/dist/session-export.js.map +7 -0
  243. package/dist/shadow-dom-crawler.d.ts +35 -0
  244. package/dist/shadow-dom-crawler.js +132 -0
  245. package/dist/shadow-dom-crawler.js.map +7 -0
  246. package/dist/sitemap-writer.d.ts +21 -0
  247. package/dist/sitemap-writer.js +47 -0
  248. package/dist/sitemap-writer.js.map +7 -0
  249. package/dist/sitemap.d.ts +51 -0
  250. package/dist/sitemap.js +160 -0
  251. package/dist/sitemap.js.map +7 -0
  252. package/dist/skyvern.d.ts +56 -0
  253. package/dist/skyvern.js +129 -0
  254. package/dist/skyvern.js.map +7 -0
  255. package/dist/sqs-queue.d.ts +13 -0
  256. package/dist/sqs-queue.js +115 -0
  257. package/dist/sqs-queue.js.map +7 -0
  258. package/dist/sso-token.d.ts +29 -0
  259. package/dist/sso-token.js +76 -0
  260. package/dist/sso-token.js.map +7 -0
  261. package/dist/state-graph.d.ts +59 -0
  262. package/dist/state-graph.js +193 -0
  263. package/dist/state-graph.js.map +7 -0
  264. package/dist/stealth/human-behavior.d.ts +20 -0
  265. package/dist/stealth/human-behavior.js +202 -0
  266. package/dist/stealth/human-behavior.js.map +7 -0
  267. package/dist/telemetry.d.ts +34 -0
  268. package/dist/telemetry.js +77 -0
  269. package/dist/telemetry.js.map +7 -0
  270. package/dist/test-executor.d.ts +81 -0
  271. package/dist/test-executor.js +443 -0
  272. package/dist/test-executor.js.map +7 -0
  273. package/dist/tls-fingerprint.d.ts +50 -0
  274. package/dist/tls-fingerprint.js +81 -0
  275. package/dist/tls-fingerprint.js.map +7 -0
  276. package/dist/url-pattern-completer.d.ts +28 -0
  277. package/dist/url-pattern-completer.js +122 -0
  278. package/dist/url-pattern-completer.js.map +7 -0
  279. package/dist/visual-diff-verdict.d.ts +40 -0
  280. package/dist/visual-diff-verdict.js +27 -0
  281. package/dist/visual-diff-verdict.js.map +7 -0
  282. package/dist/webhook-notifier.d.ts +30 -0
  283. package/dist/webhook-notifier.js +76 -0
  284. package/dist/webhook-notifier.js.map +7 -0
  285. package/dist/websocket-detector.d.ts +24 -0
  286. package/dist/websocket-detector.js +60 -0
  287. package/dist/websocket-detector.js.map +7 -0
  288. package/dist/worker.d.ts +1 -0
  289. package/dist/worker.js +33 -0
  290. package/dist/worker.js.map +7 -0
  291. package/package.json +49 -4
  292. package/src/accessibility-auditor.ts +102 -0
  293. package/src/accessibility-tree-crawler.ts +246 -0
  294. package/src/advanced-ui-probes.ts +470 -0
  295. package/src/ai-login.ts +298 -0
  296. package/src/auth-token-rotator.ts +89 -0
  297. package/src/auth.ts +418 -0
  298. package/src/browser-extension.ts +186 -0
  299. package/src/captcha-solver.ts +143 -0
  300. package/src/color-contrast.ts +116 -0
  301. package/src/content-chunker.ts +170 -0
  302. package/src/content-dedup.ts +99 -0
  303. package/src/cookie-consent.ts +106 -0
  304. package/src/cookie-jar.ts +91 -0
  305. package/src/crawl-session-manager.ts +231 -0
  306. package/src/crawler.ts +7123 -0
  307. package/src/crawler.ts.rej +12 -0
  308. package/src/crud-interaction.ts +386 -0
  309. package/src/custom-js-injector.ts +93 -0
  310. package/src/dark-mode-capture.ts +110 -0
  311. package/src/dead-link-detector.ts +76 -0
  312. package/src/dropdown-enumerator.ts +312 -0
  313. package/src/element-hash.ts +16 -0
  314. package/src/email-otp.ts +252 -0
  315. package/src/engine-router.ts +141 -0
  316. package/src/export/bulk-export.ts +85 -0
  317. package/src/export/vector-export.ts +160 -0
  318. package/src/extractors/advanced.ts +309 -0
  319. package/src/extractors/artifact-envelope.ts +111 -0
  320. package/src/extractors/branding-extractor.ts +159 -0
  321. package/src/extractors/summary-extractor.ts +49 -0
  322. package/src/failure-analysis-agent.ts +51 -0
  323. package/src/feature-flag-detector.ts +97 -0
  324. package/src/filter-pagination-crawler.ts +500 -0
  325. package/src/form-detector.ts +103 -0
  326. package/src/frontier.test.ts +64 -0
  327. package/src/frontier.ts +144 -0
  328. package/src/gap-pass.ts +165 -0
  329. package/src/graphql-detector.ts +129 -0
  330. package/src/http-engine.ts +453 -0
  331. package/src/incremental-diff.ts +259 -0
  332. package/src/index.ts +52 -0
  333. package/src/input-fuzzer.ts +129 -0
  334. package/src/interactive-probe.ts +1136 -0
  335. package/src/job-queue.test.ts +187 -0
  336. package/src/job-queue.ts +4504 -0
  337. package/src/js-coverage.ts +87 -0
  338. package/src/js-router-detector.ts +112 -0
  339. package/src/language-detector.ts +82 -0
  340. package/src/link-classifier.ts +65 -0
  341. package/src/live-view.ts +146 -0
  342. package/src/locator-extractor.ts +309 -0
  343. package/src/logger.ts +9 -0
  344. package/src/markdown-extract.test.ts +73 -0
  345. package/src/markdown-extract.ts +76 -0
  346. package/src/multi-context-handler.ts +296 -0
  347. package/src/native-crawler.ts +1108 -0
  348. package/src/nested-window-crawler.ts +310 -0
  349. package/src/network-action-correlator.ts +320 -0
  350. package/src/network-observer.ts +204 -0
  351. package/src/network-throttle.ts +69 -0
  352. package/src/openapi-generator.ts +135 -0
  353. package/src/pdf-extractor.ts +123 -0
  354. package/src/performance-metrics-collector.ts +243 -0
  355. package/src/performance-metrics.ts +134 -0
  356. package/src/phash.ts +184 -0
  357. package/src/pii-scrubber.ts +99 -0
  358. package/src/playwright-recorder.ts +170 -0
  359. package/src/policies/crawl-safety.ts +52 -0
  360. package/src/profiles/domain-profile.ts +190 -0
  361. package/src/proxy.ts +238 -0
  362. package/src/rate-limiter.ts +87 -0
  363. package/src/redirect-tracker.ts +97 -0
  364. package/src/redis-frontier.ts +143 -0
  365. package/src/redis-queue.ts +208 -0
  366. package/src/response-cache.ts +150 -0
  367. package/src/responsive-detector.ts +92 -0
  368. package/src/retry-policy.ts +87 -0
  369. package/src/robots-txt.ts +96 -0
  370. package/src/screen-reader.ts +98 -0
  371. package/src/screenshot-masker.ts +98 -0
  372. package/src/seo-checker.ts +134 -0
  373. package/src/service-worker-interceptor.ts +185 -0
  374. package/src/session-capture.ts +335 -0
  375. package/src/session-export.test.ts +75 -0
  376. package/src/session-export.ts +172 -0
  377. package/src/shadow-dom-crawler.ts +198 -0
  378. package/src/sitemap-writer.ts +65 -0
  379. package/src/sitemap.test.ts +127 -0
  380. package/src/sitemap.ts +233 -0
  381. package/src/skyvern.ts +199 -0
  382. package/src/sqs-queue.ts +129 -0
  383. package/src/sso-token.ts +120 -0
  384. package/src/state-graph.ts +280 -0
  385. package/src/stealth/human-behavior.ts +227 -0
  386. package/src/telemetry.ts +122 -0
  387. package/src/test-executor.test.ts +72 -0
  388. package/src/test-executor.ts +578 -0
  389. package/src/tls-fingerprint.ts +111 -0
  390. package/src/url-pattern-completer.ts +161 -0
  391. package/src/vendor.d.ts +20 -0
  392. package/src/visual-diff-verdict.test.ts +80 -0
  393. package/src/visual-diff-verdict.ts +59 -0
  394. package/src/webhook-notifier.ts +109 -0
  395. package/src/websocket-detector.ts +83 -0
  396. package/src/worker.ts +50 -0
  397. package/tsconfig.json +13 -0
  398. package/types/accessibility-auditor.d.ts +29 -0
  399. package/types/accessibility-tree-crawler.d.ts +44 -0
  400. package/types/advanced-ui-probes.d.ts +47 -0
  401. package/types/ai-login.d.ts +32 -0
  402. package/types/auth-token-rotator.d.ts +47 -0
  403. package/types/auth.d.ts +20 -0
  404. package/types/browser-extension.d.ts +37 -0
  405. package/types/bull-worker.d.ts +6 -0
  406. package/types/captcha-solver.d.ts +16 -0
  407. package/types/color-contrast.d.ts +30 -0
  408. package/types/content-chunker.d.ts +18 -0
  409. package/types/content-dedup.d.ts +23 -0
  410. package/types/cookie-consent.d.ts +25 -0
  411. package/types/cookie-jar.d.ts +20 -0
  412. package/types/crawl-session-manager.d.ts +44 -0
  413. package/types/crawler.d.ts +233 -0
  414. package/types/crud-interaction.d.ts +31 -0
  415. package/types/custom-js-injector.d.ts +42 -0
  416. package/types/dark-mode-capture.d.ts +23 -0
  417. package/types/dead-link-detector.d.ts +37 -0
  418. package/types/dropdown-enumerator.d.ts +20 -0
  419. package/types/element-hash.d.ts +9 -0
  420. package/types/email-otp.d.ts +42 -0
  421. package/types/engine-router.d.ts +47 -0
  422. package/types/export/bulk-export.d.ts +26 -0
  423. package/types/export/vector-export.d.ts +76 -0
  424. package/types/extractors/advanced.d.ts +64 -0
  425. package/types/extractors/artifact-envelope.d.ts +63 -0
  426. package/types/extractors/branding-extractor.d.ts +46 -0
  427. package/types/extractors/summary-extractor.d.ts +10 -0
  428. package/types/failure-analysis-agent.d.ts +16 -0
  429. package/types/feature-flag-detector.d.ts +30 -0
  430. package/types/filter-pagination-crawler.d.ts +52 -0
  431. package/types/form-detector.d.ts +34 -0
  432. package/types/frontier.d.ts +40 -0
  433. package/types/gap-pass.d.ts +36 -0
  434. package/types/graphql-detector.d.ts +39 -0
  435. package/types/http-engine.d.ts +80 -0
  436. package/types/incremental-diff.d.ts +116 -0
  437. package/types/index.d.ts +52 -0
  438. package/types/input-fuzzer.d.ts +30 -0
  439. package/types/interactive-probe.d.ts +144 -0
  440. package/types/job-queue.d.ts +308 -0
  441. package/types/js-coverage.d.ts +29 -0
  442. package/types/js-router-detector.d.ts +17 -0
  443. package/types/language-detector.d.ts +22 -0
  444. package/types/link-classifier.d.ts +23 -0
  445. package/types/live-view.d.ts +36 -0
  446. package/types/locator-extractor.d.ts +46 -0
  447. package/types/logger.d.ts +2 -0
  448. package/types/markdown-extract.d.ts +8 -0
  449. package/types/multi-context-handler.d.ts +55 -0
  450. package/types/native-crawler.d.ts +69 -0
  451. package/types/nested-window-crawler.d.ts +41 -0
  452. package/types/network-action-correlator.d.ts +47 -0
  453. package/types/network-observer.d.ts +49 -0
  454. package/types/network-throttle.d.ts +25 -0
  455. package/types/openapi-generator.d.ts +49 -0
  456. package/types/pdf-extractor.d.ts +28 -0
  457. package/types/performance-metrics-collector.d.ts +50 -0
  458. package/types/performance-metrics.d.ts +52 -0
  459. package/types/phash.d.ts +50 -0
  460. package/types/pii-scrubber.d.ts +21 -0
  461. package/types/playwright-recorder.d.ts +39 -0
  462. package/types/policies/crawl-safety.d.ts +9 -0
  463. package/types/profiles/domain-profile.d.ts +19 -0
  464. package/types/proxy.d.ts +50 -0
  465. package/types/rate-limiter.d.ts +31 -0
  466. package/types/redirect-tracker.d.ts +37 -0
  467. package/types/redis-frontier.d.ts +48 -0
  468. package/types/redis-queue.d.ts +48 -0
  469. package/types/response-cache.d.ts +38 -0
  470. package/types/responsive-detector.d.ts +29 -0
  471. package/types/retry-policy.d.ts +20 -0
  472. package/types/robots-txt.d.ts +9 -0
  473. package/types/screen-reader.d.ts +15 -0
  474. package/types/screenshot-masker.d.ts +28 -0
  475. package/types/seo-checker.d.ts +33 -0
  476. package/types/service-worker-interceptor.d.ts +36 -0
  477. package/types/session-capture.d.ts +39 -0
  478. package/types/session-export.d.ts +49 -0
  479. package/types/shadow-dom-crawler.d.ts +35 -0
  480. package/types/sitemap-writer.d.ts +21 -0
  481. package/types/sitemap.d.ts +51 -0
  482. package/types/skyvern.d.ts +56 -0
  483. package/types/sqs-queue.d.ts +13 -0
  484. package/types/sso-token.d.ts +29 -0
  485. package/types/state-graph.d.ts +59 -0
  486. package/types/stealth/human-behavior.d.ts +20 -0
  487. package/types/telemetry.d.ts +34 -0
  488. package/types/test-executor.d.ts +84 -0
  489. package/types/tls-fingerprint.d.ts +50 -0
  490. package/types/url-pattern-completer.d.ts +28 -0
  491. package/types/visual-diff-verdict.d.ts +40 -0
  492. package/types/webhook-notifier.d.ts +30 -0
  493. package/types/websocket-detector.d.ts +24 -0
  494. package/types/worker.d.ts +1 -0
@@ -0,0 +1,172 @@
1
+ /**
2
+ * Converts a Playwright storageState (the format ZeTa already captures via
3
+ * session-capture.ts/live-view.ts and stores per-project) into snippets for
4
+ * other automation frameworks. Playwright's storageState is the de-facto
5
+ * standard for browser session export — see docs/UNIVERSAL-SESSION-CAPTURE-
6
+ * STRATEGY.md — so every other framework's import mechanism is a small,
7
+ * mechanical conversion away, not a new capture flow.
8
+ *
9
+ * Pure, dependency-free functions — no I/O, no network, fully unit-testable.
10
+ */
11
+
12
+ export interface StorageStateCookie {
13
+ name: string;
14
+ value: string;
15
+ domain: string;
16
+ path: string;
17
+ expires?: number;
18
+ httpOnly?: boolean;
19
+ secure?: boolean;
20
+ sameSite?: 'Strict' | 'Lax' | 'None';
21
+ }
22
+
23
+ export interface StorageStateOrigin {
24
+ origin: string;
25
+ localStorage: Array<{ name: string; value: string }>;
26
+ }
27
+
28
+ export interface StorageState {
29
+ cookies: StorageStateCookie[];
30
+ origins: StorageStateOrigin[];
31
+ }
32
+
33
+ export type SessionExportFormat = 'cypress' | 'selenium-python' | 'webdriverio';
34
+
35
+ function jsStringLiteral(s: string): string {
36
+ return JSON.stringify(s);
37
+ }
38
+
39
+ /** Python dict-literal repr — JSON.stringify's `true`/`false`/`null` aren't
40
+ * valid Python (Python needs `True`/`False`/`None`), so a plain
41
+ * JSON.stringify would emit syntactically invalid Python for any cookie
42
+ * with a boolean field. */
43
+ function pythonDictLiteral(obj: Record<string, unknown>): string {
44
+ const parts = Object.entries(obj).map(([k, v]) => {
45
+ let repr: string;
46
+ if (typeof v === 'boolean') repr = v ? 'True' : 'False';
47
+ else if (v === null || v === undefined) repr = 'None';
48
+ else if (typeof v === 'number') repr = String(v);
49
+ else repr = JSON.stringify(v);
50
+ return `${JSON.stringify(k)}: ${repr}`;
51
+ });
52
+ return `{${parts.join(', ')}}`;
53
+ }
54
+
55
+ /** cy.setCookie() + localStorage.setItem() inside a reusable custom command,
56
+ * following Cypress's documented cy.session() pattern (see references in
57
+ * docs/UNIVERSAL-SESSION-CAPTURE-STRATEGY.md). */
58
+ export function toCypressSnippet(state: StorageState): string {
59
+ const cookieLines = state.cookies.map((c) => {
60
+ const opts: string[] = [`domain: ${jsStringLiteral(c.domain)}`, `path: ${jsStringLiteral(c.path)}`];
61
+ if (c.secure) opts.push('secure: true');
62
+ if (c.httpOnly) opts.push('httpOnly: true');
63
+ if (c.sameSite) opts.push(`sameSite: ${jsStringLiteral(c.sameSite.toLowerCase())}`);
64
+ return ` cy.setCookie(${jsStringLiteral(c.name)}, ${jsStringLiteral(c.value)}, { ${opts.join(', ')} });`;
65
+ });
66
+
67
+ const localStorageBlocks = state.origins
68
+ .filter((o) => o.localStorage.length > 0)
69
+ .map((o) => {
70
+ const sets = o.localStorage
71
+ .map((kv) => ` window.localStorage.setItem(${jsStringLiteral(kv.name)}, ${jsStringLiteral(kv.value)});`)
72
+ .join('\n');
73
+ return ` cy.visit(${jsStringLiteral(o.origin)});\n cy.window().then((window) => {\n${sets}\n });`;
74
+ });
75
+
76
+ return [
77
+ '// Generated by ZeTa-AI — restores a captured browser session in Cypress.',
78
+ '// Usage: import this file and call restoreZetaSession() inside cy.session(), e.g.:',
79
+ "// cy.session('zeta-session', restoreZetaSession)",
80
+ "Cypress.Commands.add('restoreZetaSession', () => {",
81
+ ...cookieLines,
82
+ ...localStorageBlocks,
83
+ '});',
84
+ ].join('\n');
85
+ }
86
+
87
+ /** driver.add_cookie() (Selenium requires the browser already be on the
88
+ * cookie's domain before adding it) + execute_script() for localStorage,
89
+ * per Selenium's documented Cookie API. */
90
+ export function toSeleniumPythonDict(state: StorageState): string {
91
+ const byDomain = new Map<string, StorageStateCookie[]>();
92
+ for (const c of state.cookies) {
93
+ const list = byDomain.get(c.domain) ?? [];
94
+ list.push(c);
95
+ byDomain.set(c.domain, list);
96
+ }
97
+
98
+ const lines: string[] = [
99
+ '# Generated by ZeTa-AI — restores a captured browser session in Selenium.',
100
+ '# Selenium requires the driver to already be on a matching domain before',
101
+ '# add_cookie() will accept a cookie for it — navigate first, then restore.',
102
+ 'def restore_zeta_session(driver):',
103
+ ];
104
+
105
+ for (const [domain, cookies] of byDomain) {
106
+ lines.push(` driver.get(${JSON.stringify(`https://${domain.replace(/^\./, '')}`)})`);
107
+ for (const c of cookies) {
108
+ const cookieDict: Record<string, unknown> = { name: c.name, value: c.value, path: c.path };
109
+ if (c.expires && c.expires > 0) cookieDict.expiry = Math.floor(c.expires);
110
+ if (c.secure) cookieDict.secure = true;
111
+ lines.push(` driver.add_cookie(${pythonDictLiteral(cookieDict)})`);
112
+ }
113
+ }
114
+
115
+ for (const o of state.origins) {
116
+ if (o.localStorage.length === 0) continue;
117
+ lines.push(` driver.get(${JSON.stringify(o.origin)})`);
118
+ for (const kv of o.localStorage) {
119
+ lines.push(
120
+ ` driver.execute_script(${JSON.stringify(`window.localStorage.setItem(arguments[0], arguments[1]);`)}, ${JSON.stringify(kv.name)}, ${JSON.stringify(kv.value)})`,
121
+ );
122
+ }
123
+ }
124
+
125
+ return lines.join('\n');
126
+ }
127
+
128
+ /** browser.setCookies() accepts an array shape very close to Playwright's own
129
+ * cookie objects (per WebdriverIO's documented API) — closest 1:1 mapping
130
+ * of the three target formats. */
131
+ export function toWebdriverIOSnippet(state: StorageState): string {
132
+ const cookies = state.cookies.map((c) => ({
133
+ name: c.name,
134
+ value: c.value,
135
+ domain: c.domain,
136
+ path: c.path,
137
+ secure: !!c.secure,
138
+ httpOnly: !!c.httpOnly,
139
+ ...(c.expires && c.expires > 0 ? { expiry: Math.floor(c.expires) } : {}),
140
+ }));
141
+
142
+ const localStorageSets = state.origins
143
+ .filter((o) => o.localStorage.length > 0)
144
+ .map((o) => {
145
+ const sets = o.localStorage
146
+ .map((kv) => ` localStorage.setItem(${jsStringLiteral(kv.name)}, ${jsStringLiteral(kv.value)});`)
147
+ .join('\n');
148
+ return ` await browser.url(${jsStringLiteral(o.origin)});\n await browser.execute(() => {\n${sets}\n });`;
149
+ });
150
+
151
+ return [
152
+ '// Generated by ZeTa-AI — restores a captured browser session in WebdriverIO.',
153
+ 'async function restoreZetaSession(browser) {',
154
+ ` await browser.setCookies(${JSON.stringify(cookies, null, 2).split('\n').join('\n ')});`,
155
+ ...localStorageSets,
156
+ '}',
157
+ 'module.exports = { restoreZetaSession };',
158
+ ].join('\n');
159
+ }
160
+
161
+ export function exportSessionAs(format: SessionExportFormat, state: StorageState): { content: string; filename: string; mimeType: string } {
162
+ switch (format) {
163
+ case 'cypress':
164
+ return { content: toCypressSnippet(state), filename: 'zeta-session.cy-commands.js', mimeType: 'application/javascript' };
165
+ case 'selenium-python':
166
+ return { content: toSeleniumPythonDict(state), filename: 'zeta_session.py', mimeType: 'text/x-python' };
167
+ case 'webdriverio':
168
+ return { content: toWebdriverIOSnippet(state), filename: 'zeta-session.wdio.js', mimeType: 'application/javascript' };
169
+ default:
170
+ throw new Error(`Unknown session export format: ${format}`);
171
+ }
172
+ }
@@ -0,0 +1,198 @@
1
+ /**
2
+ * Shadow DOM crawler — enumerate shadow hosts and detect web component libraries.
3
+ *
4
+ * Walks the live DOM recursively (up to depth 8) to find all open shadow roots
5
+ * and custom elements with closed shadow roots, then classifies the component
6
+ * framework in use. Results feed ZeTa's test generator so it can account for
7
+ * elements that are invisible to standard Playwright locators.
8
+ */
9
+
10
+ import type { Page } from 'playwright';
11
+
12
+ export interface ShadowInteractiveEl {
13
+ role: string;
14
+ label: string;
15
+ type: string;
16
+ tagName: string;
17
+ }
18
+
19
+ export interface ShadowHostInfo {
20
+ tagName: string;
21
+ hostId: string;
22
+ shadowMode: 'open' | 'closed';
23
+ depth: number;
24
+ interactiveCount: number;
25
+ interactiveElements: ShadowInteractiveEl[];
26
+ slotNames: string[];
27
+ childShadowHosts: number;
28
+ }
29
+
30
+ export interface ShadowDomCrawlResult {
31
+ shadowHostCount: number;
32
+ openShadowCount: number;
33
+ closedShadowCount: number;
34
+ maxNestingDepth: number;
35
+ hosts: ShadowHostInfo[];
36
+ totalInteractiveInShadow: number;
37
+ detectedLibraries: string[];
38
+ blindSpots: string[];
39
+ }
40
+
41
+ interface RawShadowHost {
42
+ tagName: string;
43
+ hostId: string;
44
+ shadowMode: 'open' | 'closed';
45
+ depth: number;
46
+ interactiveCount: number;
47
+ interactiveDetails: Array<{ role: string; label: string; type: string; tagName: string }>;
48
+ slotNames: string[];
49
+ childShadowHosts: number;
50
+ }
51
+
52
+ async function enumerateShadowHosts(page: any): Promise<ShadowHostInfo[]> {
53
+ const raw: RawShadowHost[] = await page.evaluate(() => {
54
+ // esbuild injects __name() for named function declarations; polyfill prevents ReferenceError in browser.
55
+ if (typeof (globalThis as any).__name === 'undefined') (globalThis as any).__name = (fn: any) => fn;
56
+ function collectHosts(root: Document | ShadowRoot, depth: number): RawShadowHost[] {
57
+ if (depth > 8) return [];
58
+ const hosts: RawShadowHost[] = [];
59
+ const els = root.querySelectorAll('*');
60
+ for (const el of Array.from(els)) {
61
+ const htmlEl = el as HTMLElement & { shadowRoot: ShadowRoot | null };
62
+ if (htmlEl.shadowRoot) {
63
+ const interactive = htmlEl.shadowRoot.querySelectorAll(
64
+ 'button,input,select,textarea,a[href],[role="button"],[role="link"],[role="checkbox"],[role="menuitem"],[role="tab"],[role="switch"]'
65
+ );
66
+ const slots = Array.from(htmlEl.shadowRoot.querySelectorAll('slot')).map(
67
+ (s) => (s as HTMLSlotElement).name || 'default'
68
+ );
69
+ const nestedHosts = Array.from(htmlEl.shadowRoot.querySelectorAll('*')).filter(
70
+ (c) => (c as HTMLElement & { shadowRoot: ShadowRoot | null }).shadowRoot
71
+ ).length;
72
+ hosts.push({
73
+ tagName: htmlEl.tagName.toLowerCase(),
74
+ hostId:
75
+ htmlEl.id ||
76
+ (htmlEl.className?.toString()?.split(' ')[0]) ||
77
+ '',
78
+ shadowMode: 'open',
79
+ depth,
80
+ interactiveCount: interactive.length,
81
+ interactiveDetails: Array.from(interactive)
82
+ .slice(0, 8)
83
+ .map((i) => {
84
+ const iEl = i as HTMLElement;
85
+ return {
86
+ role: iEl.getAttribute('role') || iEl.tagName.toLowerCase(),
87
+ label: (
88
+ iEl.getAttribute('aria-label') ||
89
+ iEl.getAttribute('placeholder') ||
90
+ iEl.textContent?.trim() ||
91
+ ''
92
+ ).slice(0, 40),
93
+ type: iEl.getAttribute('type') || '',
94
+ tagName: iEl.tagName.toLowerCase(),
95
+ };
96
+ }),
97
+ slotNames: slots,
98
+ childShadowHosts: nestedHosts,
99
+ });
100
+ const nested = collectHosts(htmlEl.shadowRoot, depth + 1);
101
+ hosts.push(...nested);
102
+ } else if (el.tagName.includes('-') && !(el as HTMLElement & { shadowRoot: ShadowRoot | null }).shadowRoot) {
103
+ hosts.push({
104
+ tagName: el.tagName.toLowerCase(),
105
+ hostId: (el as HTMLElement).id || '',
106
+ shadowMode: 'closed',
107
+ depth,
108
+ interactiveCount: 0,
109
+ interactiveDetails: [],
110
+ slotNames: [],
111
+ childShadowHosts: 0,
112
+ });
113
+ }
114
+ }
115
+ return hosts;
116
+ }
117
+ return collectHosts(document, 0);
118
+ });
119
+
120
+ return raw.slice(0, 100).map((h) => ({
121
+ tagName: h.tagName,
122
+ hostId: h.hostId,
123
+ shadowMode: h.shadowMode,
124
+ depth: h.depth,
125
+ interactiveCount: h.interactiveCount,
126
+ interactiveElements: h.interactiveDetails,
127
+ slotNames: h.slotNames,
128
+ childShadowHosts: h.childShadowHosts,
129
+ }));
130
+ }
131
+
132
+ async function detectWebComponentLibrary(page: any): Promise<string[]> {
133
+ return page.evaluate(() => {
134
+ const checks: [string, string][] = [
135
+ ['sl-button', 'Shoelace'],
136
+ ['fast-button', 'Microsoft FAST'],
137
+ ['mwc-button', 'Material Web Components'],
138
+ ['ion-button', 'Ionic'],
139
+ ['vaadin-button', 'Vaadin'],
140
+ ['fluent-button', 'Fluent UI Web Components'],
141
+ ['hy-icon', 'Hydra'],
142
+ ];
143
+ const detected: string[] = [];
144
+ for (const [tag, name] of checks) {
145
+ if (customElements.get(tag)) detected.push(name);
146
+ }
147
+ const allTags = Array.from(document.querySelectorAll('*'))
148
+ .map((e) => e.tagName.toLowerCase())
149
+ .filter((t) => t.includes('-'));
150
+ const uniqueTags = Array.from(new Set(allTags));
151
+ if (uniqueTags.some((t) => t.startsWith('sl-')) && !detected.includes('Shoelace'))
152
+ detected.push('Shoelace');
153
+ if (uniqueTags.some((t) => t.startsWith('fast-')) && !detected.includes('Microsoft FAST'))
154
+ detected.push('Microsoft FAST');
155
+ if (uniqueTags.some((t) => t.startsWith('ion-')) && !detected.includes('Ionic'))
156
+ detected.push('Ionic');
157
+ return detected;
158
+ });
159
+ }
160
+
161
+ export async function crawlShadowDom(page: any): Promise<ShadowDomCrawlResult> {
162
+ const empty: ShadowDomCrawlResult = {
163
+ shadowHostCount: 0,
164
+ openShadowCount: 0,
165
+ closedShadowCount: 0,
166
+ maxNestingDepth: 0,
167
+ hosts: [],
168
+ totalInteractiveInShadow: 0,
169
+ detectedLibraries: [],
170
+ blindSpots: [],
171
+ };
172
+
173
+ try {
174
+ const [hosts, detectedLibraries] = await Promise.all([
175
+ enumerateShadowHosts(page),
176
+ detectWebComponentLibrary(page),
177
+ ]);
178
+
179
+ const openHosts = hosts.filter((h) => h.shadowMode === 'open');
180
+ const closedHosts = hosts.filter((h) => h.shadowMode === 'closed');
181
+ const maxNestingDepth = hosts.reduce((max, h) => Math.max(max, h.depth), 0);
182
+ const totalInteractiveInShadow = openHosts.reduce((sum, h) => sum + h.interactiveCount, 0);
183
+ const blindSpots = [...new Set(closedHosts.map((h) => h.tagName))];
184
+
185
+ return {
186
+ shadowHostCount: hosts.length,
187
+ openShadowCount: openHosts.length,
188
+ closedShadowCount: closedHosts.length,
189
+ maxNestingDepth,
190
+ hosts,
191
+ totalInteractiveInShadow,
192
+ detectedLibraries,
193
+ blindSpots,
194
+ };
195
+ } catch {
196
+ return empty;
197
+ }
198
+ }
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Sitemap writer — generate XML sitemap from crawled screens.
3
+ *
4
+ * Compliant with sitemaps.org protocol (sitemap index + individual sitemap files).
5
+ * Splits into 50k-URL files per the spec when needed.
6
+ * Used for: SEO audit export, future crawl seed, and crawler/robots.txt comparison.
7
+ */
8
+
9
+ import * as fs from 'fs/promises';
10
+ import * as path from 'path';
11
+
12
+ export interface SitemapUrl {
13
+ loc: string;
14
+ lastmod?: string;
15
+ changefreq?: 'always' | 'hourly' | 'daily' | 'weekly' | 'monthly' | 'yearly' | 'never';
16
+ priority?: number; // 0.0–1.0
17
+ }
18
+
19
+ const XML_HEADER = '<?xml version="1.0" encoding="UTF-8"?>\n';
20
+ const SITEMAP_NS = 'xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"';
21
+
22
+ function escape(s: string): string {
23
+ return s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&apos;');
24
+ }
25
+
26
+ function buildSitemapXml(urls: SitemapUrl[]): string {
27
+ const entries = urls.map(u => [
28
+ ' <url>',
29
+ ` <loc>${escape(u.loc)}</loc>`,
30
+ u.lastmod ? ` <lastmod>${u.lastmod}</lastmod>` : '',
31
+ u.changefreq ? ` <changefreq>${u.changefreq}</changefreq>` : '',
32
+ u.priority !== undefined ? ` <priority>${u.priority.toFixed(1)}</priority>` : '',
33
+ ' </url>',
34
+ ].filter(Boolean).join('\n')).join('\n');
35
+
36
+ return `${XML_HEADER}<urlset ${SITEMAP_NS}>\n${entries}\n</urlset>`;
37
+ }
38
+
39
+ export async function writeSitemap(
40
+ urls: SitemapUrl[],
41
+ outputPath: string,
42
+ ): Promise<void> {
43
+ await fs.mkdir(path.dirname(outputPath), { recursive: true });
44
+ await fs.writeFile(outputPath, buildSitemapXml(urls), 'utf8');
45
+ }
46
+
47
+ /** Convert crawled screen records to SitemapUrl objects */
48
+ export function screensToSitemapUrls(
49
+ screens: Array<{ url: string; updatedAt?: Date; depth?: number }>,
50
+ ): SitemapUrl[] {
51
+ return screens
52
+ .filter(s => {
53
+ try { return new URL(s.url).protocol.startsWith('http'); } catch { return false; }
54
+ })
55
+ .map(s => ({
56
+ loc: s.url,
57
+ lastmod: s.updatedAt ? s.updatedAt.toISOString().split('T')[0] : undefined,
58
+ changefreq: 'weekly' as const,
59
+ priority: s.depth === 0 ? 1.0 : s.depth === 1 ? 0.8 : 0.6,
60
+ }));
61
+ }
62
+
63
+ export function buildSitemapString(urls: SitemapUrl[]): string {
64
+ return buildSitemapXml(urls);
65
+ }
@@ -0,0 +1,127 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import http from 'node:http';
4
+ import os from 'node:os';
5
+ import { MockAgent } from 'undici';
6
+ import { XMLParser } from 'fast-xml-parser';
7
+ import { extractUrlsFromParsedSitemap, fetchSitemapUrls, discoverSitemapUrls } from './sitemap.js';
8
+
9
+ const parser = new XMLParser();
10
+
11
+ test('extractUrlsFromParsedSitemap: parses a plain <urlset> sitemap', () => {
12
+ const xml = `<?xml version="1.0" encoding="UTF-8"?>
13
+ <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
14
+ <url><loc>https://example.com/</loc></url>
15
+ <url><loc>https://example.com/pricing</loc></url>
16
+ <url><loc>https://example.com/about</loc></url>
17
+ </urlset>`;
18
+ const { pageUrls, subSitemapUrls } = extractUrlsFromParsedSitemap(parser.parse(xml), 'https://example.com');
19
+ assert.deepEqual(pageUrls.sort(), ['https://example.com/', 'https://example.com/about', 'https://example.com/pricing'].sort());
20
+ assert.deepEqual(subSitemapUrls, []);
21
+ });
22
+
23
+ test('extractUrlsFromParsedSitemap: parses a <sitemapindex> into sub-sitemap URLs', () => {
24
+ const xml = `<?xml version="1.0" encoding="UTF-8"?>
25
+ <sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
26
+ <sitemap><loc>https://example.com/sitemap-pages.xml</loc></sitemap>
27
+ <sitemap><loc>https://example.com/sitemap-blog.xml</loc></sitemap>
28
+ </sitemapindex>`;
29
+ const { pageUrls, subSitemapUrls } = extractUrlsFromParsedSitemap(parser.parse(xml), 'https://example.com');
30
+ assert.deepEqual(pageUrls, []);
31
+ assert.deepEqual(subSitemapUrls, ['https://example.com/sitemap-pages.xml', 'https://example.com/sitemap-blog.xml']);
32
+ });
33
+
34
+ test('extractUrlsFromParsedSitemap: filters out cross-origin URLs', () => {
35
+ const xml = `<urlset>
36
+ <url><loc>https://example.com/page</loc></url>
37
+ <url><loc>https://attacker.example/evil</loc></url>
38
+ </urlset>`;
39
+ const { pageUrls } = extractUrlsFromParsedSitemap(parser.parse(xml), 'https://example.com');
40
+ assert.deepEqual(pageUrls, ['https://example.com/page']);
41
+ });
42
+
43
+ test('extractUrlsFromParsedSitemap: dedupes repeated URLs', () => {
44
+ const xml = `<urlset>
45
+ <url><loc>https://example.com/a</loc></url>
46
+ <url><loc>https://example.com/a</loc></url>
47
+ </urlset>`;
48
+ const { pageUrls } = extractUrlsFromParsedSitemap(parser.parse(xml), 'https://example.com');
49
+ assert.deepEqual(pageUrls, ['https://example.com/a']);
50
+ });
51
+
52
+ test('extractUrlsFromParsedSitemap: a single <url> entry is not an array (fast-xml-parser quirk) — still handled', () => {
53
+ const xml = `<urlset><url><loc>https://example.com/only</loc></url></urlset>`;
54
+ const { pageUrls } = extractUrlsFromParsedSitemap(parser.parse(xml), 'https://example.com');
55
+ assert.deepEqual(pageUrls, ['https://example.com/only']);
56
+ });
57
+
58
+ test('extractUrlsFromParsedSitemap: handles empty/malformed input without throwing', () => {
59
+ assert.deepEqual(extractUrlsFromParsedSitemap({}, 'https://example.com'), { pageUrls: [], subSitemapUrls: [] });
60
+ assert.deepEqual(extractUrlsFromParsedSitemap(null, 'https://example.com'), { pageUrls: [], subSitemapUrls: [] });
61
+ assert.deepEqual(extractUrlsFromParsedSitemap({ urlset: { url: [{ notLoc: 'x' }] } }, 'https://example.com'), { pageUrls: [], subSitemapUrls: [] });
62
+ });
63
+
64
+ // --- End-to-end fetch path: real fetch, real XML parse, mocked socket layer ---
65
+ // Bound to the sandbox's actual (non-loopback) address — assertCrawlUrlSafe
66
+ // deliberately blocks literal localhost/127.0.0.1 (meaningless as a crawl
67
+ // target, would just hit the crawler container itself) but allows other
68
+ // addresses, matching how a real deployment reaches a tenant's internal app.
69
+ function getNonLoopbackAddress(): string {
70
+ for (const ifaces of Object.values(os.networkInterfaces())) {
71
+ for (const iface of ifaces as any[]) {
72
+ if (iface.family === 'IPv4' && !iface.internal) return iface.address;
73
+ }
74
+ }
75
+ throw new Error('No non-loopback IPv4 address found on this host — cannot run the live sitemap test');
76
+ }
77
+
78
+ test('fetchSitemapUrls: end-to-end against a sitemap index response', async () => {
79
+ const origin = 'http://example.test';
80
+ const indexXml = `<?xml version="1.0"?>
81
+ <sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
82
+ <sitemap><loc>${origin}/sub1.xml</loc></sitemap>
83
+ <sitemap><loc>${origin}/sub2.xml</loc></sitemap>
84
+ </sitemapindex>`;
85
+ const sub1Xml = `<urlset><url><loc>${origin}/page1</loc></url><url><loc>${origin}/page2</loc></url></urlset>`;
86
+ const sub2Xml = `<urlset><url><loc>${origin}/page3</loc></url></urlset>`;
87
+ const dispatcher = new MockAgent();
88
+ dispatcher.disableNetConnect();
89
+ const pool = dispatcher.get(origin);
90
+ pool.intercept({ path: '/sitemap.xml', method: 'GET' }).reply(200, indexXml);
91
+ pool.intercept({ path: '/sub1.xml', method: 'GET' }).reply(200, sub1Xml);
92
+ pool.intercept({ path: '/sub2.xml', method: 'GET' }).reply(200, sub2Xml);
93
+
94
+ try {
95
+ const urls = await fetchSitemapUrls(`${origin}/sitemap.xml`, origin, 0, dispatcher);
96
+ assert.deepEqual(
97
+ urls.sort(),
98
+ [`${origin}/page1`, `${origin}/page2`, `${origin}/page3`].sort(),
99
+ );
100
+ } finally {
101
+ await dispatcher.close();
102
+ }
103
+ });
104
+
105
+ test('discoverSitemapUrls: defaults to /sitemap.xml at the app origin and returns [] gracefully on 404', async () => {
106
+ const host = getNonLoopbackAddress();
107
+ const server = http.createServer((_req, res) => { res.writeHead(404); res.end(); });
108
+ await new Promise<void>((resolve) => server.listen(0, host, resolve));
109
+ const port = (server.address() as any).port;
110
+
111
+ try {
112
+ const urls = await discoverSitemapUrls(`http://${host}:${port}/app`);
113
+ assert.deepEqual(urls, []);
114
+ } finally {
115
+ server.close();
116
+ }
117
+ });
118
+
119
+ test('discoverSitemapUrls: never throws for an invalid appUrl', async () => {
120
+ const urls = await discoverSitemapUrls('not-a-valid-url');
121
+ assert.deepEqual(urls, []);
122
+ });
123
+
124
+ test('discoverSitemapUrls: is blocked (returns []) for a disallowed target like localhost — assertCrawlUrlSafe still applies', async () => {
125
+ const urls = await discoverSitemapUrls('http://localhost:1/app');
126
+ assert.deepEqual(urls, []);
127
+ });