linkedin-secret-sauce 0.12.0 → 0.12.2

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 (44) hide show
  1. package/README.md +50 -21
  2. package/dist/cosiall-client.d.ts +1 -1
  3. package/dist/cosiall-client.js +1 -1
  4. package/dist/enrichment/index.d.ts +3 -3
  5. package/dist/enrichment/index.js +19 -2
  6. package/dist/enrichment/matching.d.ts +29 -9
  7. package/dist/enrichment/matching.js +545 -142
  8. package/dist/enrichment/providers/bounceban.d.ts +82 -0
  9. package/dist/enrichment/providers/bounceban.js +447 -0
  10. package/dist/enrichment/providers/bouncer.d.ts +1 -1
  11. package/dist/enrichment/providers/bouncer.js +19 -21
  12. package/dist/enrichment/providers/construct.d.ts +1 -1
  13. package/dist/enrichment/providers/construct.js +22 -38
  14. package/dist/enrichment/providers/cosiall.d.ts +27 -0
  15. package/dist/enrichment/providers/cosiall.js +109 -0
  16. package/dist/enrichment/providers/dropcontact.d.ts +15 -9
  17. package/dist/enrichment/providers/dropcontact.js +188 -19
  18. package/dist/enrichment/providers/hunter.d.ts +8 -1
  19. package/dist/enrichment/providers/hunter.js +52 -28
  20. package/dist/enrichment/providers/index.d.ts +10 -7
  21. package/dist/enrichment/providers/index.js +12 -1
  22. package/dist/enrichment/providers/ldd.d.ts +1 -10
  23. package/dist/enrichment/providers/ldd.js +20 -97
  24. package/dist/enrichment/providers/smartprospect.js +28 -48
  25. package/dist/enrichment/providers/snovio.d.ts +1 -1
  26. package/dist/enrichment/providers/snovio.js +29 -31
  27. package/dist/enrichment/providers/trykitt.d.ts +63 -0
  28. package/dist/enrichment/providers/trykitt.js +210 -0
  29. package/dist/enrichment/types.d.ts +234 -17
  30. package/dist/enrichment/types.js +60 -48
  31. package/dist/enrichment/utils/candidate-parser.d.ts +107 -0
  32. package/dist/enrichment/utils/candidate-parser.js +173 -0
  33. package/dist/enrichment/utils/noop-provider.d.ts +39 -0
  34. package/dist/enrichment/utils/noop-provider.js +37 -0
  35. package/dist/enrichment/utils/rate-limiter.d.ts +103 -0
  36. package/dist/enrichment/utils/rate-limiter.js +204 -0
  37. package/dist/enrichment/utils/validation.d.ts +75 -3
  38. package/dist/enrichment/utils/validation.js +164 -11
  39. package/dist/linkedin-api.d.ts +40 -1
  40. package/dist/linkedin-api.js +160 -27
  41. package/dist/types.d.ts +50 -1
  42. package/dist/utils/lru-cache.d.ts +105 -0
  43. package/dist/utils/lru-cache.js +175 -0
  44. package/package.json +25 -26
@@ -8,27 +8,33 @@
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
9
  exports.SMARTPROSPECT_SUB_INDUSTRIES = exports.PROVIDER_COSTS = exports.DEFAULT_PROVIDER_ORDER = void 0;
