@detiq/crawler 1.1.23 → 1.1.25

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 (40) hide show
  1. package/dist/crawler.js +173 -28
  2. package/dist/crawler.js.map +2 -2
  3. package/dist/extractors/branding-extractor.js +104 -0
  4. package/dist/extractors/branding-extractor.js.map +7 -0
  5. package/dist/extractors/summary-extractor.js +36 -0
  6. package/dist/extractors/summary-extractor.js.map +7 -0
  7. package/dist/frontier.js +20 -2
  8. package/dist/frontier.js.map +2 -2
  9. package/dist/incremental-diff.js +76 -0
  10. package/dist/incremental-diff.js.map +2 -2
  11. package/dist/index.js +3 -1
  12. package/dist/index.js.map +2 -2
  13. package/dist/interactive-probe.js +108 -17
  14. package/dist/interactive-probe.js.map +2 -2
  15. package/dist/job-queue.js +408 -61
  16. package/dist/job-queue.js.map +2 -2
  17. package/dist/sqs-queue.js +78 -10
  18. package/dist/sqs-queue.js.map +2 -2
  19. package/dist/worker.js +7 -7
  20. package/dist/worker.js.map +2 -2
  21. package/package.json +1 -1
  22. package/src/crawler.ts +208 -30
  23. package/src/extractors/branding-extractor.ts +159 -0
  24. package/src/extractors/summary-extractor.ts +49 -0
  25. package/src/frontier.ts +31 -1
  26. package/src/incremental-diff.ts +138 -0
  27. package/src/index.ts +1 -1
  28. package/src/interactive-probe.ts +129 -20
  29. package/src/job-queue.ts +489 -63
  30. package/src/sqs-queue.ts +92 -12
  31. package/src/worker.ts +13 -11
  32. package/types/crawler.d.ts +20 -0
  33. package/types/extractors/branding-extractor.d.ts +46 -0
  34. package/types/extractors/summary-extractor.d.ts +10 -0
  35. package/types/frontier.d.ts +9 -1
  36. package/types/incremental-diff.d.ts +57 -0
  37. package/types/index.d.ts +1 -1
  38. package/types/interactive-probe.d.ts +5 -1
  39. package/types/job-queue.d.ts +53 -0
  40. package/types/sqs-queue.d.ts +12 -1
package/src/sqs-queue.ts CHANGED
@@ -1,21 +1,96 @@
1
- import { SQSClient, SendMessageCommand, ReceiveMessageCommand, DeleteMessageCommand } from '@aws-sdk/client-sqs';
1
+ import {
2
+ SQSClient,
3
+ SendMessageCommand,
4
+ ReceiveMessageCommand,
5
+ DeleteMessageCommand,
6
+ GetQueueAttributesCommand,
7
+ } from '@aws-sdk/client-sqs';
2
8
 
3
9
  const sqs = new SQSClient({ region: process.env.AWS_REGION ?? 'us-east-1' });
4
10
 
