@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,274 @@
1
+ import { chromium } from "playwright";
2
+ import { randomBytes } from "node:crypto";
3
+ import { login } from "./auth.js";
4
+ import { generateTOTP } from "./job-queue.js";
5
+ import { getNonHeadlessUserAgent } from "./crawler.js";
6
+ const activeSessions = /* @__PURE__ */ new Map();
7
+ async function startSessionCapture(appUrl) {
8
+ const inDocker = process.env.PLAYWRIGHT_IN_DOCKER === "true";
9
+ const noDisplay = process.platform === "linux" && !process.env.DISPLAY;
10
+ if (inDocker || noDisplay) {
11
+ throw new Error("HEADLESS_ENV");
12
+ }
13
+ const id = randomBytes(16).toString("hex");
14
+ const browser = await chromium.launch({ headless: false });
15
+ const userAgent = await getNonHeadlessUserAgent(chromium.executablePath());
16
+ const context = await browser.newContext({ userAgent });
17
+ const page = await context.newPage();
18
+ await page.goto(appUrl, { waitUntil: "domcontentloaded" }).catch(() => {
19
+ });
20
+ const timer = setTimeout(() => cancelSessionCapture(id).catch(() => {
21
+ }), 10 * 60 * 1e3);
22
+ activeSessions.set(id, { browser, context, timer });
23
+ return id;
24
+ }
25
+ async function completeSessionCapture(captureId) {
26
+ const session = activeSessions.get(captureId);
27
+ if (!session) throw new Error("Capture session not found or expired.");
28
+ clearTimeout(session.timer);
29
+ const state = await session.context.storageState();
30
+ await session.browser.close().catch(() => {
31
+ });
32
+ activeSessions.delete(captureId);
33
+ return JSON.stringify(state);
34
+ }
35
+ async function cancelSessionCapture(captureId) {
36
+ const session = activeSessions.get(captureId);
37
+ if (!session) return;
38
+ clearTimeout(session.timer);
39
+ await session.browser.close().catch(() => {
40
+ });
41
+ activeSessions.delete(captureId);
42
+ }
43
+ async function exploreAppAfterLogin(page) {
44
+ const sleep = (ms) => page.waitForTimeout(ms).catch(() => {
45
+ });
46
+ const isVisible = `
47
+ function isVisible(el) {
48
+ const s = window.getComputedStyle(el);
49
+ if (s.display === 'none' || s.visibility === 'hidden' || parseFloat(s.opacity) < 0.1) return false;
50
+ const r = el.getBoundingClientRect();
51
+ return r.width > 4 && r.height > 4;
52
+ }
53
+ `;
54
+ try {
55
+ const origin = await page.evaluate(() => location.origin).catch(() => "");
56
+ const startUrl = await page.evaluate(() => location.href).catch(() => "");
57
+ await page.evaluate(new Function(`
58
+ ${isVisible}
59
+ const inBadCtx = el => {
60
+ let p = el.parentElement;
61
+ while (p) {
62
+ const role = p.getAttribute('role') ?? '';
63
+ if (['dialog','alertdialog','combobox','listbox'].includes(role)) return true;
64
+ p = p.parentElement;
65
+ }
66
+ return false;
67
+ };
68
+ const targets = new Set();
69
+ document.querySelectorAll('[aria-haspopup]:not([aria-expanded="true"])').forEach(el => {
70
+ if (isVisible(el) && !inBadCtx(el)) targets.add(el);
71
+ });
72
+ document.querySelectorAll('[aria-expanded="false"]').forEach(el => {
73
+ if ((el.tagName === 'BUTTON' || el.getAttribute('role') === 'button') && isVisible(el) && !inBadCtx(el)) targets.add(el);
74
+ });
75
+ document.querySelectorAll('[data-state="closed"],[data-headlessui-state="closed"]').forEach(el => {
76
+ if ((el.tagName === 'BUTTON' || el.getAttribute('role') === 'button') && isVisible(el) && !inBadCtx(el)) targets.add(el);
77
+ });
78
+ targets.forEach(el => { try { el.click(); } catch {} });
79
+ `)).catch(() => {
80
+ });
81
+ await sleep(600);
82
+ const navLinks = await page.evaluate(new Function(`
83
+ ${isVisible}
84
+ const links = new Set();
85
+ const navEls = document.querySelectorAll('nav a, [role="navigation"] a, aside a, header a');
86
+ navEls.forEach(a => {
87
+ const href = a.getAttribute('href');
88
+ if (!href || href.startsWith('#') || href.startsWith('mailto:') || href.startsWith('tel:')) return;
89
+ if (!isVisible(a)) return;
90
+ try {
91
+ const url = new URL(href, location.href);
92
+ if (url.origin === location.origin) links.add(url.href);
93
+ } catch {}
94
+ });
95
+ return [...links].slice(0, 10); // cap at 10 nav pages
96
+ `)).catch(() => []);
97
+ await page.keyboard?.press("Escape").catch(() => {
98
+ });
99
+ await sleep(200);
100
+ for (const link of navLinks) {
101
+ try {
102
+ if (link === startUrl) continue;
103
+ await page.goto(link, { waitUntil: "networkidle", timeout: 15e3 }).catch(() => {
104
+ });
105
+ await sleep(400);
106
+ await interactWithCurrentPage(page, isVisible, sleep);
107
+ } catch {
108
+ }
109
+ }
110
+ if (navLinks.length > 0) {
111
+ await page.goto(startUrl, { waitUntil: "networkidle", timeout: 15e3 }).catch(() => {
112
+ });
113
+ await sleep(400);
114
+ }
115
+ await interactWithCurrentPage(page, isVisible, sleep);
116
+ await page.waitForLoadState("networkidle", { timeout: 5e3 }).catch(() => {
117
+ });
118
+ } catch {
119
+ }
120
+ }
121
+ async function interactWithCurrentPage(page, isVisible, sleep) {
122
+ await page.evaluate(new Function(`
123
+ ${isVisible}
124
+ const tabs = [...document.querySelectorAll('[role="tab"]')].filter(isVisible);
125
+ tabs.forEach(tab => { try { tab.click(); } catch {} });
126
+ `)).catch(() => {
127
+ });
128
+ await sleep(400);
129
+ await page.evaluate(new Function(`
130
+ ${isVisible}
131
+ const nextBtns = [...document.querySelectorAll(
132
+ 'button[aria-label*="next" i], button[aria-label*="forward" i], button[aria-label*="right" i], ' +
133
+ '[class*="carousel" i] button, [class*="slider" i] button, [data-direction="next"]'
134
+ )].filter(isVisible);
135
+ // Click next up to 3 times per carousel
136
+ for (let i = 0; i < 3; i++) {
137
+ nextBtns.forEach(btn => { try { btn.click(); } catch {} });
138
+ }
139
+ `)).catch(() => {
140
+ });
141
+ await sleep(400);
142
+ await page.evaluate(new Function(`
143
+ ${isVisible}
144
+ document.querySelectorAll(
145
+ '[data-state="closed"] button, [aria-expanded="false"], ' +
146
+ 'details:not([open]) > summary, [class*="accordion" i] button'
147
+ ).forEach(el => {
148
+ if (isVisible(el)) { try { el.click(); } catch {} }
149
+ });
150
+ `)).catch(() => {
151
+ });
152
+ await sleep(300);
153
+ await page.evaluate(() => {
154
+ window.scrollTo({ top: document.body.scrollHeight, behavior: "instant" });
155
+ }).catch(() => {
156
+ });
157
+ await sleep(300);
158
+ await page.evaluate(() => {
159
+ window.scrollTo({ top: 0, behavior: "instant" });
160
+ }).catch(() => {
161
+ });
162
+ }
163
+ async function validateAndCaptureSession(appUrl, creds, llmConfig, captchaOpts, loginInstructions) {
164
+ const browser = await chromium.launch({ headless: true });
165
+ try {
166
+ const userAgent = await getNonHeadlessUserAgent(chromium.executablePath());
167
+ const contextOpts = { userAgent };
168
+ if (creds.httpBasicUsername && creds.httpBasicPassword) {
169
+ contextOpts.httpCredentials = { username: creds.httpBasicUsername, password: creds.httpBasicPassword };
170
+ }
171
+ const context = await browser.newContext(contextOpts);
172
+ const page = await context.newPage();
173
+ const getOneTimeCode = creds.totpSecret ? async () => generateTOTP(creds.totpSecret) : void 0;
174
+ let loggedIn;
175
+ try {
176
+ loggedIn = await login(page, appUrl, {
177
+ username: creds.username,
178
+ password: creds.password,
179
+ getOneTimeCode
180
+ });
181
+ } catch (loginErr) {
182
+ const shotBuf = await page.screenshot({ type: "jpeg", quality: 60, fullPage: false }).catch(() => null);
183
+ throw Object.assign(loginErr, {
184
+ failureScreenshot: shotBuf ? shotBuf.toString("base64") : null
185
+ });
186
+ }
187
+ if (!loggedIn && llmConfig) {
188
+ let sh = null;
189
+ try {
190
+ const { Stagehand } = await import("@browserbasehq/stagehand");
191
+ const { loginWithStagehand } = await import("./ai-login.js");
192
+ sh = new Stagehand({
193
+ env: "LOCAL",
194
+ model: { modelName: llmConfig.modelName, apiKey: llmConfig.apiKey },
195
+ serverCache: false,
196
+ verbose: 0,
197
+ localBrowserLaunchOptions: { headless: true, args: [`--user-agent=${userAgent}`] }
198
+ });
199
+ await sh.init();
200
+ const shPage = await sh.resolvePage();
201
+ await shPage.goto(appUrl, { waitUntil: "networkidle", timeout: 3e4 }).catch(() => {
202
+ });
203
+ const shLoggedIn = await loginWithStagehand(
204
+ sh,
205
+ { username: creds.username, password: creds.password, getOneTimeCode },
206
+ appUrl,
207
+ captchaOpts,
208
+ void 0,
209
+ loginInstructions
210
+ );
211
+ if (shLoggedIn) {
212
+ const shCtx = sh.context ?? sh.browserContext;
213
+ if (shCtx) {
214
+ const shState = await shCtx.storageState();
215
+ const shCookies = shState.cookies?.length ?? 0;
216
+ const shLocalStorage = shState.origins?.reduce((s, o) => s + (o.localStorage?.length ?? 0), 0) ?? 0;
217
+ if (shCookies > 0 || shLocalStorage > 0) {
218
+ await sh.close().catch(() => {
219
+ });
220
+ sh = null;
221
+ return { storageState: JSON.stringify(shState), cookies: shCookies, localStorage: shLocalStorage };
222
+ }
223
+ }
224
+ loggedIn = true;
225
+ }
226
+ } catch (shErr) {
227
+ const msg = String(shErr?.message ?? shErr);
228
+ if (msg.includes("CAPTCHA") || shErr?.isSSOError) throw shErr;
229
+ } finally {
230
+ if (sh) await sh.close().catch(() => {
231
+ });
232
+ }
233
+ }
234
+ if (!loggedIn) {
235
+ const shotBuf = await page.screenshot({ type: "jpeg", quality: 60, fullPage: false }).catch(() => null);
236
+ const challenge = await page.evaluate(() => {
237
+ const text = (document.body?.innerText ?? "").toLowerCase();
238
+ const hasCloudflare = /verify you are human|checking your browser|cloudflare|cf-challenge|turnstile/.test(text) || !!document.querySelector('iframe[src*="challenges.cloudflare.com"], .cf-turnstile, [data-sitekey]');
239
+ const hasCaptcha = hasCloudflare || /captcha|recaptcha|hcaptcha/.test(text) || !!document.querySelector('iframe[src*="recaptcha"], iframe[src*="hcaptcha"], #captcha, .captcha, [id*="captcha"], [class*="captcha"]');
240
+ return { hasCloudflare, hasCaptcha };
241
+ }).catch(() => ({ hasCloudflare: false, hasCaptcha: false }));
242
+ const message = challenge.hasCloudflare ? "Cloudflare human verification blocked headless validation. This cannot be bypassed reliably in code; use Live Browser or manual session capture, configure a CAPTCHA solver, or allowlist the crawler IP." : challenge.hasCaptcha ? "A CAPTCHA blocked headless validation. Configure CAPTCHA Auto-Solve or use Live Browser/manual session capture." : "Login failed \u2014 credentials rejected or the login flow requires a manual/SSO step. Check credentials, saved login instructions, or use Live Browser session capture.";
243
+ const err = Object.assign(new Error(message), {
244
+ failureScreenshot: shotBuf ? shotBuf.toString("base64") : null,
245
+ reason: challenge.hasCloudflare ? "CLOUDFLARE_CHALLENGE" : challenge.hasCaptcha ? "CAPTCHA_CHALLENGE" : "LOGIN_NOT_CONFIRMED"
246
+ });
247
+ throw err;
248
+ }
249
+ await page.waitForLoadState("networkidle", { timeout: 8e3 }).catch(() => {
250
+ });
251
+ const finalUrl = page.url();
252
+ await exploreAppAfterLogin(page);
253
+ const state = await context.storageState();
254
+ const cookies = state.cookies?.length ?? 0;
255
+ const localStorage = state.origins?.reduce(
256
+ (sum, o) => sum + (o.localStorage?.length ?? 0),
257
+ 0
258
+ ) ?? 0;
259
+ if (cookies === 0 && localStorage === 0) {
260
+ throw new Error("Login appeared to succeed but no session data was captured \u2014 try manual session capture instead");
261
+ }
262
+ return { storageState: JSON.stringify(state), cookies, localStorage, finalUrl };
263
+ } finally {
264
+ await browser.close().catch(() => {
265
+ });
266
+ }
267
+ }
268
+ export {
269
+ cancelSessionCapture,
270
+ completeSessionCapture,
271
+ startSessionCapture,
272
+ validateAndCaptureSession
273
+ };
274
+ //# sourceMappingURL=session-capture.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/session-capture.ts"],
4
+ "sourcesContent": ["import { chromium } from 'playwright';\nimport { randomBytes } from 'node:crypto';\nimport { login } from './auth.js';\nimport { generateTOTP } from './job-queue.js';\nimport { getNonHeadlessUserAgent } from './crawler.js';\n\ninterface CaptureSession {\n browser: any;\n context: any;\n timer: ReturnType<typeof setTimeout>;\n}\n\nconst activeSessions = new Map<string, CaptureSession>();\n\n/**\n * Opens a headed (visible) browser at appUrl and returns a captureId.\n * The user logs in manually, then calls completeSessionCapture() to harvest storageState.\n * Sessions expire after 10 minutes if not completed.\n *\n * Throws 'HEADLESS_ENV' if running in Docker/Linux without a display server.\n * The API route surfaces this as a 503 so the frontend can switch to manual JSON paste.\n */\nexport async function startSessionCapture(appUrl: string): Promise<string> {\n const inDocker = process.env.PLAYWRIGHT_IN_DOCKER === 'true';\n const noDisplay = process.platform === 'linux' && !process.env.DISPLAY;\n if (inDocker || noDisplay) {\n throw new Error('HEADLESS_ENV');\n }\n\n const id = randomBytes(16).toString('hex');\n const browser = await chromium.launch({ headless: false });\n const userAgent = await getNonHeadlessUserAgent(chromium.executablePath());\n const context = await browser.newContext({ userAgent });\n const page = await context.newPage();\n await page.goto(appUrl, { waitUntil: 'domcontentloaded' }).catch(() => {});\n const timer = setTimeout(() => cancelSessionCapture(id).catch(() => {}), 10 * 60 * 1000);\n activeSessions.set(id, { browser, context, timer });\n return id;\n}\n\n/** Captures storageState from the open browser, closes it, and returns the JSON string. */\nexport async function completeSessionCapture(captureId: string): Promise<string> {\n const session = activeSessions.get(captureId);\n if (!session) throw new Error('Capture session not found or expired.');\n clearTimeout(session.timer);\n const state = await session.context.storageState();\n await session.browser.close().catch(() => {});\n activeSessions.delete(captureId);\n return JSON.stringify(state);\n}\n\n/** Closes the browser without capturing \u2014 called on Cancel. */\nexport async function cancelSessionCapture(captureId: string): Promise<void> {\n const session = activeSessions.get(captureId);\n if (!session) return;\n clearTimeout(session.timer);\n await session.browser.close().catch(() => {});\n activeSessions.delete(captureId);\n}\n\n/**\n * Post-login exploration: navigates nav links, cycles tabs, advances carousels,\n * opens accordions, scrolls \u2014 so the browser accumulates full session state before capture.\n * All best-effort: never throws, never fails the capture.\n */\nasync function exploreAppAfterLogin(page: any): Promise<void> {\n const sleep = (ms: number) => page.waitForTimeout(ms).catch(() => {});\n\n const isVisible = `\n function isVisible(el) {\n const s = window.getComputedStyle(el);\n if (s.display === 'none' || s.visibility === 'hidden' || parseFloat(s.opacity) < 0.1) return false;\n const r = el.getBoundingClientRect();\n return r.width > 4 && r.height > 4;\n }\n `;\n\n try {\n const origin = await page.evaluate(() => location.origin).catch(() => '');\n const startUrl = await page.evaluate(() => location.href).catch(() => '');\n\n // \u2500\u2500 1. Expand collapsed nav menus / hamburgers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n await page.evaluate(new Function(`\n ${isVisible}\n const inBadCtx = el => {\n let p = el.parentElement;\n while (p) {\n const role = p.getAttribute('role') ?? '';\n if (['dialog','alertdialog','combobox','listbox'].includes(role)) return true;\n p = p.parentElement;\n }\n return false;\n };\n const targets = new Set();\n document.querySelectorAll('[aria-haspopup]:not([aria-expanded=\"true\"])').forEach(el => {\n if (isVisible(el) && !inBadCtx(el)) targets.add(el);\n });\n document.querySelectorAll('[aria-expanded=\"false\"]').forEach(el => {\n if ((el.tagName === 'BUTTON' || el.getAttribute('role') === 'button') && isVisible(el) && !inBadCtx(el)) targets.add(el);\n });\n document.querySelectorAll('[data-state=\"closed\"],[data-headlessui-state=\"closed\"]').forEach(el => {\n if ((el.tagName === 'BUTTON' || el.getAttribute('role') === 'button') && isVisible(el) && !inBadCtx(el)) targets.add(el);\n });\n targets.forEach(el => { try { el.click(); } catch {} });\n `) as () => void).catch(() => {});\n await sleep(600);\n\n // \u2500\u2500 2. Collect nav links from the expanded DOM \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n const navLinks: string[] = await page.evaluate(new Function(`\n ${isVisible}\n const links = new Set();\n const navEls = document.querySelectorAll('nav a, [role=\"navigation\"] a, aside a, header a');\n navEls.forEach(a => {\n const href = a.getAttribute('href');\n if (!href || href.startsWith('#') || href.startsWith('mailto:') || href.startsWith('tel:')) return;\n if (!isVisible(a)) return;\n try {\n const url = new URL(href, location.href);\n if (url.origin === location.origin) links.add(url.href);\n } catch {}\n });\n return [...links].slice(0, 10); // cap at 10 nav pages\n `) as () => string[]).catch(() => []);\n\n // Dismiss opened menus before navigating\n await page.keyboard?.press('Escape').catch(() => {});\n await sleep(200);\n\n // \u2500\u2500 3. Visit each nav link, interact, return \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n for (const link of navLinks) {\n try {\n if (link === startUrl) continue;\n await page.goto(link, { waitUntil: 'networkidle', timeout: 15_000 }).catch(() => {});\n await sleep(400);\n await interactWithCurrentPage(page, isVisible, sleep);\n } catch { /* skip broken page */ }\n }\n\n // Return to start page\n if (navLinks.length > 0) {\n await page.goto(startUrl, { waitUntil: 'networkidle', timeout: 15_000 }).catch(() => {});\n await sleep(400);\n }\n\n // \u2500\u2500 4. Final interaction pass on landing page \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n await interactWithCurrentPage(page, isVisible, sleep);\n\n await page.waitForLoadState('networkidle', { timeout: 5_000 }).catch(() => {});\n } catch {\n // Exploration is best-effort\n }\n}\n\n/** Interacts with tabs, carousels, accordions, and scrolls on the current page. */\nasync function interactWithCurrentPage(page: any, isVisible: string, sleep: (ms: number) => Promise<void>): Promise<void> {\n // Cycle through tabs\n await page.evaluate(new Function(`\n ${isVisible}\n const tabs = [...document.querySelectorAll('[role=\"tab\"]')].filter(isVisible);\n tabs.forEach(tab => { try { tab.click(); } catch {} });\n `) as () => void).catch(() => {});\n await sleep(400);\n\n // Advance carousels (next buttons)\n await page.evaluate(new Function(`\n ${isVisible}\n const nextBtns = [...document.querySelectorAll(\n 'button[aria-label*=\"next\" i], button[aria-label*=\"forward\" i], button[aria-label*=\"right\" i], ' +\n '[class*=\"carousel\" i] button, [class*=\"slider\" i] button, [data-direction=\"next\"]'\n )].filter(isVisible);\n // Click next up to 3 times per carousel\n for (let i = 0; i < 3; i++) {\n nextBtns.forEach(btn => { try { btn.click(); } catch {} });\n }\n `) as () => void).catch(() => {});\n await sleep(400);\n\n // Open closed accordions / disclosure panels\n await page.evaluate(new Function(`\n ${isVisible}\n document.querySelectorAll(\n '[data-state=\"closed\"] button, [aria-expanded=\"false\"], ' +\n 'details:not([open]) > summary, [class*=\"accordion\" i] button'\n ).forEach(el => {\n if (isVisible(el)) { try { el.click(); } catch {} }\n });\n `) as () => void).catch(() => {});\n await sleep(300);\n\n // Scroll to bottom then back to top\n await page.evaluate(() => { window.scrollTo({ top: document.body.scrollHeight, behavior: 'instant' }); }).catch(() => {});\n await sleep(300);\n await page.evaluate(() => { window.scrollTo({ top: 0, behavior: 'instant' }); }).catch(() => {});\n}\n\n/**\n * Headless login + session capture \u2014 works on Docker/cloud (no display needed).\n * Tries DOM-based login first (fast, free). If it fails and llmConfig is provided,\n * falls back to Stagehand AI-guided login (handles modals, multi-step, marketing pages).\n *\n * Returns { storageState, cookies, localStorage } on success.\n * Throws a human-readable error on failure.\n */\nexport async function validateAndCaptureSession(\n appUrl: string,\n creds: {\n username: string; password: string; totpSecret?: string;\n httpBasicUsername?: string; httpBasicPassword?: string;\n },\n llmConfig?: { modelName: string; apiKey: string },\n captchaOpts?: { apiKey?: string; provider?: '2captcha' | 'capsolver' },\n loginInstructions?: string,\n): Promise<{ storageState: string; cookies: number; localStorage: number; finalUrl?: string }> {\n const browser = await chromium.launch({ headless: true });\n try {\n const userAgent = await getNonHeadlessUserAgent(chromium.executablePath());\n const contextOpts: any = { userAgent };\n if (creds.httpBasicUsername && creds.httpBasicPassword) {\n contextOpts.httpCredentials = { username: creds.httpBasicUsername, password: creds.httpBasicPassword };\n }\n const context = await browser.newContext(contextOpts);\n const page = await context.newPage();\n\n const getOneTimeCode = creds.totpSecret\n ? async () => generateTOTP(creds.totpSecret!)\n : undefined;\n\n let loggedIn: boolean;\n try {\n loggedIn = await login(page, appUrl, {\n username: creds.username,\n password: creds.password,\n getOneTimeCode,\n });\n } catch (loginErr: any) {\n // login() throws when it can read a real error message from the page\n const shotBuf = await page.screenshot({ type: 'jpeg', quality: 60, fullPage: false }).catch(() => null);\n throw Object.assign(loginErr, {\n failureScreenshot: shotBuf ? shotBuf.toString('base64') : null,\n });\n }\n\n // Stagehand AI fallback when DOM login fails and LLM config is available\n if (!loggedIn && llmConfig) {\n let sh: any = null;\n try {\n const { Stagehand } = await import('@browserbasehq/stagehand');\n const { loginWithStagehand } = await import('./ai-login.js');\n sh = new Stagehand({\n env: 'LOCAL',\n model: { modelName: llmConfig.modelName as any, apiKey: llmConfig.apiKey },\n serverCache: false,\n verbose: 0,\n localBrowserLaunchOptions: { headless: true, args: [`--user-agent=${userAgent}`] },\n });\n await sh.init();\n const shPage = await (sh as any).resolvePage();\n await shPage.goto(appUrl, { waitUntil: 'networkidle', timeout: 30_000 }).catch(() => {});\n const shLoggedIn = await loginWithStagehand(\n sh,\n { username: creds.username, password: creds.password, getOneTimeCode },\n appUrl,\n captchaOpts,\n undefined,\n loginInstructions,\n );\n if (shLoggedIn) {\n const shCtx = (sh as any).context ?? (sh as any).browserContext;\n if (shCtx) {\n const shState = await shCtx.storageState();\n const shCookies = shState.cookies?.length ?? 0;\n const shLocalStorage = shState.origins?.reduce((s: number, o: any) => s + (o.localStorage?.length ?? 0), 0) ?? 0;\n if (shCookies > 0 || shLocalStorage > 0) {\n await sh.close().catch(() => {});\n sh = null;\n return { storageState: JSON.stringify(shState), cookies: shCookies, localStorage: shLocalStorage };\n }\n }\n loggedIn = true; // Stagehand succeeded but we'll capture from the plain browser context below\n }\n } catch (shErr: any) {\n const msg = String(shErr?.message ?? shErr);\n if (msg.includes('CAPTCHA') || shErr?.isSSOError) throw shErr;\n // Stagehand failed silently \u2014 fall through to \"credentials rejected\"\n } finally {\n if (sh) await sh.close().catch(() => {});\n }\n }\n\n if (!loggedIn) {\n const shotBuf = await page.screenshot({ type: 'jpeg', quality: 60, fullPage: false }).catch(() => null);\n const challenge = await page.evaluate(() => {\n const text = (document.body?.innerText ?? '').toLowerCase();\n const hasCloudflare = /verify you are human|checking your browser|cloudflare|cf-challenge|turnstile/.test(text) ||\n !!document.querySelector('iframe[src*=\"challenges.cloudflare.com\"], .cf-turnstile, [data-sitekey]');\n const hasCaptcha = hasCloudflare || /captcha|recaptcha|hcaptcha/.test(text) ||\n !!document.querySelector('iframe[src*=\"recaptcha\"], iframe[src*=\"hcaptcha\"], #captcha, .captcha, [id*=\"captcha\"], [class*=\"captcha\"]');\n return { hasCloudflare, hasCaptcha };\n }).catch(() => ({ hasCloudflare: false, hasCaptcha: false }));\n const message = challenge.hasCloudflare\n ? 'Cloudflare human verification blocked headless validation. This cannot be bypassed reliably in code; use Live Browser or manual session capture, configure a CAPTCHA solver, or allowlist the crawler IP.'\n : challenge.hasCaptcha\n ? 'A CAPTCHA blocked headless validation. Configure CAPTCHA Auto-Solve or use Live Browser/manual session capture.'\n : 'Login failed \u2014 credentials rejected or the login flow requires a manual/SSO step. Check credentials, saved login instructions, or use Live Browser session capture.';\n const err = Object.assign(new Error(message), {\n failureScreenshot: shotBuf ? shotBuf.toString('base64') : null,\n reason: challenge.hasCloudflare ? 'CLOUDFLARE_CHALLENGE' : challenge.hasCaptcha ? 'CAPTCHA_CHALLENGE' : 'LOGIN_NOT_CONFIRMED',\n });\n throw err;\n }\n\n // Wait for post-login navigation to settle before capturing session\n await page.waitForLoadState('networkidle', { timeout: 8_000 }).catch(() => {});\n\n // Capture post-login landing URL before exploration navigates away\n const finalUrl = page.url();\n\n // Post-login exploration: navigate tabs, expand menus, scroll to trigger lazy-loaded state\n await exploreAppAfterLogin(page);\n\n const state = await context.storageState();\n const cookies = state.cookies?.length ?? 0;\n const localStorage = state.origins?.reduce(\n (sum, o) => sum + (o.localStorage?.length ?? 0), 0\n ) ?? 0;\n\n if (cookies === 0 && localStorage === 0) {\n throw new Error('Login appeared to succeed but no session data was captured \u2014 try manual session capture instead');\n }\n\n return { storageState: JSON.stringify(state), cookies, localStorage, finalUrl };\n } finally {\n await browser.close().catch(() => {});\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,49 @@
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
+ export interface StorageStateCookie {
12
+ name: string;
13
+ value: string;
14
+ domain: string;
15
+ path: string;
16
+ expires?: number;
17
+ httpOnly?: boolean;
18
+ secure?: boolean;
19
+ sameSite?: 'Strict' | 'Lax' | 'None';
20
+ }
21
+ export interface StorageStateOrigin {
22
+ origin: string;
23
+ localStorage: Array<{
24
+ name: string;
25
+ value: string;
26
+ }>;
27
+ }
28
+ export interface StorageState {
29
+ cookies: StorageStateCookie[];
30
+ origins: StorageStateOrigin[];
31
+ }
32
+ export type SessionExportFormat = 'cypress' | 'selenium-python' | 'webdriverio';
33
+ /** cy.setCookie() + localStorage.setItem() inside a reusable custom command,
34
+ * following Cypress's documented cy.session() pattern (see references in
35
+ * docs/UNIVERSAL-SESSION-CAPTURE-STRATEGY.md). */
36
+ export declare function toCypressSnippet(state: StorageState): string;
37
+ /** driver.add_cookie() (Selenium requires the browser already be on the
38
+ * cookie's domain before adding it) + execute_script() for localStorage,
39
+ * per Selenium's documented Cookie API. */
40
+ export declare function toSeleniumPythonDict(state: StorageState): string;
41
+ /** browser.setCookies() accepts an array shape very close to Playwright's own
42
+ * cookie objects (per WebdriverIO's documented API) — closest 1:1 mapping
43
+ * of the three target formats. */
44
+ export declare function toWebdriverIOSnippet(state: StorageState): string;
45
+ export declare function exportSessionAs(format: SessionExportFormat, state: StorageState): {
46
+ content: string;
47
+ filename: string;
48
+ mimeType: string;
49
+ };
@@ -0,0 +1,117 @@
1
+ function jsStringLiteral(s) {
2
+ return JSON.stringify(s);
3
+ }
4
+ function pythonDictLiteral(obj) {
5
+ const parts = Object.entries(obj).map(([k, v]) => {
6
+ let repr;
7
+ if (typeof v === "boolean") repr = v ? "True" : "False";
8
+ else if (v === null || v === void 0) repr = "None";
9
+ else if (typeof v === "number") repr = String(v);
10
+ else repr = JSON.stringify(v);
11
+ return `${JSON.stringify(k)}: ${repr}`;
12
+ });
13
+ return `{${parts.join(", ")}}`;
14
+ }
15
+ function toCypressSnippet(state) {
16
+ const cookieLines = state.cookies.map((c) => {
17
+ const opts = [`domain: ${jsStringLiteral(c.domain)}`, `path: ${jsStringLiteral(c.path)}`];
18
+ if (c.secure) opts.push("secure: true");
19
+ if (c.httpOnly) opts.push("httpOnly: true");
20
+ if (c.sameSite) opts.push(`sameSite: ${jsStringLiteral(c.sameSite.toLowerCase())}`);
21
+ return ` cy.setCookie(${jsStringLiteral(c.name)}, ${jsStringLiteral(c.value)}, { ${opts.join(", ")} });`;
22
+ });
23
+ const localStorageBlocks = state.origins.filter((o) => o.localStorage.length > 0).map((o) => {
24
+ const sets = o.localStorage.map((kv) => ` window.localStorage.setItem(${jsStringLiteral(kv.name)}, ${jsStringLiteral(kv.value)});`).join("\n");
25
+ return ` cy.visit(${jsStringLiteral(o.origin)});
26
+ cy.window().then((window) => {
27
+ ${sets}
28
+ });`;
29
+ });
30
+ return [
31
+ "// Generated by ZeTa-AI \u2014 restores a captured browser session in Cypress.",
32
+ "// Usage: import this file and call restoreZetaSession() inside cy.session(), e.g.:",
33
+ "// cy.session('zeta-session', restoreZetaSession)",
34
+ "Cypress.Commands.add('restoreZetaSession', () => {",
35
+ ...cookieLines,
36
+ ...localStorageBlocks,
37
+ "});"
38
+ ].join("\n");
39
+ }
40
+ function toSeleniumPythonDict(state) {
41
+ const byDomain = /* @__PURE__ */ new Map();
42
+ for (const c of state.cookies) {
43
+ const list = byDomain.get(c.domain) ?? [];
44
+ list.push(c);
45
+ byDomain.set(c.domain, list);
46
+ }
47
+ const lines = [
48
+ "# Generated by ZeTa-AI \u2014 restores a captured browser session in Selenium.",
49
+ "# Selenium requires the driver to already be on a matching domain before",
50
+ "# add_cookie() will accept a cookie for it \u2014 navigate first, then restore.",
51
+ "def restore_zeta_session(driver):"
52
+ ];
53
+ for (const [domain, cookies] of byDomain) {
54
+ lines.push(` driver.get(${JSON.stringify(`https://${domain.replace(/^\./, "")}`)})`);
55
+ for (const c of cookies) {
56
+ const cookieDict = { name: c.name, value: c.value, path: c.path };
57
+ if (c.expires && c.expires > 0) cookieDict.expiry = Math.floor(c.expires);
58
+ if (c.secure) cookieDict.secure = true;
59
+ lines.push(` driver.add_cookie(${pythonDictLiteral(cookieDict)})`);
60
+ }
61
+ }
62
+ for (const o of state.origins) {
63
+ if (o.localStorage.length === 0) continue;
64
+ lines.push(` driver.get(${JSON.stringify(o.origin)})`);
65
+ for (const kv of o.localStorage) {
66
+ lines.push(
67
+ ` driver.execute_script(${JSON.stringify(`window.localStorage.setItem(arguments[0], arguments[1]);`)}, ${JSON.stringify(kv.name)}, ${JSON.stringify(kv.value)})`
68
+ );
69
+ }
70
+ }
71
+ return lines.join("\n");
72
+ }
73
+ function toWebdriverIOSnippet(state) {
74
+ const cookies = state.cookies.map((c) => ({
75
+ name: c.name,
76
+ value: c.value,
77
+ domain: c.domain,
78
+ path: c.path,
79
+ secure: !!c.secure,
80
+ httpOnly: !!c.httpOnly,
81
+ ...c.expires && c.expires > 0 ? { expiry: Math.floor(c.expires) } : {}
82
+ }));
83
+ const localStorageSets = state.origins.filter((o) => o.localStorage.length > 0).map((o) => {
84
+ const sets = o.localStorage.map((kv) => ` localStorage.setItem(${jsStringLiteral(kv.name)}, ${jsStringLiteral(kv.value)});`).join("\n");
85
+ return ` await browser.url(${jsStringLiteral(o.origin)});
86
+ await browser.execute(() => {
87
+ ${sets}
88
+ });`;
89
+ });
90
+ return [
91
+ "// Generated by ZeTa-AI \u2014 restores a captured browser session in WebdriverIO.",
92
+ "async function restoreZetaSession(browser) {",
93
+ ` await browser.setCookies(${JSON.stringify(cookies, null, 2).split("\n").join("\n ")});`,
94
+ ...localStorageSets,
95
+ "}",
96
+ "module.exports = { restoreZetaSession };"
97
+ ].join("\n");
98
+ }
99
+ function exportSessionAs(format, state) {
100
+ switch (format) {
101
+ case "cypress":
102
+ return { content: toCypressSnippet(state), filename: "zeta-session.cy-commands.js", mimeType: "application/javascript" };
103
+ case "selenium-python":
104
+ return { content: toSeleniumPythonDict(state), filename: "zeta_session.py", mimeType: "text/x-python" };
105
+ case "webdriverio":
106
+ return { content: toWebdriverIOSnippet(state), filename: "zeta-session.wdio.js", mimeType: "application/javascript" };
107
+ default:
108
+ throw new Error(`Unknown session export format: ${format}`);
109
+ }
110
+ }
111
+ export {
112
+ exportSessionAs,
113
+ toCypressSnippet,
114
+ toSeleniumPythonDict,
115
+ toWebdriverIOSnippet
116
+ };
117
+ //# sourceMappingURL=session-export.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/session-export.ts"],
4
+ "sourcesContent": ["/**\n * Converts a Playwright storageState (the format ZeTa already captures via\n * session-capture.ts/live-view.ts and stores per-project) into snippets for\n * other automation frameworks. Playwright's storageState is the de-facto\n * standard for browser session export \u2014 see docs/UNIVERSAL-SESSION-CAPTURE-\n * STRATEGY.md \u2014 so every other framework's import mechanism is a small,\n * mechanical conversion away, not a new capture flow.\n *\n * Pure, dependency-free functions \u2014 no I/O, no network, fully unit-testable.\n */\n\nexport interface StorageStateCookie {\n name: string;\n value: string;\n domain: string;\n path: string;\n expires?: number;\n httpOnly?: boolean;\n secure?: boolean;\n sameSite?: 'Strict' | 'Lax' | 'None';\n}\n\nexport interface StorageStateOrigin {\n origin: string;\n localStorage: Array<{ name: string; value: string }>;\n}\n\nexport interface StorageState {\n cookies: StorageStateCookie[];\n origins: StorageStateOrigin[];\n}\n\nexport type SessionExportFormat = 'cypress' | 'selenium-python' | 'webdriverio';\n\nfunction jsStringLiteral(s: string): string {\n return JSON.stringify(s);\n}\n\n/** Python dict-literal repr \u2014 JSON.stringify's `true`/`false`/`null` aren't\n * valid Python (Python needs `True`/`False`/`None`), so a plain\n * JSON.stringify would emit syntactically invalid Python for any cookie\n * with a boolean field. */\nfunction pythonDictLiteral(obj: Record<string, unknown>): string {\n const parts = Object.entries(obj).map(([k, v]) => {\n let repr: string;\n if (typeof v === 'boolean') repr = v ? 'True' : 'False';\n else if (v === null || v === undefined) repr = 'None';\n else if (typeof v === 'number') repr = String(v);\n else repr = JSON.stringify(v);\n return `${JSON.stringify(k)}: ${repr}`;\n });\n return `{${parts.join(', ')}}`;\n}\n\n/** cy.setCookie() + localStorage.setItem() inside a reusable custom command,\n * following Cypress's documented cy.session() pattern (see references in\n * docs/UNIVERSAL-SESSION-CAPTURE-STRATEGY.md). */\nexport function toCypressSnippet(state: StorageState): string {\n const cookieLines = state.cookies.map((c) => {\n const opts: string[] = [`domain: ${jsStringLiteral(c.domain)}`, `path: ${jsStringLiteral(c.path)}`];\n if (c.secure) opts.push('secure: true');\n if (c.httpOnly) opts.push('httpOnly: true');\n if (c.sameSite) opts.push(`sameSite: ${jsStringLiteral(c.sameSite.toLowerCase())}`);\n return ` cy.setCookie(${jsStringLiteral(c.name)}, ${jsStringLiteral(c.value)}, { ${opts.join(', ')} });`;\n });\n\n const localStorageBlocks = state.origins\n .filter((o) => o.localStorage.length > 0)\n .map((o) => {\n const sets = o.localStorage\n .map((kv) => ` window.localStorage.setItem(${jsStringLiteral(kv.name)}, ${jsStringLiteral(kv.value)});`)\n .join('\\n');\n return ` cy.visit(${jsStringLiteral(o.origin)});\\n cy.window().then((window) => {\\n${sets}\\n });`;\n });\n\n return [\n '// Generated by ZeTa-AI \u2014 restores a captured browser session in Cypress.',\n '// Usage: import this file and call restoreZetaSession() inside cy.session(), e.g.:',\n \"// cy.session('zeta-session', restoreZetaSession)\",\n \"Cypress.Commands.add('restoreZetaSession', () => {\",\n ...cookieLines,\n ...localStorageBlocks,\n '});',\n ].join('\\n');\n}\n\n/** driver.add_cookie() (Selenium requires the browser already be on the\n * cookie's domain before adding it) + execute_script() for localStorage,\n * per Selenium's documented Cookie API. */\nexport function toSeleniumPythonDict(state: StorageState): string {\n const byDomain = new Map<string, StorageStateCookie[]>();\n for (const c of state.cookies) {\n const list = byDomain.get(c.domain) ?? [];\n list.push(c);\n byDomain.set(c.domain, list);\n }\n\n const lines: string[] = [\n '# Generated by ZeTa-AI \u2014 restores a captured browser session in Selenium.',\n '# Selenium requires the driver to already be on a matching domain before',\n '# add_cookie() will accept a cookie for it \u2014 navigate first, then restore.',\n 'def restore_zeta_session(driver):',\n ];\n\n for (const [domain, cookies] of byDomain) {\n lines.push(` driver.get(${JSON.stringify(`https://${domain.replace(/^\\./, '')}`)})`);\n for (const c of cookies) {\n const cookieDict: Record<string, unknown> = { name: c.name, value: c.value, path: c.path };\n if (c.expires && c.expires > 0) cookieDict.expiry = Math.floor(c.expires);\n if (c.secure) cookieDict.secure = true;\n lines.push(` driver.add_cookie(${pythonDictLiteral(cookieDict)})`);\n }\n }\n\n for (const o of state.origins) {\n if (o.localStorage.length === 0) continue;\n lines.push(` driver.get(${JSON.stringify(o.origin)})`);\n for (const kv of o.localStorage) {\n lines.push(\n ` driver.execute_script(${JSON.stringify(`window.localStorage.setItem(arguments[0], arguments[1]);`)}, ${JSON.stringify(kv.name)}, ${JSON.stringify(kv.value)})`,\n );\n }\n }\n\n return lines.join('\\n');\n}\n\n/** browser.setCookies() accepts an array shape very close to Playwright's own\n * cookie objects (per WebdriverIO's documented API) \u2014 closest 1:1 mapping\n * of the three target formats. */\nexport function toWebdriverIOSnippet(state: StorageState): string {\n const cookies = state.cookies.map((c) => ({\n name: c.name,\n value: c.value,\n domain: c.domain,\n path: c.path,\n secure: !!c.secure,\n httpOnly: !!c.httpOnly,\n ...(c.expires && c.expires > 0 ? { expiry: Math.floor(c.expires) } : {}),\n }));\n\n const localStorageSets = state.origins\n .filter((o) => o.localStorage.length > 0)\n .map((o) => {\n const sets = o.localStorage\n .map((kv) => ` localStorage.setItem(${jsStringLiteral(kv.name)}, ${jsStringLiteral(kv.value)});`)\n .join('\\n');\n return ` await browser.url(${jsStringLiteral(o.origin)});\\n await browser.execute(() => {\\n${sets}\\n });`;\n });\n\n return [\n '// Generated by ZeTa-AI \u2014 restores a captured browser session in WebdriverIO.',\n 'async function restoreZetaSession(browser) {',\n ` await browser.setCookies(${JSON.stringify(cookies, null, 2).split('\\n').join('\\n ')});`,\n ...localStorageSets,\n '}',\n 'module.exports = { restoreZetaSession };',\n ].join('\\n');\n}\n\nexport function exportSessionAs(format: SessionExportFormat, state: StorageState): { content: string; filename: string; mimeType: string } {\n switch (format) {\n case 'cypress':\n return { content: toCypressSnippet(state), filename: 'zeta-session.cy-commands.js', mimeType: 'application/javascript' };\n case 'selenium-python':\n return { content: toSeleniumPythonDict(state), filename: 'zeta_session.py', mimeType: 'text/x-python' };\n case 'webdriverio':\n return { content: toWebdriverIOSnippet(state), filename: 'zeta-session.wdio.js', mimeType: 'application/javascript' };\n default:\n throw new Error(`Unknown session export format: ${format}`);\n }\n}\n"],
5
+ "mappings": "AAkCA,SAAS,gBAAgB,GAAmB;AAC1C,SAAO,KAAK,UAAU,CAAC;AACzB;AAMA,SAAS,kBAAkB,KAAsC;AAC/D,QAAM,QAAQ,OAAO,QAAQ,GAAG,EAAE,IAAI,CAAC,CAAC,GAAG,CAAC,MAAM;AAChD,QAAI;AACJ,QAAI,OAAO,MAAM,UAAW,QAAO,IAAI,SAAS;AAAA,aACvC,MAAM,QAAQ,MAAM,OAAW,QAAO;AAAA,aACtC,OAAO,MAAM,SAAU,QAAO,OAAO,CAAC;AAAA,QAC1C,QAAO,KAAK,UAAU,CAAC;AAC5B,WAAO,GAAG,KAAK,UAAU,CAAC,CAAC,KAAK,IAAI;AAAA,EACtC,CAAC;AACD,SAAO,IAAI,MAAM,KAAK,IAAI,CAAC;AAC7B;AAKO,SAAS,iBAAiB,OAA6B;AAC5D,QAAM,cAAc,MAAM,QAAQ,IAAI,CAAC,MAAM;AAC3C,UAAM,OAAiB,CAAC,WAAW,gBAAgB,EAAE,MAAM,CAAC,IAAI,SAAS,gBAAgB,EAAE,IAAI,CAAC,EAAE;AAClG,QAAI,EAAE,OAAQ,MAAK,KAAK,cAAc;AACtC,QAAI,EAAE,SAAU,MAAK,KAAK,gBAAgB;AAC1C,QAAI,EAAE,SAAU,MAAK,KAAK,aAAa,gBAAgB,EAAE,SAAS,YAAY,CAAC,CAAC,EAAE;AAClF,WAAO,kBAAkB,gBAAgB,EAAE,IAAI,CAAC,KAAK,gBAAgB,EAAE,KAAK,CAAC,OAAO,KAAK,KAAK,IAAI,CAAC;AAAA,EACrG,CAAC;AAED,QAAM,qBAAqB,MAAM,QAC9B,OAAO,CAAC,MAAM,EAAE,aAAa,SAAS,CAAC,EACvC,IAAI,CAAC,MAAM;AACV,UAAM,OAAO,EAAE,aACZ,IAAI,CAAC,OAAO,qCAAqC,gBAAgB,GAAG,IAAI,CAAC,KAAK,gBAAgB,GAAG,KAAK,CAAC,IAAI,EAC3G,KAAK,IAAI;AACZ,WAAO,cAAc,gBAAgB,EAAE,MAAM,CAAC;AAAA;AAAA,EAAyC,IAAI;AAAA;AAAA,EAC7F,CAAC;AAEH,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAG;AAAA,IACH,GAAG;AAAA,IACH;AAAA,EACF,EAAE,KAAK,IAAI;AACb;AAKO,SAAS,qBAAqB,OAA6B;AAChE,QAAM,WAAW,oBAAI,IAAkC;AACvD,aAAW,KAAK,MAAM,SAAS;AAC7B,UAAM,OAAO,SAAS,IAAI,EAAE,MAAM,KAAK,CAAC;AACxC,SAAK,KAAK,CAAC;AACX,aAAS,IAAI,EAAE,QAAQ,IAAI;AAAA,EAC7B;AAEA,QAAM,QAAkB;AAAA,IACtB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAEA,aAAW,CAAC,QAAQ,OAAO,KAAK,UAAU;AACxC,UAAM,KAAK,kBAAkB,KAAK,UAAU,WAAW,OAAO,QAAQ,OAAO,EAAE,CAAC,EAAE,CAAC,GAAG;AACtF,eAAW,KAAK,SAAS;AACvB,YAAM,aAAsC,EAAE,MAAM,EAAE,MAAM,OAAO,EAAE,OAAO,MAAM,EAAE,KAAK;AACzF,UAAI,EAAE,WAAW,EAAE,UAAU,EAAG,YAAW,SAAS,KAAK,MAAM,EAAE,OAAO;AACxE,UAAI,EAAE,OAAQ,YAAW,SAAS;AAClC,YAAM,KAAK,yBAAyB,kBAAkB,UAAU,CAAC,GAAG;AAAA,IACtE;AAAA,EACF;AAEA,aAAW,KAAK,MAAM,SAAS;AAC7B,QAAI,EAAE,aAAa,WAAW,EAAG;AACjC,UAAM,KAAK,kBAAkB,KAAK,UAAU,EAAE,MAAM,CAAC,GAAG;AACxD,eAAW,MAAM,EAAE,cAAc;AAC/B,YAAM;AAAA,QACJ,6BAA6B,KAAK,UAAU,0DAA0D,CAAC,KAAK,KAAK,UAAU,GAAG,IAAI,CAAC,KAAK,KAAK,UAAU,GAAG,KAAK,CAAC;AAAA,MAClK;AAAA,IACF;AAAA,EACF;AAEA,SAAO,MAAM,KAAK,IAAI;AACxB;AAKO,SAAS,qBAAqB,OAA6B;AAChE,QAAM,UAAU,MAAM,QAAQ,IAAI,CAAC,OAAO;AAAA,IACxC,MAAM,EAAE;AAAA,IACR,OAAO,EAAE;AAAA,IACT,QAAQ,EAAE;AAAA,IACV,MAAM,EAAE;AAAA,IACR,QAAQ,CAAC,CAAC,EAAE;AAAA,IACZ,UAAU,CAAC,CAAC,EAAE;AAAA,IACd,GAAI,EAAE,WAAW,EAAE,UAAU,IAAI,EAAE,QAAQ,KAAK,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EACxE,EAAE;AAEF,QAAM,mBAAmB,MAAM,QAC5B,OAAO,CAAC,MAAM,EAAE,aAAa,SAAS,CAAC,EACvC,IAAI,CAAC,MAAM;AACV,UAAM,OAAO,EAAE,aACZ,IAAI,CAAC,OAAO,4BAA4B,gBAAgB,GAAG,IAAI,CAAC,KAAK,gBAAgB,GAAG,KAAK,CAAC,IAAI,EAClG,KAAK,IAAI;AACZ,WAAO,uBAAuB,gBAAgB,EAAE,MAAM,CAAC;AAAA;AAAA,EAAwC,IAAI;AAAA;AAAA,EACrG,CAAC;AAEH,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,8BAA8B,KAAK,UAAU,SAAS,MAAM,CAAC,EAAE,MAAM,IAAI,EAAE,KAAK,MAAM,CAAC;AAAA,IACvF,GAAG;AAAA,IACH;AAAA,IACA;AAAA,EACF,EAAE,KAAK,IAAI;AACb;AAEO,SAAS,gBAAgB,QAA6B,OAA8E;AACzI,UAAQ,QAAQ;AAAA,IACd,KAAK;AACH,aAAO,EAAE,SAAS,iBAAiB,KAAK,GAAG,UAAU,+BAA+B,UAAU,yBAAyB;AAAA,IACzH,KAAK;AACH,aAAO,EAAE,SAAS,qBAAqB,KAAK,GAAG,UAAU,mBAAmB,UAAU,gBAAgB;AAAA,IACxG,KAAK;AACH,aAAO,EAAE,SAAS,qBAAqB,KAAK,GAAG,UAAU,wBAAwB,UAAU,yBAAyB;AAAA,IACtH;AACE,YAAM,IAAI,MAAM,kCAAkC,MAAM,EAAE;AAAA,EAC9D;AACF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,35 @@
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
+ export interface ShadowInteractiveEl {
10
+ role: string;
11
+ label: string;
12
+ type: string;
13
+ tagName: string;
14
+ }
15
+ export interface ShadowHostInfo {
16
+ tagName: string;
17
+ hostId: string;
18
+ shadowMode: 'open' | 'closed';
19
+ depth: number;
20
+ interactiveCount: number;
21
+ interactiveElements: ShadowInteractiveEl[];
22
+ slotNames: string[];
23
+ childShadowHosts: number;
24
+ }
25
+ export interface ShadowDomCrawlResult {
26
+ shadowHostCount: number;
27
+ openShadowCount: number;
28
+ closedShadowCount: number;
29
+ maxNestingDepth: number;
30
+ hosts: ShadowHostInfo[];
31
+ totalInteractiveInShadow: number;
32
+ detectedLibraries: string[];
33
+ blindSpots: string[];
34
+ }
35
+ export declare function crawlShadowDom(page: any): Promise<ShadowDomCrawlResult>;