10
10
  /**
11
- * Default provider order - 2-Phase Strategy
11
+ * Default provider order - 3-Phase Strategy
12
12
  *
13
13
  * PHASE 1 - Free lookups (run in parallel):
14
14
  * - ldd: LinkedIn Data Dump - real verified emails (FREE with subscription)
15
15
  * - smartprospect: SmartLead API - real verified emails (FREE with subscription)
16
+ * - cosiall: Cosiall Profile Emails - emails from LinkedIn profiles (FREE)
17
+ * - trykitt: TryKitt.ai - AI email finder (FREE for individuals, unlimited)
18
+ *
19
+ * PHASE 2 - Pattern guessing + verification (if Phase 1 < 80% confidence):
16
20
  * - construct: Pattern guessing + MX check (FREE)
21
+ * - bounceban: BounceBan catch-all verification (FREE single, $0.003/bulk)
17
22
  *
18
- * PHASE 2 - Paid verification/finding (only if Phase 1 inconclusive):
19
- * - bouncer: SMTP verify constructed emails ($0.006/email)
20
- * - snovio: Email finder for catch-all domains ($0.02/email)
21
- * - hunter: Hunter.io fallback ($0.005/email)
23
+ * PHASE 3 - Paid finders (only if Phase 2 inconclusive):
24
+ * - hunter: Hunter.io email finder ($0.005/email)
25
+ * - snovio: Snov.io email finder ($0.02/email)
22
26
  *
23
- * Note: dropcontact available but not in default order (expensive at $0.01)
27
+ * Note: bouncer, dropcontact available but not in default order
24
28
  */
25
29
  exports.DEFAULT_PROVIDER_ORDER = [
26
30
  "ldd",
27
31
  "smartprospect",
32
+ "cosiall",
33
+ "trykitt",
28
34
  "construct",
29
- "bouncer",
30
- "snovio",
35
+ "bounceban",
31
36
  "hunter",
37
+ "snovio",
32
38
  ];
33
39
  /**
34
40
  * Provider costs in USD per lookup
@@ -36,7 +42,10 @@ exports.DEFAULT_PROVIDER_ORDER = [
36
42
  * Costs based on 2025 pricing:
37
43
  * - ldd: FREE (subscription-based)
38
44
  * - smartprospect: FREE (included in SmartLead subscription)
45
+ * - cosiall: FREE (uses global Cosiall config)
46
+ * - trykitt: FREE (unlimited for individuals, $0.005/email high volume)
39
47
  * - construct: FREE (pattern guessing + MX check)
48
+ * - bounceban: FREE single / $0.003/email bulk (catch-all specialist)
40
49
  * - bouncer: $0.006/email (SMTP verification, 99%+ accuracy)
41
50
  * - snovio: $0.02/email (email finding + verification)
42
51
  * - hunter: $0.005/email
@@ -46,6 +55,9 @@ exports.PROVIDER_COSTS = {
46
55
  construct: 0,
47
56
  ldd: 0,
48
57
  smartprospect: 0,
58
+ cosiall: 0,
59
+ trykitt: 0, // FREE for individuals
60
+ bounceban: 0.003, // FREE single, $0.003 bulk
49
61
  hunter: 0.005,
50
62
  dropcontact: 0.01,
51
63
  bouncer: 0.006,
@@ -57,46 +69,46 @@ exports.PROVIDER_COSTS = {
57
69
  */
