@detiq/crawler 1.1.8 → 1.1.9

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 (82) hide show
  1. package/dist/accessibility-auditor.js +74 -54
  2. package/dist/ai-login.js +249 -227
  3. package/dist/auth-token-rotator.js +67 -57
  4. package/dist/auth.js +336 -299
  5. package/dist/browser-extension.js +142 -104
  6. package/dist/captcha-solver.js +125 -98
  7. package/dist/color-contrast.js +89 -77
  8. package/dist/content-chunker.js +126 -126
  9. package/dist/content-dedup.js +71 -54
  10. package/dist/cookie-consent.js +83 -68
  11. package/dist/cookie-jar.js +60 -53
  12. package/dist/crawler.js +5345 -4486
  13. package/dist/custom-js-injector.js +61 -45
  14. package/dist/dark-mode-capture.js +81 -67
  15. package/dist/dead-link-detector.js +55 -47
  16. package/dist/element-hash.js +12 -8
  17. package/dist/email-otp.js +195 -154
  18. package/dist/engine-router.js +98 -87
  19. package/dist/export/bulk-export.js +55 -70
  20. package/dist/export/vector-export.js +105 -77
  21. package/dist/extractors/advanced.js +266 -249
  22. package/dist/extractors/artifact-envelope.js +58 -59
  23. package/dist/failure-analysis-agent.js +33 -37
  24. package/dist/feature-flag-detector.js +66 -51
  25. package/dist/form-detector.js +75 -55
  26. package/dist/frontier.js +101 -76
  27. package/dist/gap-pass.js +89 -68
  28. package/dist/graphql-detector.js +72 -62
  29. package/dist/http-engine.js +330 -282
  30. package/dist/incremental-diff.js +87 -54
  31. package/dist/index.js +28 -67
  32. package/dist/input-fuzzer.js +97 -93
  33. package/dist/interactive-probe.js +827 -715
  34. package/dist/job-queue.js +2169 -2151
  35. package/dist/js-coverage.js +56 -52
  36. package/dist/js-router-detector.js +117 -86
  37. package/dist/language-detector.js +63 -51
  38. package/dist/link-classifier.js +50 -32
  39. package/dist/live-view.js +108 -91
  40. package/dist/locator-extractor.js +187 -151
  41. package/dist/logger.js +7 -9
  42. package/dist/markdown-extract.js +61 -48
  43. package/dist/native-crawler.js +946 -780
  44. package/dist/network-observer.js +156 -135
  45. package/dist/network-throttle.js +40 -29
  46. package/dist/openapi-generator.js +92 -77
  47. package/dist/pdf-extractor.js +90 -72
  48. package/dist/performance-metrics.js +102 -80
  49. package/dist/phash.js +144 -125
  50. package/dist/pii-scrubber.js +68 -65
  51. package/dist/playwright-recorder.js +58 -70
  52. package/dist/policies/crawl-safety.js +34 -31
  53. package/dist/profiles/domain-profile.js +125 -130
  54. package/dist/proxy.js +172 -146
  55. package/dist/rate-limiter.js +65 -58
  56. package/dist/redirect-tracker.js +64 -60
  57. package/dist/redis-frontier.js +136 -116
  58. package/dist/redis-queue.js +144 -157
  59. package/dist/response-cache.js +140 -127
  60. package/dist/responsive-detector.js +46 -42
  61. package/dist/retry-policy.js +50 -46
  62. package/dist/robots-txt.js +83 -69
  63. package/dist/screen-reader.js +70 -45
  64. package/dist/screenshot-masker.js +77 -59
  65. package/dist/seo-checker.js +107 -88
  66. package/dist/session-capture.js +212 -192
  67. package/dist/session-export.js +133 -107
  68. package/dist/sitemap-writer.js +41 -38
  69. package/dist/sitemap.js +202 -141
  70. package/dist/skyvern.js +148 -123
  71. package/dist/sqs-queue.js +42 -42
  72. package/dist/sso-token.js +90 -74
  73. package/dist/stealth/human-behavior.js +173 -156
  74. package/dist/telemetry.js +82 -68
  75. package/dist/test-executor.js +335 -309
  76. package/dist/tls-fingerprint.js +88 -77
  77. package/dist/url-pattern-completer.js +90 -71
  78. package/dist/visual-diff-verdict.js +38 -23
  79. package/dist/webhook-notifier.js +69 -71
  80. package/dist/websocket-detector.js +68 -56
  81. package/dist/worker.js +38 -29
  82. package/package.json +1 -1
