@taodigital/crawler 0.0.0-stage → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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,65 @@
1
+ async function trackRedirects(page, url) {
2
+ const hops = [];
3
+ const seen = /* @__PURE__ */ new Set();
4
+ let lastResponseTime = Date.now();
5
+ const onResponse = (response) => {
6
+ const status = response.status();
7
+ if (status >= 300 && status < 400) {
8
+ const from = response.url();
9
+ const to = response.headers()["location"] ?? "";
10
+ const now = Date.now();
11
+ hops.push({ from, to, statusCode: status, durationMs: now - lastResponseTime });
12
+ lastResponseTime = now;
13
+ }
14
+ };
15
+ page.on("response", onResponse);
16
+ try {
17
+ await page.goto(url, { waitUntil: "domcontentloaded", timeout: 3e4 }).catch(() => {
18
+ });
19
+ } finally {
20
+ page.off("response", onResponse);
21
+ }
22
+ const finalUrl = page.url();
23
+ const isCircular = hops.some((h) => {
24
+ if (seen.has(h.to)) return true;
25
+ seen.add(h.from);
26
+ return false;
27
+ });
28
+ return {
29
+ originalUrl: url,
30
+ finalUrl,
31
+ hops,
32
+ isCircular,
33
+ hopCount: hops.length,
34
+ totalDurationMs: hops.reduce((sum, h) => sum + h.durationMs, 0)
35
+ };
36
+ }
37
+ class RedirectRegistry {
38
+ chains = /* @__PURE__ */ new Map();
39
+ register(chain) {
40
+ this.chains.set(chain.originalUrl, chain);
41
+ }
42
+ get(url) {
43
+ return this.chains.get(url);
44
+ }
45
+ getAll() {
46
+ return [...this.chains.values()];
47
+ }
48
+ getProblematic() {
49
+ return [...this.chains.values()].filter((c) => c.isCircular || c.hopCount > 3);
50
+ }
51
+ stats() {
52
+ const all = [...this.chains.values()];
53
+ return {
54
+ total: all.length,
55
+ withRedirects: all.filter((c) => c.hopCount > 0).length,
56
+ circular: all.filter((c) => c.isCircular).length,
57
+ deepChains: all.filter((c) => c.hopCount > 3).length
58
+ };
59
+ }
60
+ }
61
+ export {
62
+ RedirectRegistry,
63
+ trackRedirects
64
+ };
65
+ //# sourceMappingURL=redirect-tracker.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/redirect-tracker.ts"],
4
+ "sourcesContent": ["/**\n * Redirect chain tracker \u2014 records the full redirect chain for crawled URLs.\n *\n * Why: redirect chains (A\u2192B\u2192C) slow down pages; circular redirects crash crawlers;\n * redirect loops indicate config errors. ZeTa records chains for SEO/perf audit.\n *\n * Integration: hook into Playwright's page.on('response') to see each redirect hop.\n */\n\nexport interface RedirectHop {\n from: string;\n to: string;\n statusCode: number;\n durationMs: number;\n}\n\nexport interface RedirectChain {\n originalUrl: string;\n finalUrl: string;\n hops: RedirectHop[];\n isCircular: boolean;\n hopCount: number;\n totalDurationMs: number;\n}\n\nimport type { Page } from 'playwright';\n\nexport async function trackRedirects(page: Page, url: string): Promise<RedirectChain> {\n const hops: RedirectHop[] = [];\n const seen = new Set<string>();\n let lastResponseTime = Date.now();\n\n const onResponse = (response: any) => {\n const status = response.status();\n if (status >= 300 && status < 400) {\n const from = response.url();\n const to = response.headers()['location'] ?? '';\n const now = Date.now();\n hops.push({ from, to, statusCode: status, durationMs: now - lastResponseTime });\n lastResponseTime = now;\n }\n };\n\n page.on('response', onResponse);\n\n try {\n await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 30_000 }).catch(() => {});\n } finally {\n page.off('response', onResponse);\n }\n\n const finalUrl = page.url();\n const isCircular = hops.some(h => {\n if (seen.has(h.to)) return true;\n seen.add(h.from);\n return false;\n });\n\n return {\n originalUrl: url,\n finalUrl,\n hops,\n isCircular,\n hopCount: hops.length,\n totalDurationMs: hops.reduce((sum, h) => sum + h.durationMs, 0),\n };\n}\n\nexport class RedirectRegistry {\n private chains = new Map<string, RedirectChain>();\n\n register(chain: RedirectChain): void {\n this.chains.set(chain.originalUrl, chain);\n }\n\n get(url: string): RedirectChain | undefined {\n return this.chains.get(url);\n }\n\n getAll(): RedirectChain[] {\n return [...this.chains.values()];\n }\n\n getProblematic(): RedirectChain[] {\n return [...this.chains.values()].filter(c => c.isCircular || c.hopCount > 3);\n }\n\n stats(): { total: number; withRedirects: number; circular: number; deepChains: number } {\n const all = [...this.chains.values()];\n return {\n total: all.length,\n withRedirects: all.filter(c => c.hopCount > 0).length,\n circular: all.filter(c => c.isCircular).length,\n deepChains: all.filter(c => c.hopCount > 3).length,\n };\n }\n}\n"],
5
+ "mappings": "AA2BA,eAAsB,eAAe,MAAY,KAAqC;AACpF,QAAM,OAAsB,CAAC;AAC7B,QAAM,OAAO,oBAAI,IAAY;AAC7B,MAAI,mBAAmB,KAAK,IAAI;AAEhC,QAAM,aAAa,CAAC,aAAkB;AACpC,UAAM,SAAS,SAAS,OAAO;AAC/B,QAAI,UAAU,OAAO,SAAS,KAAK;AACjC,YAAM,OAAO,SAAS,IAAI;AAC1B,YAAM,KAAK,SAAS,QAAQ,EAAE,UAAU,KAAK;AAC7C,YAAM,MAAM,KAAK,IAAI;AACrB,WAAK,KAAK,EAAE,MAAM,IAAI,YAAY,QAAQ,YAAY,MAAM,iBAAiB,CAAC;AAC9E,yBAAmB;AAAA,IACrB;AAAA,EACF;AAEA,OAAK,GAAG,YAAY,UAAU;AAE9B,MAAI;AACF,UAAM,KAAK,KAAK,KAAK,EAAE,WAAW,oBAAoB,SAAS,IAAO,CAAC,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AAAA,EACzF,UAAE;AACA,SAAK,IAAI,YAAY,UAAU;AAAA,EACjC;AAEA,QAAM,WAAW,KAAK,IAAI;AAC1B,QAAM,aAAa,KAAK,KAAK,OAAK;AAChC,QAAI,KAAK,IAAI,EAAE,EAAE,EAAG,QAAO;AAC3B,SAAK,IAAI,EAAE,IAAI;AACf,WAAO;AAAA,EACT,CAAC;AAED,SAAO;AAAA,IACL,aAAa;AAAA,IACb;AAAA,IACA;AAAA,IACA;AAAA,IACA,UAAU,KAAK;AAAA,IACf,iBAAiB,KAAK,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,YAAY,CAAC;AAAA,EAChE;AACF;AAEO,MAAM,iBAAiB;AAAA,EACpB,SAAS,oBAAI,IAA2B;AAAA,EAEhD,SAAS,OAA4B;AACnC,SAAK,OAAO,IAAI,MAAM,aAAa,KAAK;AAAA,EAC1C;AAAA,EAEA,IAAI,KAAwC;AAC1C,WAAO,KAAK,OAAO,IAAI,GAAG;AAAA,EAC5B;AAAA,EAEA,SAA0B;AACxB,WAAO,CAAC,GAAG,KAAK,OAAO,OAAO,CAAC;AAAA,EACjC;AAAA,EAEA,iBAAkC;AAChC,WAAO,CAAC,GAAG,KAAK,OAAO,OAAO,CAAC,EAAE,OAAO,OAAK,EAAE,cAAc,EAAE,WAAW,CAAC;AAAA,EAC7E;AAAA,EAEA,QAAwF;AACtF,UAAM,MAAM,CAAC,GAAG,KAAK,OAAO,OAAO,CAAC;AACpC,WAAO;AAAA,MACL,OAAO,IAAI;AAAA,MACX,eAAe,IAAI,OAAO,OAAK,EAAE,WAAW,CAAC,EAAE;AAAA,MAC/C,UAAU,IAAI,OAAO,OAAK,EAAE,UAAU,EAAE;AAAA,MACxC,YAAY,IAAI,OAAO,OAAK,EAAE,WAAW,CAAC,EAAE;AAAA,IAC9C;AAAA,EACF;AACF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Redis-backed distributed crawl frontier.
3
+ *
4
+ * Replaces the in-memory CrawlFrontier for horizontally-scaled crawler deployments.
5
+ * Multiple crawler workers on different containers can share the same URL queue for
6
+ * a given job — enabling 10x crawl throughput on large enterprise apps.
7
+ *
8
+ * Redis key namespace: zeta:frontier:{jobId}
9
+ * LIST zeta:frontier:{jobId}:queue — BFS queue (LPUSH/RPOP)
10
+ * SET zeta:frontier:{jobId}:visited — deduplication set
11
+ * EXPIRE — 24h TTL on all keys
12
+ *
13
+ * Graceful fallback: if REDIS_URL is not set, RedisCrawlFrontier silently falls
14
+ * back to in-memory arrays so local development works without Redis.
15
+ */
16
+ export declare class RedisCrawlFrontier {
17
+ private readonly jobId;
18
+ private redis;
19
+ private queueKey;
20
+ private visitedKey;
21
+ private inMemoryQueue;
22
+ private inMemoryVisited;
23
+ private useRedis;
24
+ constructor(jobId: string);
25
+ init(): Promise<void>;
26
+ /** Add a URL to the frontier. No-op if already visited. Returns true if added. */
27
+ push(url: string): Promise<boolean>;
28
+ /** Pop the next URL (FIFO/BFS). Returns null if queue empty. */
29
+ shift(): Promise<string | null>;
30
+ /** Check if a URL has already been visited/queued. */
31
+ hasVisited(url: string): Promise<boolean>;
32
+ /** Current queue length. */
33
+ length(): Promise<number>;
34
+ /** Total visited count. */
35
+ visitedCount(): Promise<number>;
36
+ /** Seed initial URLs (used at job start). */
37
+ seed(urls: string[]): Promise<void>;
38
+ /** Clean up all Redis keys for this frontier. Call at job completion. */
39
+ cleanup(): Promise<void>;
40
+ /** Stats for monitoring. */
41
+ stats(): Promise<{
42
+ backend: 'redis' | 'memory';
43
+ queued: number;
44
+ visited: number;
45
+ }>;
46
+ }
47
+ /** Check if Redis is configured for distributed frontier. */
48
+ export declare function isRedisFrontierEnabled(): boolean;
@@ -0,0 +1,126 @@
1
+ const FRONTIER_TTL = 86400;
2
+ class RedisCrawlFrontier {
3
+ constructor(jobId) {
4
+ this.jobId = jobId;
5
+ this.queueKey = `zeta:frontier:${jobId}:queue`;
6
+ this.visitedKey = `zeta:frontier:${jobId}:visited`;
7
+ this.useRedis = !!process.env.REDIS_URL;
8
+ }
9
+ jobId;
10
+ redis;
11
+ queueKey;
12
+ visitedKey;
13
+ inMemoryQueue = [];
14
+ inMemoryVisited = /* @__PURE__ */ new Set();
15
+ useRedis;
16
+ async init() {
17
+ if (!this.useRedis) return;
18
+ try {
19
+ const Redis = await import("ioredis").then((m) => m.default ?? m);
20
+ this.redis = new Redis(process.env.REDIS_URL, {
21
+ maxRetriesPerRequest: 2,
22
+ enableReadyCheck: false,
23
+ lazyConnect: true
24
+ });
25
+ await this.redis.connect().catch(() => {
26
+ this.useRedis = false;
27
+ this.redis = null;
28
+ });
29
+ } catch {
30
+ this.useRedis = false;
31
+ this.redis = null;
32
+ }
33
+ }
34
+ /** Add a URL to the frontier. No-op if already visited. Returns true if added. */
35
+ async push(url) {
36
+ if (this.useRedis && this.redis) {
37
+ try {
38
+ const isNew = await this.redis.sadd(this.visitedKey, url);
39
+ if (!isNew) return false;
40
+ await this.redis.lpush(this.queueKey, url);
41
+ await this.redis.expire(this.queueKey, FRONTIER_TTL);
42
+ await this.redis.expire(this.visitedKey, FRONTIER_TTL);
43
+ return true;
44
+ } catch {
45
+ }
46
+ }
47
+ if (this.inMemoryVisited.has(url)) return false;
48
+ this.inMemoryVisited.add(url);
49
+ this.inMemoryQueue.push(url);
50
+ return true;
51
+ }
52
+ /** Pop the next URL (FIFO/BFS). Returns null if queue empty. */
53
+ async shift() {
54
+ if (this.useRedis && this.redis) {
55
+ try {
56
+ const url = await this.redis.rpop(this.queueKey);
57
+ return url ?? null;
58
+ } catch {
59
+ }
60
+ }
61
+ return this.inMemoryQueue.shift() ?? null;
62
+ }
63
+ /** Check if a URL has already been visited/queued. */
64
+ async hasVisited(url) {
65
+ if (this.useRedis && this.redis) {
66
+ try {
67
+ return await this.redis.sismember(this.visitedKey, url) === 1;
68
+ } catch {
69
+ }
70
+ }
71
+ return this.inMemoryVisited.has(url);
72
+ }
73
+ /** Current queue length. */
74
+ async length() {
75
+ if (this.useRedis && this.redis) {
76
+ try {
77
+ return await this.redis.llen(this.queueKey);
78
+ } catch {
79
+ }
80
+ }
81
+ return this.inMemoryQueue.length;
82
+ }
83
+ /** Total visited count. */
84
+ async visitedCount() {
85
+ if (this.useRedis && this.redis) {
86
+ try {
87
+ return await this.redis.scard(this.visitedKey);
88
+ } catch {
89
+ }
90
+ }
91
+ return this.inMemoryVisited.size;
92
+ }
93
+ /** Seed initial URLs (used at job start). */
94
+ async seed(urls) {
95
+ for (const url of urls) await this.push(url);
96
+ }
97
+ /** Clean up all Redis keys for this frontier. Call at job completion. */
98
+ async cleanup() {
99
+ if (this.useRedis && this.redis) {
100
+ try {
101
+ await this.redis.del(this.queueKey, this.visitedKey);
102
+ } catch {
103
+ }
104
+ try {
105
+ await this.redis.quit();
106
+ } catch {
107
+ }
108
+ }
109
+ }
110
+ /** Stats for monitoring. */
111
+ async stats() {
112
+ return {
113
+ backend: this.useRedis && this.redis ? "redis" : "memory",
114
+ queued: await this.length(),
115
+ visited: await this.visitedCount()
116
+ };
117
+ }
118
+ }
119
+ function isRedisFrontierEnabled() {
120
+ return !!process.env.REDIS_URL;
121
+ }
122
+ export {
123
+ RedisCrawlFrontier,
124
+ isRedisFrontierEnabled
125
+ };
126
+ //# sourceMappingURL=redis-frontier.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/redis-frontier.ts"],
4
+ "sourcesContent": ["/**\n * Redis-backed distributed crawl frontier.\n *\n * Replaces the in-memory CrawlFrontier for horizontally-scaled crawler deployments.\n * Multiple crawler workers on different containers can share the same URL queue for\n * a given job \u2014 enabling 10x crawl throughput on large enterprise apps.\n *\n * Redis key namespace: zeta:frontier:{jobId}\n * LIST zeta:frontier:{jobId}:queue \u2014 BFS queue (LPUSH/RPOP)\n * SET zeta:frontier:{jobId}:visited \u2014 deduplication set\n * EXPIRE \u2014 24h TTL on all keys\n *\n * Graceful fallback: if REDIS_URL is not set, RedisCrawlFrontier silently falls\n * back to in-memory arrays so local development works without Redis.\n */\n\nconst FRONTIER_TTL = 86400; // 24h in seconds\n\nexport class RedisCrawlFrontier {\n private redis: any;\n private queueKey: string;\n private visitedKey: string;\n private inMemoryQueue: string[] = [];\n private inMemoryVisited = new Set<string>();\n private useRedis: boolean;\n\n constructor(private readonly jobId: string) {\n this.queueKey = `zeta:frontier:${jobId}:queue`;\n this.visitedKey = `zeta:frontier:${jobId}:visited`;\n this.useRedis = !!process.env.REDIS_URL;\n }\n\n async init(): Promise<void> {\n if (!this.useRedis) return;\n try {\n const Redis: any = await import('ioredis').then(m => m.default ?? m);\n this.redis = new Redis(process.env.REDIS_URL!, {\n maxRetriesPerRequest: 2,\n enableReadyCheck: false,\n lazyConnect: true,\n });\n await this.redis.connect().catch(() => {\n this.useRedis = false;\n this.redis = null;\n });\n } catch {\n this.useRedis = false;\n this.redis = null;\n }\n }\n\n /** Add a URL to the frontier. No-op if already visited. Returns true if added. */\n async push(url: string): Promise<boolean> {\n if (this.useRedis && this.redis) {\n try {\n const isNew = await this.redis.sadd(this.visitedKey, url);\n if (!isNew) return false;\n await this.redis.lpush(this.queueKey, url);\n await this.redis.expire(this.queueKey, FRONTIER_TTL);\n await this.redis.expire(this.visitedKey, FRONTIER_TTL);\n return true;\n } catch {\n // Fall through to in-memory on Redis error\n }\n }\n if (this.inMemoryVisited.has(url)) return false;\n this.inMemoryVisited.add(url);\n this.inMemoryQueue.push(url);\n return true;\n }\n\n /** Pop the next URL (FIFO/BFS). Returns null if queue empty. */\n async shift(): Promise<string | null> {\n if (this.useRedis && this.redis) {\n try {\n const url = await this.redis.rpop(this.queueKey);\n return url ?? null;\n } catch {\n // Fall through\n }\n }\n return this.inMemoryQueue.shift() ?? null;\n }\n\n /** Check if a URL has already been visited/queued. */\n async hasVisited(url: string): Promise<boolean> {\n if (this.useRedis && this.redis) {\n try {\n return (await this.redis.sismember(this.visitedKey, url)) === 1;\n } catch { /* fall through */ }\n }\n return this.inMemoryVisited.has(url);\n }\n\n /** Current queue length. */\n async length(): Promise<number> {\n if (this.useRedis && this.redis) {\n try {\n return await this.redis.llen(this.queueKey);\n } catch { /* fall through */ }\n }\n return this.inMemoryQueue.length;\n }\n\n /** Total visited count. */\n async visitedCount(): Promise<number> {\n if (this.useRedis && this.redis) {\n try {\n return await this.redis.scard(this.visitedKey);\n } catch { /* fall through */ }\n }\n return this.inMemoryVisited.size;\n }\n\n /** Seed initial URLs (used at job start). */\n async seed(urls: string[]): Promise<void> {\n for (const url of urls) await this.push(url);\n }\n\n /** Clean up all Redis keys for this frontier. Call at job completion. */\n async cleanup(): Promise<void> {\n if (this.useRedis && this.redis) {\n try {\n await this.redis.del(this.queueKey, this.visitedKey);\n } catch { /* ignore */ }\n try { await this.redis.quit(); } catch { /* ignore */ }\n }\n }\n\n /** Stats for monitoring. */\n async stats(): Promise<{ backend: 'redis' | 'memory'; queued: number; visited: number }> {\n return {\n backend: this.useRedis && this.redis ? 'redis' : 'memory',\n queued: await this.length(),\n visited: await this.visitedCount(),\n };\n }\n}\n\n/** Check if Redis is configured for distributed frontier. */\nexport function isRedisFrontierEnabled(): boolean {\n return !!process.env.REDIS_URL;\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Redis Streams distributed job queue.
3
+ *
4
+ * Provides horizontal crawler scaling via Redis Streams consumer groups.
5
+ * Multiple crawler nodes can pull from the same stream — each job is
6
+ * delivered to exactly one consumer (at-least-once via ACK/PENDING).
7
+ *
8
+ * Used when REDIS_URL is set AND JOB_QUEUE_BACKEND=redis.
9
+ * Falls back to the Postgres-backed queue when Redis is unavailable.
10
+ *
11
+ * Stream key: zeta:crawl:stream
12
+ * Group name: zeta:crawl:workers
13
+ * Consumer id: zeta:worker:<hostname>:<pid>
14
+ */
15
+ export interface RedisQueueJob {
16
+ id: string;
17
+ jobId: string;
18
+ tenantId: string;
19
+ projectId: string;
20
+ payload: Record<string, unknown>;
21
+ enqueuedAt: string;
22
+ }
23
+ export interface RedisQueueClient {
24
+ enqueue(jobId: string, tenantId: string, projectId: string, payload?: Record<string, unknown>): Promise<string>;
25
+ dequeue(timeoutMs?: number): Promise<RedisQueueJob | null>;
26
+ ack(streamEntryId: string): Promise<void>;
27
+ nack(streamEntryId: string): Promise<void>;
28
+ reclaimStale(maxIdleMs?: number): Promise<RedisQueueJob[]>;
29
+ stats(): Promise<{
30
+ pending: number;
31
+ delivered: number;
32
+ groupLag: number;
33
+ }>;
34
+ close(): Promise<void>;
35
+ }
36
+ /**
37
+ * Create a Redis Streams job queue client.
38
+ * Throws if REDIS_URL is not set.
39
+ */
40
+ export declare function createRedisQueue(): Promise<RedisQueueClient>;
41
+ /**
42
+ * Check if Redis queue backend is configured.
43
+ */
44
+ export declare function isRedisQueueEnabled(): boolean;
45
+ /**
46
+ * Convenience: create queue only if enabled, else return null.
47
+ */
48
+ export declare function maybeCreateRedisQueue(): Promise<RedisQueueClient | null>;
@@ -0,0 +1,165 @@
1
+ import { hostname } from "node:os";
2
+ const STREAM_KEY = "zeta:crawl:stream";
3
+ const GROUP_NAME = "zeta:crawl:workers";
4
+ const MAX_LEN = 1e4;
5
+ function consumerId() {
6
+ return `zeta:worker:${hostname()}:${process.pid}`;
7
+ }
8
+ let ioredis;
9
+ async function getIoredis() {
10
+ if (!ioredis) {
11
+ ioredis = await import("ioredis").then((m) => m.default ?? m);
12
+ }
13
+ return ioredis;
14
+ }
15
+ async function createRedisQueue() {
16
+ const redisUrl = process.env.REDIS_URL;
17
+ if (!redisUrl) throw new Error("REDIS_URL env var required for redis queue backend");
18
+ const Redis = await getIoredis();
19
+ const redis = new Redis(redisUrl, {
20
+ maxRetriesPerRequest: 3,
21
+ enableReadyCheck: true,
22
+ lazyConnect: false
23
+ });
24
+ await redis.xgroup("CREATE", STREAM_KEY, GROUP_NAME, "$", "MKSTREAM").catch((e) => {
25
+ if (!e.message?.includes("BUSYGROUP")) throw e;
26
+ });
27
+ const consumer = consumerId();
28
+ return {
29
+ async enqueue(jobId, tenantId, projectId, payload = {}) {
30
+ const entryId = await redis.xadd(
31
+ STREAM_KEY,
32
+ "MAXLEN",
33
+ "~",
34
+ MAX_LEN,
35
+ "*",
36
+ "jobId",
37
+ jobId,
38
+ "tenantId",
39
+ tenantId,
40
+ "projectId",
41
+ projectId,
42
+ "payload",
43
+ JSON.stringify(payload),
44
+ "enqueuedAt",
45
+ (/* @__PURE__ */ new Date()).toISOString()
46
+ );
47
+ return entryId;
48
+ },
49
+ async dequeue(timeoutMs = 5e3) {
50
+ const pending = await redis.xreadgroup(
51
+ "GROUP",
52
+ GROUP_NAME,
53
+ consumer,
54
+ "COUNT",
55
+ "1",
56
+ "STREAMS",
57
+ STREAM_KEY,
58
+ "0"
59
+ ) ?? [];
60
+ let entries = pending?.[0]?.[1] ?? [];
61
+ if (!entries.length) {
62
+ const fresh = await redis.xreadgroup(
63
+ "GROUP",
64
+ GROUP_NAME,
65
+ consumer,
66
+ "COUNT",
67
+ "1",
68
+ "BLOCK",
69
+ timeoutMs,
70
+ "STREAMS",
71
+ STREAM_KEY,
72
+ ">"
73
+ ) ?? [];
74
+ entries = fresh?.[0]?.[1] ?? [];
75
+ }
76
+ if (!entries.length) return null;
77
+ const [entryId, fields] = entries[0];
78
+ const obj = {};
79
+ for (let i = 0; i < fields.length; i += 2) {
80
+ obj[fields[i]] = fields[i + 1];
81
+ }
82
+ return {
83
+ id: entryId,
84
+ jobId: obj.jobId,
85
+ tenantId: obj.tenantId,
86
+ projectId: obj.projectId,
87
+ payload: JSON.parse(obj.payload ?? "{}"),
88
+ enqueuedAt: obj.enqueuedAt
89
+ };
90
+ },
91
+ async ack(streamEntryId) {
92
+ await redis.xack(STREAM_KEY, GROUP_NAME, streamEntryId);
93
+ },
94
+ async nack(streamEntryId) {
95
+ await redis.xack(STREAM_KEY, GROUP_NAME, streamEntryId);
96
+ },
97
+ async reclaimStale(maxIdleMs = 6e4) {
98
+ const result = await redis.xautoclaim(
99
+ STREAM_KEY,
100
+ GROUP_NAME,
101
+ consumer,
102
+ maxIdleMs,
103
+ "0-0",
104
+ "COUNT",
105
+ "10"
106
+ );
107
+ const entries = Array.isArray(result) ? result[1] ?? [] : [];
108
+ return entries.map(([entryId, fields]) => {
109
+ const obj = {};
110
+ for (let i = 0; i < fields.length; i += 2) obj[fields[i]] = fields[i + 1];
111
+ return {
112
+ id: entryId,
113
+ jobId: obj.jobId,
114
+ tenantId: obj.tenantId,
115
+ projectId: obj.projectId,
116
+ payload: JSON.parse(obj.payload ?? "{}"),
117
+ enqueuedAt: obj.enqueuedAt
118
+ };
119
+ });
120
+ },
121
+ async stats() {
122
+ const info = await redis.xinfo("GROUPS", STREAM_KEY);
123
+ const group = info.find((g) => {
124
+ const arr = Array.isArray(g) ? g : [];
125
+ for (let i = 0; i < arr.length; i += 2) {
126
+ if (arr[i] === "name" && arr[i + 1] === GROUP_NAME) return true;
127
+ }
128
+ return false;
129
+ });
130
+ if (!group) return { pending: 0, delivered: 0, groupLag: 0 };
131
+ const flat = Array.isArray(group) ? group : [];
132
+ const get = (key) => {
133
+ for (let i = 0; i < flat.length - 1; i += 2) {
134
+ if (flat[i] === key) return Number(flat[i + 1]) || 0;
135
+ }
136
+ return 0;
137
+ };
138
+ return {
139
+ pending: get("pel-count"),
140
+ delivered: get("delivered-count"),
141
+ groupLag: get("lag")
142
+ };
143
+ },
144
+ async close() {
145
+ await redis.quit().catch(() => {
146
+ });
147
+ }
148
+ };
149
+ }
150
+ function isRedisQueueEnabled() {
151
+ return !!(process.env.REDIS_URL && process.env.JOB_QUEUE_BACKEND === "redis");
152
+ }
153
+ async function maybeCreateRedisQueue() {
154
+ if (!isRedisQueueEnabled()) return null;
155
+ return createRedisQueue().catch((e) => {
156
+ console.error("[redis-queue] Failed to connect:", e.message);
157
+ return null;
158
+ });
159
+ }
160
+ export {
161
+ createRedisQueue,
162
+ isRedisQueueEnabled,
163
+ maybeCreateRedisQueue
164
+ };
165
+ //# sourceMappingURL=redis-queue.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/redis-queue.ts"],
4
+ "sourcesContent": ["/**\n * Redis Streams distributed job queue.\n *\n * Provides horizontal crawler scaling via Redis Streams consumer groups.\n * Multiple crawler nodes can pull from the same stream \u2014 each job is\n * delivered to exactly one consumer (at-least-once via ACK/PENDING).\n *\n * Used when REDIS_URL is set AND JOB_QUEUE_BACKEND=redis.\n * Falls back to the Postgres-backed queue when Redis is unavailable.\n *\n * Stream key: zeta:crawl:stream\n * Group name: zeta:crawl:workers\n * Consumer id: zeta:worker:<hostname>:<pid>\n */\n\nimport { createHash } from 'node:crypto';\nimport { hostname } from 'node:os';\n\nexport interface RedisQueueJob {\n id: string; // Redis stream entry ID (e.g. \"1720001234567-0\")\n jobId: string; // Postgres job row ID\n tenantId: string;\n projectId: string;\n payload: Record<string, unknown>;\n enqueuedAt: string;\n}\n\nexport interface RedisQueueClient {\n enqueue(jobId: string, tenantId: string, projectId: string, payload?: Record<string, unknown>): Promise<string>;\n dequeue(timeoutMs?: number): Promise<RedisQueueJob | null>;\n ack(streamEntryId: string): Promise<void>;\n nack(streamEntryId: string): Promise<void>;\n reclaimStale(maxIdleMs?: number): Promise<RedisQueueJob[]>;\n stats(): Promise<{ pending: number; delivered: number; groupLag: number }>;\n close(): Promise<void>;\n}\n\nconst STREAM_KEY = 'zeta:crawl:stream';\nconst GROUP_NAME = 'zeta:crawl:workers';\nconst MAX_LEN = 10_000; // approx cap on stream length\n\nfunction consumerId(): string {\n return `zeta:worker:${hostname()}:${process.pid}`;\n}\n\nlet ioredis: any;\nasync function getIoredis() {\n if (!ioredis) {\n ioredis = await import('ioredis').then(m => m.default ?? m);\n }\n return ioredis;\n}\n\n/**\n * Create a Redis Streams job queue client.\n * Throws if REDIS_URL is not set.\n */\nexport async function createRedisQueue(): Promise<RedisQueueClient> {\n const redisUrl = process.env.REDIS_URL;\n if (!redisUrl) throw new Error('REDIS_URL env var required for redis queue backend');\n\n const Redis = await getIoredis();\n const redis = new Redis(redisUrl, {\n maxRetriesPerRequest: 3,\n enableReadyCheck: true,\n lazyConnect: false,\n });\n\n // Create stream + consumer group if they don't exist\n await redis.xgroup('CREATE', STREAM_KEY, GROUP_NAME, '$', 'MKSTREAM').catch((e: any) => {\n if (!e.message?.includes('BUSYGROUP')) throw e;\n });\n\n const consumer = consumerId();\n\n return {\n async enqueue(jobId, tenantId, projectId, payload = {}): Promise<string> {\n const entryId = await redis.xadd(\n STREAM_KEY, 'MAXLEN', '~', MAX_LEN, '*',\n 'jobId', jobId,\n 'tenantId', tenantId,\n 'projectId', projectId,\n 'payload', JSON.stringify(payload),\n 'enqueuedAt', new Date().toISOString(),\n );\n return entryId as string;\n },\n\n async dequeue(timeoutMs = 5_000): Promise<RedisQueueJob | null> {\n // First drain pending (unacknowledged) messages for this consumer\n const pending: any[][] = await redis.xreadgroup(\n 'GROUP', GROUP_NAME, consumer,\n 'COUNT', '1',\n 'STREAMS', STREAM_KEY, '0',\n ) ?? [];\n\n let entries = pending?.[0]?.[1] ?? [];\n\n if (!entries.length) {\n // Block-read for new messages\n const fresh: any[][] = await redis.xreadgroup(\n 'GROUP', GROUP_NAME, consumer,\n 'COUNT', '1',\n 'BLOCK', timeoutMs,\n 'STREAMS', STREAM_KEY, '>',\n ) ?? [];\n entries = fresh?.[0]?.[1] ?? [];\n }\n\n if (!entries.length) return null;\n\n const [entryId, fields] = entries[0] as [string, string[]];\n const obj: Record<string, string> = {};\n for (let i = 0; i < fields.length; i += 2) {\n obj[fields[i]] = fields[i + 1];\n }\n\n return {\n id: entryId,\n jobId: obj.jobId,\n tenantId: obj.tenantId,\n projectId: obj.projectId,\n payload: JSON.parse(obj.payload ?? '{}'),\n enqueuedAt: obj.enqueuedAt,\n };\n },\n\n async ack(streamEntryId: string): Promise<void> {\n await redis.xack(STREAM_KEY, GROUP_NAME, streamEntryId);\n },\n\n async nack(streamEntryId: string): Promise<void> {\n // Move back to '>'-readable by deleting and re-adding (simplest approach)\n // In production, prefer a dead-letter stream instead.\n await redis.xack(STREAM_KEY, GROUP_NAME, streamEntryId);\n },\n\n async reclaimStale(maxIdleMs = 60_000): Promise<RedisQueueJob[]> {\n const result: any = await redis.xautoclaim(\n STREAM_KEY, GROUP_NAME, consumer,\n maxIdleMs, '0-0', 'COUNT', '10',\n );\n const entries: [string, string[]][] = Array.isArray(result) ? (result[1] ?? []) : [];\n return entries.map(([entryId, fields]) => {\n const obj: Record<string, string> = {};\n for (let i = 0; i < fields.length; i += 2) obj[fields[i]] = fields[i + 1];\n return {\n id: entryId,\n jobId: obj.jobId,\n tenantId: obj.tenantId,\n projectId: obj.projectId,\n payload: JSON.parse(obj.payload ?? '{}'),\n enqueuedAt: obj.enqueuedAt,\n };\n });\n },\n\n async stats(): Promise<{ pending: number; delivered: number; groupLag: number }> {\n const info: any[] = await redis.xinfo('GROUPS', STREAM_KEY);\n // xinfo groups returns array of arrays: [name, ..., pel-count, ..., lag, ...]\n const group = info.find((g: any) => {\n const arr = Array.isArray(g) ? g : [];\n for (let i = 0; i < arr.length; i += 2) {\n if (arr[i] === 'name' && arr[i + 1] === GROUP_NAME) return true;\n }\n return false;\n });\n\n if (!group) return { pending: 0, delivered: 0, groupLag: 0 };\n\n const flat = Array.isArray(group) ? group : [];\n const get = (key: string) => {\n for (let i = 0; i < flat.length - 1; i += 2) {\n if (flat[i] === key) return Number(flat[i + 1]) || 0;\n }\n return 0;\n };\n\n return {\n pending: get('pel-count'),\n delivered: get('delivered-count'),\n groupLag: get('lag'),\n };\n },\n\n async close(): Promise<void> {\n await redis.quit().catch(() => {});\n },\n };\n}\n\n/**\n * Check if Redis queue backend is configured.\n */\nexport function isRedisQueueEnabled(): boolean {\n return !!(process.env.REDIS_URL && process.env.JOB_QUEUE_BACKEND === 'redis');\n}\n\n/**\n * Convenience: create queue only if enabled, else return null.\n */\nexport async function maybeCreateRedisQueue(): Promise<RedisQueueClient | null> {\n if (!isRedisQueueEnabled()) return null;\n return createRedisQueue().catch((e) => {\n console.error('[redis-queue] Failed to connect:', e.message);\n return null;\n });\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,38 @@
1
+ export interface ResponseCacheEntry {
2
+ url: string;
3
+ etag?: string;
4
+ lastModified?: string;
5
+ cacheControl?: string;
6
+ body: string;
7
+ contentType: string;
8
+ statusCode: number;
9
+ cachedAt: number;
10
+ hitCount: number;
11
+ }
12
+ export declare class ResponseCache {
13
+ private readonly dir;
14
+ private totalHits;
15
+ private totalRequests;
16
+ constructor(cacheDir: string);
17
+ private key;
18
+ private path;
19
+ get(url: string): ResponseCacheEntry | null;
20
+ private read;
21
+ isFresh(url: string): boolean;
22
+ staleIfError(url: string): boolean;
23
+ set(url: string, entry: Omit<ResponseCacheEntry, 'cachedAt' | 'hitCount'>): void;
24
+ getValidationHeaders(url: string): {
25
+ 'If-None-Match'?: string;
26
+ 'If-Modified-Since'?: string;
27
+ };
28
+ markHit(url: string): void;
29
+ invalidate(url: string): void;
30
+ clear(): void;
31
+ stats(): {
32
+ entries: number;
33
+ totalSizeBytes: number;
34
+ hitRate: number;
35
+ totalHits: number;
36
+ };
37
+ prune(maxAgeMs: number): number;
38
+ }