58
70
  exports.SMARTPROSPECT_SUB_INDUSTRIES = [
59
71
  // Software & Internet
60
- 'Internet',
61
- 'Information Technology and Services',
62
- 'Information Services',
63
- 'Computer Software',
64
- 'Computer & Network Security',
65
- 'Computer Games',
72
+ "Internet",
73
+ "Information Technology and Services",
74
+ "Information Services",
75
+ "Computer Software",
76
+ "Computer & Network Security",
77
+ "Computer Games",
66
78
  // Real Estate & Construction
67
- 'Glass, Ceramics & Concrete',
68
- 'Construction',
69
- 'Commercial Real Estate',
70
- 'Civil Engineering',
71
- 'Building Materials',
72
- 'Architecture & Planning',
79
+ "Glass, Ceramics & Concrete",
80
+ "Construction",
81
+ "Commercial Real Estate",
82
+ "Civil Engineering",
83
+ "Building Materials",
84
+ "Architecture & Planning",
73
85
  // Business Services
74
- 'Writing and Editing',
75
- 'Translation and Localization',
76
- 'Think Tanks',
77
- 'Staffing and Recruiting',
78
- 'Security and Investigations',
79
- 'Public Safety',
80
- 'Public Relations and Communications',
81
- 'Program Development',
82
- 'Professional Training & Coaching',
83
- 'Market Research',
84
- 'Marketing and Advertising',
85
- 'Management Consulting',
86
- 'Legal Services',
87
- 'Law Practice',
88
- 'Law Enforcement',
89
- 'International Trade and Development',
90
- 'Import and Export',
91
- 'Human Resources',
92
- 'Graphic Design',
93
- 'Facilities Services',
94
- 'Executive Office',
95
- 'Events Services',
96
- 'Environmental Services',
97
- 'Design',
98
- 'Business Supplies and Equipment',
99
- 'Animation',
100
- 'Alternative Dispute Resolution',
101
- 'Outsourcing/Offshoring',
86
+ "Writing and Editing",
87
+ "Translation and Localization",
88
+ "Think Tanks",
89
+ "Staffing and Recruiting",
90
+ "Security and Investigations",
91
+ "Public Safety",
92
+ "Public Relations and Communications",
93
+ "Program Development",
94
+ "Professional Training & Coaching",
95
+ "Market Research",
96
+ "Marketing and Advertising",
97
+ "Management Consulting",
98
+ "Legal Services",
99
+ "Law Practice",
100
+ "Law Enforcement",
101
+ "International Trade and Development",
102
+ "Import and Export",
103
+ "Human Resources",
104
+ "Graphic Design",
105
+ "Facilities Services",
106
+ "Executive Office",
107
+ "Events Services",
108
+ "Environmental Services",
109
+ "Design",
110
+ "Business Supplies and Equipment",
111
+ "Animation",
112
+ "Alternative Dispute Resolution",
113
+ "Outsourcing/Offshoring",
102
114
  ];
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Shared Candidate Parsing Utilities
3
+ *
4
+ * Extracts common fields from EnrichmentCandidate objects.
5
+ * Reduces code duplication across providers by centralizing field extraction logic.
6
+ */
7
+ import type { EnrichmentCandidate } from "../types";
8
+ /**
9
+ * Parsed name components from a candidate
10
+ */
11
+ export interface ParsedName {
12
+ firstName: string;
13
+ lastName: string;
14
+ fullName: string;
15
+ }
16
+ /**
17
+ * Parsed company/domain info from a candidate
18
+ */
19
+ export interface ParsedCompany {
20
+ company: string | null;
21
+ domain: string | null;
22
+ }
23
+ /**
24
+ * Parsed LinkedIn identifiers from a candidate
25
+ */
26
+ export interface ParsedLinkedIn {
27
+ /** LinkedIn username/handle (e.g., "john-doe") */
28
+ username: string | null;
29
+ /** LinkedIn profile URL */
30
+ url: string | null;
31
+ /** Numeric LinkedIn ID (stable, never changes) */
32
+ numericId: string | null;
33
+ }
34
+ /**
35
+ * Full parsed candidate with all extracted fields
36
+ */
37
+ export interface ParsedCandidate {
38
+ name: ParsedName;
39
+ company: ParsedCompany;
40
+ linkedin: ParsedLinkedIn;
41
+ title: string | null;
42
+ }
43
+ /**
44
+ * Extract name components from a candidate
45
+ *
46
+ * Checks multiple field naming conventions:
47
+ * - firstName, first_name, first
48
+ * - lastName, last_name, last
49
+ * - name, fullName, full_name (split on space)
50
+ */
51
+ export declare function extractName(candidate: EnrichmentCandidate): ParsedName;
52
+ /**
53
+ * Extract company and domain from a candidate
54
+ *
55
+ * Checks multiple field naming conventions:
56
+ * - company, currentCompany, organization, org
57
+ * - domain, companyDomain, company_domain
58
+ */
59
+ export declare function extractCompany(candidate: EnrichmentCandidate): ParsedCompany;
60
+ /**
61
+ * Extract LinkedIn username from a URL
62
+ *
63
+ * Handles various URL formats:
64
+ * - https://linkedin.com/in/john-doe
65
+ * - https://www.linkedin.com/in/john-doe/
66
+ * - linkedin.com/in/john-doe?param=value
67
+ */
68
+ export declare function extractLinkedInUsernameFromUrl(url: string | undefined | null): string | null;
69
+ /**
70
+ * Extract numeric LinkedIn ID from various formats
71
+ *
72
+ * Handles:
73
+ * - Direct numeric ID: "307567"
74
+ * - URN format: "urn:li:member:307567"
75
+ * - Sales profile URN: "urn:li:fs_salesProfile:(307567,...)"
76
+ */
77
+ export declare function extractNumericLinkedInId(input: string | undefined | null): string | null;
78
+ /**
79
+ * Extract LinkedIn identifiers from a candidate
80
+ *
81
+ * Returns username, URL, and numeric ID (if available).
82
+ * Numeric ID is preferred for lookups as it never changes.
83
+ */
84
+ export declare function extractLinkedIn(candidate: EnrichmentCandidate): ParsedLinkedIn;
85
+ /**
86
+ * Extract job title from a candidate
87
+ */
88
+ export declare function extractTitle(candidate: EnrichmentCandidate): string | null;
89
+ /**
90
+ * Parse all common fields from a candidate
91
+ *
92
+ * Use this for a complete extraction of all fields at once.
93
+ * For partial extraction, use the individual functions.
94
+ */
95
+ export declare function parseCandidate(candidate: EnrichmentCandidate): ParsedCandidate;
96
+ /**
97
+ * Build a LinkedIn URL from a username/handle
98
+ */
99
+ export declare function buildLinkedInUrl(username: string): string;
100
+ /**
101
+ * Check if a candidate has minimum required fields for email lookup
102
+ *
103
+ * @param candidate - The candidate to check
104
+ * @param requireDomain - Whether domain is required (default: true)
105
+ * @returns true if candidate has at least name and optionally domain
106
+ */
107
+ export declare function hasMinimumFields(candidate: EnrichmentCandidate, requireDomain?: boolean): boolean;
@@ -0,0 +1,173 @@
1
+ "use strict";
2
+ /**
3
+ * Shared Candidate Parsing Utilities
4
+ *
5
+ * Extracts common fields from EnrichmentCandidate objects.
6
+ * Reduces code duplication across providers by centralizing field extraction logic.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.extractName = extractName;
10
+ exports.extractCompany = extractCompany;
11
+ exports.extractLinkedInUsernameFromUrl = extractLinkedInUsernameFromUrl;
12
+ exports.extractNumericLinkedInId = extractNumericLinkedInId;
13
+ exports.extractLinkedIn = extractLinkedIn;
14
+ exports.extractTitle = extractTitle;
15
+ exports.parseCandidate = parseCandidate;
16
+ exports.buildLinkedInUrl = buildLinkedInUrl;
17
+ exports.hasMinimumFields = hasMinimumFields;
18
+ /**
19
+ * Extract name components from a candidate
20
+ *
21
+ * Checks multiple field naming conventions:
22
+ * - firstName, first_name, first
23
+ * - lastName, last_name, last
24
+ * - name, fullName, full_name (split on space)
25
+ */
26
+ function extractName(candidate) {
27
+ const rawName = candidate.name || candidate.fullName || candidate.full_name;
28
+ const firstName = candidate.firstName ||
29
+ candidate.first_name ||
30
+ candidate.first ||
31
+ rawName?.split(" ")?.[0] ||
32
+ "";
33
+ const lastName = candidate.lastName ||
34
+ candidate.last_name ||
35
+ candidate.last ||
36
+ rawName?.split(" ")?.slice(1).join(" ") ||
37
+ "";
38
+ const fullName = rawName || (firstName && lastName ? `${firstName} ${lastName}`.trim() : firstName);
39
+ return { firstName, lastName, fullName };
40
+ }
41
+ /**
42
+ * Extract company and domain from a candidate
43
+ *
44
+ * Checks multiple field naming conventions:
45
+ * - company, currentCompany, organization, org
46
+ * - domain, companyDomain, company_domain
47
+ */
48
+ function extractCompany(candidate) {
49
+ const company = candidate.company ||
50
+ candidate.currentCompany ||
51
+ candidate.organization ||
52
+ candidate.org ||
53
+ null;
54
+ const domain = candidate.domain ||
55
+ candidate.companyDomain ||
56
+ candidate.company_domain ||
57
+ null;
58
+ return { company, domain };
59
+ }
60
+ /**
61
+ * Extract LinkedIn username from a URL
62
+ *
63
+ * Handles various URL formats:
64
+ * - https://linkedin.com/in/john-doe
65
+ * - https://www.linkedin.com/in/john-doe/
66
+ * - linkedin.com/in/john-doe?param=value
67
+ */
68
+ function extractLinkedInUsernameFromUrl(url) {
69
+ if (!url)
70
+ return null;
71
+ const match = url.match(/linkedin\.com\/in\/([^\/\?]+)/i);
72
+ return match ? match[1] : null;
73
+ }
74
+ /**
75
+ * Extract numeric LinkedIn ID from various formats
76
+ *
77
+ * Handles:
78
+ * - Direct numeric ID: "307567"
79
+ * - URN format: "urn:li:member:307567"
80
+ * - Sales profile URN: "urn:li:fs_salesProfile:(307567,...)"
81
+ */
82
+ function extractNumericLinkedInId(input) {
83
+ if (!input)
84
+ return null;
85
+ const trimmed = input.trim();
86
+ // Direct numeric ID
87
+ if (/^\d+$/.test(trimmed)) {
88
+ return trimmed;
89
+ }
90
+ // URN format: urn:li:member:307567
91
+ const memberMatch = trimmed.match(/urn:li:member:(\d+)/i);
92
+ if (memberMatch) {
93
+ return memberMatch[1];
94
+ }
95
+ // Sales profile URN with numeric ID
96
+ const salesMatch = trimmed.match(/urn:li:fs_salesProfile:\((\d+),/i);
97
+ if (salesMatch) {
98
+ return salesMatch[1];
99
+ }
100
+ return null;
101
+ }
102
+ /**
103
+ * Extract LinkedIn identifiers from a candidate
104
+ *
105
+ * Returns username, URL, and numeric ID (if available).
106
+ * Numeric ID is preferred for lookups as it never changes.
107
+ */
108
+ function extractLinkedIn(candidate) {
109
+ // Extract URL
110
+ const url = candidate.linkedinUrl || candidate.linkedin_url || null;
111
+ // Extract username - try direct field first, then extract from URL
112
+ const username = candidate.linkedinUsername ||
113
+ candidate.linkedin_username ||
114
+ candidate.linkedinHandle ||
115
+ candidate.linkedin_handle ||
116
+ extractLinkedInUsernameFromUrl(url);
117
+ // Extract numeric ID - check multiple fields in order of preference
118
+ const numericId = extractNumericLinkedInId(candidate.numericLinkedInId) ||
119
+ extractNumericLinkedInId(candidate.numeric_linkedin_id) ||
120
+ extractNumericLinkedInId(candidate.objectUrn) ||
121
+ extractNumericLinkedInId(candidate.object_urn) ||
122
+ extractNumericLinkedInId(candidate.linkedinId) ||
123
+ extractNumericLinkedInId(candidate.linkedin_id);
124
+ return { username, url, numericId };
125
+ }
126
+ /**
127
+ * Extract job title from a candidate
128
+ */
129
+ function extractTitle(candidate) {
130
+ return (candidate.title ||
131
+ candidate.currentRole ||
132
+ candidate.current_role ||
133
+ null);
134
+ }
135
+ /**
136
+ * Parse all common fields from a candidate
137
+ *
138
+ * Use this for a complete extraction of all fields at once.
139
+ * For partial extraction, use the individual functions.
140
+ */
141
+ function parseCandidate(candidate) {
142
+ return {
143
+ name: extractName(candidate),
144
+ company: extractCompany(candidate),
145
+ linkedin: extractLinkedIn(candidate),
146
+ title: extractTitle(candidate),
147
+ };
148
+ }
149
+ /**
150
+ * Build a LinkedIn URL from a username/handle
151
+ */
152
+ function buildLinkedInUrl(username) {
153
+ return `https://www.linkedin.com/in/${username}`;
154
+ }
155
+ /**
156
+ * Check if a candidate has minimum required fields for email lookup
157
+ *
158
+ * @param candidate - The candidate to check
159
+ * @param requireDomain - Whether domain is required (default: true)
160
+ * @returns true if candidate has at least name and optionally domain
161
+ */
162
+ function hasMinimumFields(candidate, requireDomain = true) {
163
+ const { name, company } = parseCandidate(candidate);
164
+ // Must have at least first name
165
+ if (!name.firstName) {
166
+ return false;
167
+ }
168
+ // Domain required for most providers
169
+ if (requireDomain && !company.domain) {
170
+ return false;
171
+ }
172
+ return true;
173
+ }
@@ -0,0 +1,39 @@
1
+ /**
2
+ * No-Op Provider Factory
3
+ *
4
+ * Creates a no-op (no operation) provider function that returns null.
5
+ * Used when a provider is not configured (missing API key, etc.)
6
+ *
7
+ * This reduces code duplication across providers that need to return
8
+ * a disabled/unconfigured provider.
9
+ */
10
+ import type { ProviderResult, ProviderName } from "../types";
11
+ /**
12
+ * Provider function type with optional name property
13
+ */
14
+ type NoOpProviderFunc = {
15
+ (): Promise<ProviderResult | null>;
16
+ __name?: string;
17
+ };
18
+ /**
19
+ * Create a no-op provider function with the specified name
20
+ *
21
+ * The returned function:
22
+ * - Always returns null (no email found)
23
+ * - Has a `__name` property for identification in the orchestrator
24
+ *
25
+ * @param name - The provider name for identification
26
+ * @returns A no-op provider function
27
+ *
28
+ * @example
29
+ * ```typescript
30
+ * export function createHunterProvider(config: HunterConfig) {
31
+ * if (!config.apiKey) {
32
+ * return createNoOpProvider("hunter");
33
+ * }
34
+ * // ... actual implementation
35
+ * }
36
+ * ```
37
+ */
38
+ export declare function createNoOpProvider(name: ProviderName): NoOpProviderFunc;
39
+ export {};
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ /**
3
+ * No-Op Provider Factory
4
+ *
5
+ * Creates a no-op (no operation) provider function that returns null.
6
+ * Used when a provider is not configured (missing API key, etc.)
7
+ *
8
+ * This reduces code duplication across providers that need to return
9
+ * a disabled/unconfigured provider.
10
+ */
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.createNoOpProvider = createNoOpProvider;
13
+ /**
14
+ * Create a no-op provider function with the specified name
15
+ *
16
+ * The returned function:
17
+ * - Always returns null (no email found)
18
+ * - Has a `__name` property for identification in the orchestrator
19
+ *
20
+ * @param name - The provider name for identification
21
+ * @returns A no-op provider function
22
+ *
23
+ * @example
24
+ * ```typescript
25
+ * export function createHunterProvider(config: HunterConfig) {
26
+ * if (!config.apiKey) {
27
+ * return createNoOpProvider("hunter");
28
+ * }
29
+ * // ... actual implementation
30
+ * }
31
+ * ```
32
+ */
33
+ function createNoOpProvider(name) {
34
+ const noopProvider = async () => null;
35
+ noopProvider.__name = name;
36
+ return noopProvider;
37
+ }
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Rate Limiter for Email Enrichment Providers
3
+ *
4
+ * Tracks request rates per provider and enforces limits to avoid API throttling.
5
+ * Uses a sliding window approach to track requests.
6
+ */
7
+ import type { ProviderName } from "../types";
8
+ /**
9
+ * Rate limit configuration for a provider
10
+ */
11
+ export interface RateLimitConfig {
12
+ /** Maximum requests per window */
13
+ maxRequests: number;
14
+ /** Window size in milliseconds */
15
+ windowMs: number;
16
+ /** Optional minimum delay between requests in milliseconds */
17
+ minDelayMs?: number;
18
+ }
19
+ /**
20
+ * Rate limit status for a provider
21
+ */
22
+ export interface RateLimitStatus {
23
+ /** Whether the provider is currently rate limited */
24
+ isLimited: boolean;
25
+ /** Number of requests in current window */
26
+ requestsInWindow: number;
27
+ /** Maximum allowed requests */
28
+ maxRequests: number;
29
+ /** Time until rate limit resets (ms) */
30
+ resetInMs: number;
31
+ /** Recommended delay before next request (ms) */
32
+ recommendedDelayMs: number;
33
+ }
34
+ /**
35
+ * Default rate limits per provider
36
+ *
37
+ * Based on documented API limits:
38
+ * - TryKitt: 2 req/sec, 120 req/min
39
+ * - Hunter: 10 req/sec
40
+ * - Snovio: 60 req/min
41
+ * - BounceBan: 10 req/sec
42
+ * - Bouncer: 10 req/sec
43
+ * - Dropcontact: 5 req/sec
44
+ * - SmartProspect: 2 req/sec
45
+ */
46
+ export declare const DEFAULT_RATE_LIMITS: Partial<Record<ProviderName, RateLimitConfig>>;
47
+ /**
48
+ * Rate limiter instance for tracking provider requests
49
+ */
50
+ export declare class RateLimiter {
51
+ private requests;
52
+ private configs;
53
+ private lastRequestTime;
54
+ constructor(customConfigs?: Partial<Record<ProviderName, RateLimitConfig>>);
55
+ /**
56
+ * Record a request for a provider
57
+ */
58
+ recordRequest(provider: ProviderName): void;
59
+ /**
60
+ * Check if a provider is rate limited
61
+ */
62
+ isRateLimited(provider: ProviderName): boolean;
63
+ /**
64
+ * Get rate limit status for a provider
65
+ */
66
+ getStatus(provider: ProviderName): RateLimitStatus;
67
+ /**
68
+ * Wait until rate limit allows a request
69
+ *
70
+ * @returns Promise that resolves when it's safe to make a request
71
+ */
72
+ waitForSlot(provider: ProviderName): Promise<void>;
73
+ /**
74
+ * Execute a function with rate limiting
75
+ *
76
+ * Automatically waits for rate limit slot and records the request.
77
+ */
78
+ execute<T>(provider: ProviderName, fn: () => Promise<T>): Promise<T>;
79
+ /**
80
+ * Get all provider statuses
81
+ */
82
+ getAllStatuses(): Map<ProviderName, RateLimitStatus>;
83
+ /**
84
+ * Reset rate limit tracking for a provider
85
+ */
86
+ reset(provider: ProviderName): void;
87
+ /**
88
+ * Reset all rate limit tracking
89
+ */
90
+ resetAll(): void;
91
+ /**
92
+ * Prune old records outside the window
93
+ */
94
+ private pruneRecords;
95
+ }
96
+ /**
97
+ * Get the global rate limiter instance
98
+ */
99
+ export declare function getRateLimiter(): RateLimiter;
100
+ /**
101
+ * Create a new rate limiter with custom configs
102
+ */
103
+ export declare function createRateLimiter(configs?: Partial<Record<ProviderName, RateLimitConfig>>): RateLimiter;