@@ -1,60 +1,72 @@
1
+ /**
2
+ * WebSocket detection — intercepts WS connections opened during page crawl.
3
+ * Records connection URLs, message schemas, and message counts.
4
+ * This lets ZeTa discover real-time API contracts (chat, notifications, presence)
5
+ * that would be invisible to HTTP-only crawlers.
6
+ */
1
7
  function inferProtocol(sample) {
2
- try {
3
- const obj = JSON.parse(sample);
4
- if (obj?.jsonrpc) return "json-rpc";
5
- if (obj?.type === "connection_init" || obj?.type === "subscribe") return "graphql-ws";
6
- if (typeof obj?.sid === "string" || sample.startsWith("0{")) return "socket.io";
7
- return "json";
8
- } catch {
9
- return "raw";
10
- }
11
- }
12
- function attachWebSocketDetector(cdpSession) {
13
- const connections = /* @__PURE__ */ new Map();
14
- const onCreated = (evt) => {
15
- const { requestId, url, timestamp } = evt ?? {};
16
- if (!requestId || !url) return;
17
- connections.set(requestId, {
18
- url,
19
- framesSent: 0,
20
- framesReceived: 0,
21
- samples: [],
22
- protocol: "unknown",
23
- openedAt: new Date(timestamp * 1e3).toISOString()
24
- });
25
- };
26
- const onFrame = (evt, direction) => {
27
- const { requestId, response } = evt ?? {};
28
- if (!requestId) return;
29
- const conn = connections.get(requestId);
30
- if (!conn) return;
31
- if (direction === "sent") conn.framesSent++;
32
- else conn.framesReceived++;
33
- if (conn.samples.length < 10 && response?.payloadData) {
34
- const payload = String(response.payloadData).slice(0, 500);
35
- conn.samples.push({ direction, payload, ts: (/* @__PURE__ */ new Date()).toISOString() });
36
- if (conn.protocol === "unknown") conn.protocol = inferProtocol(payload);
8
+ try {
9
+ const obj = JSON.parse(sample);
10
+ if (obj?.jsonrpc)
11
+ return 'json-rpc';
12
+ if (obj?.type === 'connection_init' || obj?.type === 'subscribe')
13
+ return 'graphql-ws';
14
+ if (typeof obj?.sid === 'string' || sample.startsWith('0{'))
15
+ return 'socket.io';
16
+ return 'json';
17
+ }
18
+ catch {
19
+ return 'raw';
37
20
  }
38
- };
39
- const onSent = (evt) => onFrame(evt, "sent");
40
- const onReceived = (evt) => onFrame(evt, "received");
41
- if (cdpSession?.on) {
42
- cdpSession.on("Network.webSocketCreated", onCreated);
43
- cdpSession.on("Network.webSocketFrameSent", onSent);
44
- cdpSession.on("Network.webSocketFrameReceived", onReceived);
45
- }
46
- return {
47
- getConnections: () => [...connections.values()],
48
- detach() {
49
- if (cdpSession?.off) {
50
- cdpSession.off("Network.webSocketCreated", onCreated);
51
- cdpSession.off("Network.webSocketFrameSent", onSent);
52
- cdpSession.off("Network.webSocketFrameReceived", onReceived);
53
- }
21
+ }
22
+ export function attachWebSocketDetector(cdpSession) {
23
+ const connections = new Map();
24
+ const onCreated = (evt) => {
25
+ const { requestId, url, timestamp } = evt ?? {};
26
+ if (!requestId || !url)
27
+ return;
28
+ connections.set(requestId, {
29
+ url,
30
+ framesSent: 0,
31
+ framesReceived: 0,
32
+ samples: [],
33
+ protocol: 'unknown',
34
+ openedAt: new Date(timestamp * 1000).toISOString(),
35
+ });
36
+ };
37
+ const onFrame = (evt, direction) => {
38
+ const { requestId, response } = evt ?? {};
39
+ if (!requestId)
40
+ return;
41
+ const conn = connections.get(requestId);
42
+ if (!conn)
43
+ return;
44
+ if (direction === 'sent')
45
+ conn.framesSent++;
46
+ else
47
+ conn.framesReceived++;
48
+ if (conn.samples.length < 10 && response?.payloadData) {
49
+ const payload = String(response.payloadData).slice(0, 500);
50
+ conn.samples.push({ direction, payload, ts: new Date().toISOString() });
51
+ if (conn.protocol === 'unknown')
52
+ conn.protocol = inferProtocol(payload);
53
+ }
54
+ };
55
+ const onSent = (evt) => onFrame(evt, 'sent');
56
+ const onReceived = (evt) => onFrame(evt, 'received');
57
+ if (cdpSession?.on) {
58
+ cdpSession.on('Network.webSocketCreated', onCreated);
59
+ cdpSession.on('Network.webSocketFrameSent', onSent);
60
+ cdpSession.on('Network.webSocketFrameReceived', onReceived);
54
61
  }
55
- };
62
+ return {
63
+ getConnections: () => [...connections.values()],
64
+ detach() {
65
+ if (cdpSession?.off) {
66
+ cdpSession.off('Network.webSocketCreated', onCreated);
67
+ cdpSession.off('Network.webSocketFrameSent', onSent);
68
+ cdpSession.off('Network.webSocketFrameReceived', onReceived);
69
+ }
70
+ },
71
+ };
56
72
  }
57
- export {
58
- attachWebSocketDetector
59
- };
60
- //# sourceMappingURL=websocket-detector.js.map
package/dist/worker.js CHANGED
@@ -1,33 +1,42 @@
1
- import { startJobWorkerLoop, processDataRetentionPurge } from "./job-queue.js";
2
- import { logger } from "./logger.js";
1
+ /**
2
+ * Standalone worker process entrypoint for a dedicated crawler/worker container,
3
+ * separate from the API server (see infrastructure/ecs-task-definitions/crawler.json).
4
+ *
5
+ * index.ts is a side-effect-free library barrel imported by @detiq/api — it must NOT
6
+ * start any background loop itself, or importing a single helper from @detiq/crawler
7
+ * would silently spin up a second worker inside the API process too. This file is
8
+ * the actual entrypoint that keeps a dedicated worker container's process alive and
9
+ * consuming jobs: `node dist/worker.js`.
10
+ */
11
+ import { startJobWorkerLoop, processDataRetentionPurge } from './job-queue.js';
12
+ import { logger } from './logger.js';
3
13
  async function main() {
4
- logger.info("[worker] starting standalone crawler worker process");
5
- startJobWorkerLoop();
6
- const runPurge = () => {
7
- processDataRetentionPurge().catch((err) => {
8
- logger.error({ err: String(err) }, "[worker] data-retention purge failed");
9
- });
10
- };
11
- runPurge();
12
- setInterval(runPurge, 24 * 60 * 60 * 1e3);
13
- if (process.env.JOB_QUEUE_BACKEND === "sqs") {
14
- const { startSqsWorker } = await import("./sqs-queue.js");
15
- startSqsWorker().catch((err) => {
16
- logger.error({ err: String(err) }, "[worker] SQS worker crashed");
17
- process.exit(1);
18
- });
19
- }
14
+ logger.info('[worker] starting standalone crawler worker process');
15
+ // Durable DB-backed queue — claims every non-CRAWL job type, and CRAWL too when
16
+ // JOB_QUEUE_BACKEND isn't 'sqs'. Safe to run in many worker replicas at once.
17
+ startJobWorkerLoop();
18
+ // Daily data-retention purge: runs immediately on startup then every 24 h.
19
+ // Only runs in the dedicated worker container (this file), not in the API
20
+ // process, so there is exactly one purge sweep regardless of API replica count.
21
+ const runPurge = () => {
22
+ processDataRetentionPurge().catch((err) => {
23
+ logger.error({ err: String(err) }, '[worker] data-retention purge failed');
24
+ });
25
+ };
26
+ runPurge();
27
+ setInterval(runPurge, 24 * 60 * 60 * 1000);
28
+ // Optional: also consume the SQS transport for horizontally-scaled CRAWL fleets.
29
+ if (process.env.JOB_QUEUE_BACKEND === 'sqs') {
30
+ const { startSqsWorker } = await import('./sqs-queue.js');
31
+ startSqsWorker().catch((err) => {
32
+ logger.error({ err: String(err) }, '[worker] SQS worker crashed');
33
+ process.exit(1);
34
+ });
35
+ }
20
36
  }
21
- process.on("SIGTERM", () => {
22
- logger.info("[worker] SIGTERM received, exiting");
23
- process.exit(0);
24
- });
25
- process.on("SIGINT", () => {
26
- logger.info("[worker] SIGINT received, exiting");
27
- process.exit(0);
28
- });
37
+ process.on('SIGTERM', () => { logger.info('[worker] SIGTERM received, exiting'); process.exit(0); });
38
+ process.on('SIGINT', () => { logger.info('[worker] SIGINT received, exiting'); process.exit(0); });
29
39
  main().catch((err) => {
30
- logger.error({ err: String(err) }, "[worker] fatal startup error");
31
- process.exit(1);
40
+ logger.error({ err: String(err) }, '[worker] fatal startup error');
41
+ process.exit(1);
32
42
  });
33
- //# sourceMappingURL=worker.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@detiq/crawler",
3
- "version": "1.1.8",
3
+ "version": "1.1.9",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public",