5
- export async function sendJobToSqs(jobId: string, tenantId: string, config: any): Promise<void> {
6
- const queueUrl = process.env.SQS_CRAWL_QUEUE_URL;
7
- if (!queueUrl) throw new Error('SQS_CRAWL_QUEUE_URL not configured');
11
+ function getQueueUrl(): string {
12
+ const url = process.env.SQS_QUEUE_URL ?? process.env.SQS_CRAWL_QUEUE_URL;
13
+ if (!url) throw new Error('SQS_QUEUE_URL not configured');
14
+ return url;
15
+ }
16
+
17
+ function getDlqUrl(): string | null {
18
+ return process.env.SQS_DLQ_URL ?? null;
19
+ }
20
+
21
+ export async function sendJobToSqs(jobId: string): Promise<void> {
8
22
  await sqs.send(new SendMessageCommand({
9
- QueueUrl: queueUrl,
10
- MessageBody: JSON.stringify({ jobId, tenantId, config }),
23
+ QueueUrl: getQueueUrl(),
24
+ MessageBody: JSON.stringify({ jobId }),
11
25
  }));
12
26
  }
13
27
 
14
- export async function startSqsWorker(): Promise<void> {
15
- const queueUrl = process.env.SQS_CRAWL_QUEUE_URL;
16
- if (!queueUrl) throw new Error('SQS_CRAWL_QUEUE_URL not configured');
28
+ /** Returns the approximate number of messages in the SQS DLQ. */
29
+ export async function getDlqStats(): Promise<{ approximateMessageCount: number; dlqUrl: string | null }> {
30
+ const dlqUrl = getDlqUrl();
31
+ if (!dlqUrl) return { approximateMessageCount: 0, dlqUrl: null };
32
+ try {
33
+ const { Attributes = {} } = await sqs.send(new GetQueueAttributesCommand({
34
+ QueueUrl: dlqUrl,
35
+ AttributeNames: ['ApproximateNumberOfMessages'],
36
+ }));
37
+ return {
38
+ approximateMessageCount: parseInt(Attributes.ApproximateNumberOfMessages ?? '0', 10),
39
+ dlqUrl,
40
+ };
41
+ } catch (err: any) {
42
+ console.error('[sqs-dlq] getDlqStats error:', err.message);
43
+ return { approximateMessageCount: 0, dlqUrl };
44
+ }
45
+ }
46
+
47
+ /**
48
+ * Redrive up to `maxMessages` messages from the SQS DLQ back to the main queue.
49
+ * Each message is received from the DLQ, re-sent to the main queue, then deleted
50
+ * from the DLQ. Returns the number of messages successfully redriven.
51
+ */
52
+ export async function redriveFromDlq(maxMessages = 10): Promise<number> {
53
+ const dlqUrl = getDlqUrl();
54
+ if (!dlqUrl) {
55
+ console.warn('[sqs-dlq] SQS_DLQ_URL not configured — cannot redrive');
56
+ return 0;
57
+ }
58
+ const mainUrl = getQueueUrl();
59
+ let totalRedriven = 0;
60
+
61
+ while (totalRedriven < maxMessages) {
62
+ const batch = Math.min(10, maxMessages - totalRedriven);
63
+ const { Messages = [] } = await sqs.send(new ReceiveMessageCommand({
64
+ QueueUrl: dlqUrl,
65
+ MaxNumberOfMessages: batch,
66
+ WaitTimeSeconds: 1,
67
+ }));
68
+ if (Messages.length === 0) break;
69
+
70
+ for (const msg of Messages) {
71
+ try {
72
+ await sqs.send(new SendMessageCommand({
73
+ QueueUrl: mainUrl,
74
+ MessageBody: msg.Body!,
75
+ }));
76
+ await sqs.send(new DeleteMessageCommand({
77
+ QueueUrl: dlqUrl,
78
+ ReceiptHandle: msg.ReceiptHandle!,
79
+ }));
80
+ totalRedriven++;
81
+ console.log('[sqs-dlq] redriven message:', msg.MessageId);
82
+ } catch (err: any) {
83
+ console.error('[sqs-dlq] redrive error for message', msg.MessageId, ':', err.message);
84
+ }
85
+ }
86
+ }
87
+
88
+ return totalRedriven;
89
+ }
17
90
 
18
- const { processJob } = await import('./job-queue.js');
91
+ export async function startSqsWorker(): Promise<void> {
92
+ const queueUrl = getQueueUrl();
93
+ const { claimSpecificJob, dispatchClaimedJob } = await import('./job-queue.js');
19
94
  console.log('[sqs-worker] starting, queue:', queueUrl);
20
95
 
21
96
  while (true) {
@@ -31,14 +106,19 @@ export async function startSqsWorker(): Promise<void> {
31
106
  try {
32
107
  const data = JSON.parse(msg.Body!);
33
108
  jobId = data.jobId;
34
- await processJob(data.jobId, data.config);
109
+ // Atomically move QUEUED → RUNNING; returns null if another worker beat us.
110
+ const claimed = await claimSpecificJob(jobId);
111
+ if (claimed) {
112
+ await dispatchClaimedJob(claimed);
113
+ }
114
+ // Delete whether we ran it or another worker did — message is handled either way.
35
115
  await sqs.send(new DeleteMessageCommand({
36
116
  QueueUrl: queueUrl,
37
117
  ReceiptHandle: msg.ReceiptHandle!,
38
118
  }));
39
119
  } catch (err: any) {
40
120
  console.error(`[sqs-worker] job ${jobId} failed — leaving for SQS redrive:`, err.message);
41
- // Do NOT delete — SQS redrive policy + DLQ handles retry
121
+ // Do NOT delete — SQS visibility timeout expires and DLQ handles retry.
42
122
  }
43
123
  }));
44
124
  } catch (err: any) {
package/src/worker.ts CHANGED
@@ -14,8 +14,19 @@ import { logger } from './logger.js';
14
14
  async function main() {
15
15
  logger.info('[worker] starting standalone crawler worker process');
16
16
 
17
- // Durable DB-backed queue — claims every non-CRAWL job type, and CRAWL too when
18
- // JOB_QUEUE_BACKEND isn't 'sqs'. Safe to run in many worker replicas at once.
17
+ if (process.env.JOB_QUEUE_BACKEND === 'sqs') {
18
+ // SQS mode: messages arrive via long-poll; claimSpecificJob atomically moves
19
+ // QUEUED→RUNNING before dispatch, so multiple worker replicas are safe.
20
+ // DB poll loop also runs as a safety net for jobs whose SQS message was lost.
21
+ const { startSqsWorker } = await import('./sqs-queue.js');
22
+ startSqsWorker().catch((err) => {
23
+ logger.error({ err: String(err) }, '[worker] SQS worker crashed');
24
+ process.exit(1);
25
+ });
26
+ }
27
+
28
+ // DB-backed poll loop — primary in non-SQS mode, safety net in SQS mode.
29
+ // Claims all job types. Safe to run in many worker replicas (FOR UPDATE SKIP LOCKED).
19
30
  startJobWorkerLoop();
20
31
 
21
32
  // Daily data-retention purge: runs immediately on startup then every 24 h.
@@ -28,15 +39,6 @@ async function main() {
28
39
  };
29
40
  runPurge();
30
41
  setInterval(runPurge, 24 * 60 * 60 * 1000);
31
-
32
- // Optional: also consume the SQS transport for horizontally-scaled CRAWL fleets.
33
- if (process.env.JOB_QUEUE_BACKEND === 'sqs') {
34
- const { startSqsWorker } = await import('./sqs-queue.js');
35
- startSqsWorker().catch((err) => {
36
- logger.error({ err: String(err) }, '[worker] SQS worker crashed');
37
- process.exit(1);
38
- });
39
- }
40
42
  }
41
43
 
42
44
  process.on('SIGTERM', () => { logger.info('[worker] SIGTERM received, exiting'); process.exit(0); });
@@ -21,6 +21,12 @@ interface PerformanceMetric {
21
21
  ttfb?: number;
22
22
  cls?: number;
23
23
  }
24
+ export interface DiscoveredLinkMeta {
25
+ url: string;
26
+ text?: string;
27
+ landmark?: string;
28
+ source: 'DOM' | 'AI' | 'INTERACTIVE';
29
+ }
24
30
  /**
25
31
  * Lightweight pre-crawl link discovery: loads the app URL in a headless (proxy-aware) browser
26
32
  * and returns same-site `<a href>` links. Complements sitemap discovery for SPA/app sites that
@@ -60,6 +66,13 @@ export declare function crawlProject(opts: {
60
66
  startUrls?: string[];
61
67
  /** When provided, crawl ONLY these exact URLs (no link-following, no full screen clear). Used for selected-screen recrawl. */
62
68
  selectiveUrls?: string[];
69
+ /**
70
+ * URL path patterns that bypass the CUID/UUID generated-ID filter during BFS link discovery.
71
+ * Use {id} as a placeholder for any generated path segment (CUID, UUID, numeric ID, etc.).
72
+ * Example: ['/projects/{id}/tests', '/workspaces/{id}/settings']
73
+ * Without this, all URLs containing generated IDs are silently skipped by the BFS.
74
+ */
75
+ seedPathPatterns?: string[];
63
76
  /** When true, run the interactive probe after static crawl to capture real validation errors, toasts, and modals. */
64
77
  interactive?: boolean;
65
78
  /** Optional abort signal — abort() cancels the crawl between page navigations. */
@@ -143,10 +156,17 @@ export declare function crawlProject(opts: {
143
156
  autoThrottle?: boolean;
144
157
  /** Regex pattern — only crawl URLs whose path+query match this pattern. */
145
158
  urlFilter?: string;
159
+ /** Glob patterns — only enqueue URLs whose pathname matches at least one (supports * and **). */
160
+ includePatterns?: string[];
161
+ /** Glob patterns — skip URLs whose pathname matches any of these (supports * and **). */
162
+ excludePatterns?: string[];
146
163
  /** Already-captured URLs from a previous failed job — pre-seeded into visited map so they're never re-crawled. */
147
164
  skipUrls?: string[];
148
165
  /** DOM hashes collected in a prior segment — prevents re-screenshotting structurally identical pages across segments. */
149
166
  knownDomHashes?: string[];
167
+ /** When false, disables the puppeteer-extra stealth plugin (playwright-extra) for this crawl.
168
+ * Default true — stealth is applied unless explicitly set to false. */
169
+ stealth?: boolean;
150
170
  /** Called every 10 successful screen captures with current visited URLs and remaining queue — used for checkpoint persistence. */
151
171
  onCheckpoint?: (visitedUrls: string[], pendingQueue: string[]) => Promise<void>;
152
172
  /** Discovery mode only: called after each page with the current set of discovered original URLs — enables streaming partial results to callers. */
@@ -0,0 +1,46 @@
1
+ import type { Page } from 'playwright';
2
+ export interface BrandingColors {
3
+ primary?: string;
4
+ accent?: string;
5
+ background?: string;
6
+ textPrimary?: string;
7
+ link?: string;
8
+ }
9
+ export interface BrandingFont {
10
+ family: string;
11
+ role: 'heading' | 'body';
12
+ }
13
+ export interface BrandingImages {
14
+ logo?: string;
15
+ favicon?: string;
16
+ ogImage?: string;
17
+ }
18
+ export interface BrandingPersonality {
19
+ tone?: string;
20
+ energy?: 'high' | 'medium' | 'low';
21
+ targetAudience?: string;
22
+ }
23
+ export interface BrandingResult {
24
+ colorScheme: 'light' | 'dark';
25
+ colors: BrandingColors;
26
+ fonts: BrandingFont[];
27
+ images: BrandingImages;
28
+ personality?: BrandingPersonality;
29
+ designSystem?: {
30
+ framework?: string;
31
+ componentLibrary?: string;
32
+ };
33
+ metadata?: {
34
+ title?: string;
35
+ description?: string;
36
+ themeColor?: string;
37
+ language?: string;
38
+ };
39
+ confidence: {
40
+ colors: number;
41
+ fonts: number;
42
+ images: number;
43
+ overall: number;
44
+ };
45
+ }
46
+ export declare function extractBranding(page: Page): Promise<BrandingResult>;
@@ -0,0 +1,10 @@
1
+ export interface SummaryResult {
2
+ summary: string;
3
+ wordCount: number;
4
+ method: 'llm' | 'truncated';
5
+ model?: string;
6
+ }
7
+ export declare function extractSummary(markdown: string, options?: {
8
+ model?: string;
9
+ maxTokens?: number;
10
+ }): Promise<SummaryResult>;
@@ -9,12 +9,20 @@
9
9
  * array it replaces, so BFS (the default) has zero behavior change.
10
10
  */
11
11
  export type TraversalStrategy = 'BFS' | 'DFS' | 'BEST_FIRST';
12
+ /**
13
+ * Simple glob-like pattern matching against a URL's pathname.
14
+ * Supports `*` (matches any single path segment characters, not `/`)
15
+ * and `**` (matches anything including `/`).
16
+ */
17
+ export declare function matchesPattern(url: string, patterns: string[]): boolean;
12
18
  export declare class CrawlFrontier {
13
19
  private items;
14
20
  private readonly strategy;
15
21
  private readonly keywords;
16
22
  private readonly urlFilter;
17
- constructor(strategy: TraversalStrategy, seedUrls: string[], keywords?: string[], urlFilter?: string);
23
+ private readonly includePatterns;
24
+ private readonly excludePatterns;
25
+ constructor(strategy: TraversalStrategy, seedUrls: string[], keywords?: string[], urlFilter?: string, includePatterns?: string[], excludePatterns?: string[]);
18
26
  get length(): number;
19
27
  /**
20
28
  * P3b: sparse-parent boost — if the parent page had fewer than 5 interactive
@@ -57,3 +57,60 @@ export declare function partitionByChange(urls: string[], priorHashes: Map<strin
57
57
  toProcess: string[];
58
58
  toSkip: string[];
59
59
  };
60
+ /** A single line-level change in text content (markdown, html, summary). */
61
+ export interface TextChange {
62
+ type: 'added' | 'removed';
63
+ line: number;
64
+ content: string;
65
+ }
66
+ /** A change to a specific JSON path between two scraped JSON objects. */
67
+ export interface JsonChange {
68
+ path: string;
69
+ old: unknown;
70
+ new: unknown;
71
+ }
72
+ /** Set-based change for collections like links or images. */
73
+ export interface SetChange {
74
+ added: string[];
75
+ removed: string[];
76
+ }
77
+ /** Screenshot pixel-level change. changed is percentage (0-100). */
78
+ export interface ImageChange {
79
+ size: number;
80
+ changed: number;
81
+ mask?: string;
82
+ }
83
+ /** All possible diffs for a single monitor tick. */
84
+ export interface MonitorDiffs {
85
+ markdown?: TextChange[];
86
+ html?: TextChange[];
87
+ json?: JsonChange[];
88
+ summary?: TextChange[];
89
+ links?: SetChange;
90
+ images?: SetChange;
91
+ branding?: JsonChange[];
92
+ screenshot?: ImageChange;
93
+ }
94
+ /**
95
+ * Compute line-level text diffs between two strings.
96
+ * Returns TextChange[] showing exactly which lines were added or removed.
97
+ * No external dependencies — pure JS Myers-inspired algorithm.
98
+ */
99
+ export declare function computeTextDiffs(previous: string, current: string): TextChange[];
100
+ /**
101
+ * Compute set-based diffs for URL collections (links, images).
102
+ */
103
+ export declare function computeSetDiffs(previous: string[], current: string[]): SetChange;
104
+ /**
105
+ * Flatten a JSON object into dot-notation path/value pairs.
106
+ * Used to compute JsonChange[] between two JSON extraction results.
107
+ */
108
+ export declare function flattenJson(obj: unknown, prefix?: string): Record<string, unknown>;
109
+ /**
110
+ * Compute JSON path-level diffs between two extraction results.
111
+ */
112
+ export declare function computeJsonDiffs(previous: unknown, current: unknown): JsonChange[];
113
+ /**
114
+ * Count total number of changes across all diff types.
115
+ */
116
+ export declare function countMonitorDiffs(diffs: MonitorDiffs): number;
package/types/index.d.ts CHANGED
@@ -3,7 +3,7 @@ export * from './visual-diff-verdict.js';
3
3
  export * from './auth.js';
4
4
  export * from './screen-reader.js';
5
5
  export * from './job-queue.js';
6
- export { startSqsWorker } from './sqs-queue.js';
6
+ export { startSqsWorker, getDlqStats, redriveFromDlq } from './sqs-queue.js';
7
7
  export * from './session-capture.js';
8
8
  export * from './session-export.js';
9
9
  export * from './test-executor.js';
@@ -37,11 +37,15 @@ export interface ModalObservation {
37
37
  title: string;
38
38
  content: string;
39
39
  fields: string[];
40
+ stateId?: string;
41
+ domSnapshot?: string;
40
42
  }
41
43
  export interface ConditionalReveal {
42
44
  triggerElement: string;
43
45
  triggerValue: string;
44
46
  revealedText: string;
47
+ stateId?: string;
48
+ domSnapshot?: string;
45
49
  }
46
50
  export interface TooltipCapture {
47
51
  triggerLabel: string;
@@ -117,7 +121,7 @@ export declare function probeAllScreens(page: any, tenantId: string, projectId:
117
121
  url: string;
118
122
  screenId?: string;
119
123
  elements?: any[];
120
- }>, onProgress?: (pct: number, step: string) => Promise<void>): Promise<void>;
124
+ }>, onProgress?: (pct: number, step: string) => Promise<void>, jobId?: string): Promise<void>;
121
125
  export interface FocusableElement {
122
126
  tag: string;
123
127
  role: string | null;
@@ -67,6 +67,52 @@ export interface PlatformJobConfig {
67
67
  ssoScope?: string;
68
68
  /** HTTP/HTTPS/SOCKS5 proxy URL — routes browser through this proxy for bot-protected sites. */
69
69
  proxyUrl?: string;
70
+ /** HTML extraction mode. 'normal'=raw, 'reader'=Readability article, 'prune'=strip nav/ads. Default: 'normal'. */
71
+ htmlMode?: 'normal' | 'reader' | 'prune';
72
+ /** Per-request screenshot dimensions and quality. Defaults: 1440×900, quality 80. */
73
+ screenshotConfig?: {
74
+ fullPage?: boolean;
75
+ width?: number;
76
+ height?: number;
77
+ quality?: number;
78
+ };
79
+ /** Enable/disable Playwright stealth plugin. Default: true (existing behaviour). */
80
+ stealth?: boolean;
81
+ /** Mock mode — intercept all navigation and return synthetic content. Good for CI. */
82
+ mock?: boolean | {
83
+ minKb?: number;
84
+ maxKb?: number;
85
+ minSleep?: number;
86
+ maxSleep?: number;
87
+ };
88
+ /** MIME type allowlist — crawler skips URLs returning disallowed content types. */
89
+ allowedTypes?: string[];
90
+ /** Glob-style include patterns for URL paths (e.g. '/blog/*'). Only matching paths crawled. */
91
+ includePatterns?: string[];
92
+ /** Glob-style exclude patterns for URL paths (e.g. '/admin/*'). Matching paths skipped. */
93
+ excludePatterns?: string[];
94
+ /** Allow crawler to follow links to external domains. Default: false. */
95
+ allowExternal?: boolean;
96
+ /** Requested output formats for a single-pass multi-format extraction. */
97
+ requestedFormats?: Array<'markdown' | 'html' | 'links' | 'images' | 'summary' | 'json' | 'branding' | 'screenshot'>;
98
+ /** LLM chunker configuration for AI extraction passes. */
99
+ chunkerConfig?: {
100
+ size?: number | 'dynamic';
101
+ overlap?: number;
102
+ };
103
+ /** ISO-3166-1 alpha-2 country code for geo-targeted proxy routing (e.g. 'us', 'gb'). */
104
+ proxyCountry?: string;
105
+ /** When true, after the primary crawl auto-enqueue one CRAWL job per credentialProfile. */
106
+ crawlAllRoles?: boolean;
107
+ /** Decrypted credential profiles used when crawlAllRoles is true. */
108
+ credentialProfiles?: Array<{
109
+ id: string;
110
+ name: string;
111
+ username: string;
112
+ password: string;
113
+ }>;
114
+ /** Set on role-crawl sub-jobs — profile name tagged on discovered screens via roleAccess column. */
115
+ roleProfileName?: string;
70
116
  }
71
117
  type ClaimedJob = {
72
118
  id: string;
@@ -95,6 +141,13 @@ export declare function requeueStaleRunningJobs(): Promise<void>;
95
141
  * Safe to call from multiple replicas simultaneously (each uses its own WORKER_ID).
96
142
  */
97
143
  export declare function requeueOrphanedJobs(): Promise<void>;
144
+ /** Dispatches a claimed job to its type's processor. Last-resort safety net — every
145
+ * process*Job function already catches and records its own failures. */
146
+ export declare function dispatchClaimedJob(job: ClaimedJob): Promise<void>;
147
+ /** Re-promotes FAILED jobs to QUEUED if they are eligible for retry based on per-type limits. */
148
+ export declare function retryEligibleFailedJobs(): Promise<void>;
149
+ /** Promotes FAILED jobs to DLQ if they exceed their per-type retry limits. */
150
+ export declare function promoteToDeadLetterQueue(): Promise<void>;
98
151
  /**
99
152
  * Starts the durable job worker poll loop. This is what makes job processing
100
153
  * survive a restart — enqueueJob()'s immediate same-process claim attempt is
@@ -1,2 +1,13 @@
1
- export declare function sendJobToSqs(jobId: string, tenantId: string, config: any): Promise<void>;
1
+ export declare function sendJobToSqs(jobId: string): Promise<void>;
2
+ /** Returns the approximate number of messages in the SQS DLQ. */
3
+ export declare function getDlqStats(): Promise<{
4
+ approximateMessageCount: number;
5
+ dlqUrl: string | null;
6
+ }>;
7
+ /**
8
+ * Redrive up to `maxMessages` messages from the SQS DLQ back to the main queue.
9
+ * Each message is received from the DLQ, re-sent to the main queue, then deleted
10
+ * from the DLQ. Returns the number of messages successfully redriven.
11
+ */
12
+ export declare function redriveFromDlq(maxMessages?: number): Promise<number>;
2
13
  export declare function startSqsWorker(): Promise<void>;