linkedin-secret-sauce 0.3.29 → 0.5.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.
- package/dist/cookie-pool.d.ts +1 -1
- package/dist/cookie-pool.js +67 -35
- package/dist/cosiall-client.d.ts +20 -1
- package/dist/cosiall-client.js +48 -25
- package/dist/enrichment/index.d.ts +43 -0
- package/dist/enrichment/index.js +231 -0
- package/dist/enrichment/orchestrator.d.ts +31 -0
- package/dist/enrichment/orchestrator.js +218 -0
- package/dist/enrichment/providers/apollo.d.ts +11 -0
- package/dist/enrichment/providers/apollo.js +136 -0
- package/dist/enrichment/providers/construct.d.ts +11 -0
- package/dist/enrichment/providers/construct.js +107 -0
- package/dist/enrichment/providers/dropcontact.d.ts +16 -0
- package/dist/enrichment/providers/dropcontact.js +37 -0
- package/dist/enrichment/providers/hunter.d.ts +11 -0
- package/dist/enrichment/providers/hunter.js +162 -0
- package/dist/enrichment/providers/index.d.ts +9 -0
- package/dist/enrichment/providers/index.js +18 -0
- package/dist/enrichment/providers/ldd.d.ts +11 -0
- package/dist/enrichment/providers/ldd.js +110 -0
- package/dist/enrichment/providers/smartprospect.d.ts +11 -0
- package/dist/enrichment/providers/smartprospect.js +249 -0
- package/dist/enrichment/types.d.ts +329 -0
- package/dist/enrichment/types.js +31 -0
- package/dist/enrichment/utils/disposable-domains.d.ts +24 -0
- package/dist/enrichment/utils/disposable-domains.js +1011 -0
- package/dist/enrichment/utils/index.d.ts +6 -0
- package/dist/enrichment/utils/index.js +22 -0
- package/dist/enrichment/utils/personal-domains.d.ts +31 -0
- package/dist/enrichment/utils/personal-domains.js +95 -0
- package/dist/enrichment/utils/validation.d.ts +42 -0
- package/dist/enrichment/utils/validation.js +130 -0
- package/dist/enrichment/verification/index.d.ts +4 -0
- package/dist/enrichment/verification/index.js +8 -0
- package/dist/enrichment/verification/mx.d.ts +16 -0
- package/dist/enrichment/verification/mx.js +168 -0
- package/dist/http-client.d.ts +1 -1
- package/dist/http-client.js +146 -63
- package/dist/index.d.ts +17 -14
- package/dist/index.js +20 -1
- package/dist/linkedin-api.d.ts +97 -4
- package/dist/linkedin-api.js +416 -134
- package/dist/parsers/company-parser.d.ts +15 -1
- package/dist/parsers/company-parser.js +45 -17
- package/dist/parsers/profile-parser.d.ts +19 -1
- package/dist/parsers/profile-parser.js +131 -81
- package/dist/parsers/search-parser.d.ts +1 -1
- package/dist/parsers/search-parser.js +24 -11
- package/dist/utils/logger.d.ts +1 -1
- package/dist/utils/logger.js +28 -18
- package/dist/utils/search-encoder.d.ts +32 -1
- package/dist/utils/search-encoder.js +102 -58
- package/dist/utils/sentry.d.ts +1 -1
- package/dist/utils/sentry.js +56 -8
- package/package.json +1 -1
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* SmartProspect/Smartlead Provider
|
|
4
|
+
*
|
|
5
|
+
* Smartlead's prospect database - a REVERSE-ENGINEERED private API.
|
|
6
|
+
* Two-phase process: search (free) then fetch (costs credits).
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.createSmartProspectProvider = createSmartProspectProvider;
|
|
10
|
+
const DEFAULT_API_URL = 'https://prospect-api.smartlead.ai/api/search-email-leads';
|
|
11
|
+
/**
|
|
12
|
+
* Delay helper for retry logic
|
|
13
|
+
*/
|
|
14
|
+
async function delay(ms) {
|
|
15
|
+
return new Promise((r) => setTimeout(r, ms));
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* HTTP request with retry on 429 rate limit
|
|
19
|
+
*/
|
|
20
|
+
async function requestWithRetry(url, options, retries = 2, backoffMs = 300) {
|
|
21
|
+
let lastErr;
|
|
22
|
+
for (let i = 0; i <= retries; i++) {
|
|
23
|
+
try {
|
|
24
|
+
const res = await fetch(url, options);
|
|
25
|
+
// Retry on rate limit
|
|
26
|
+
if (res.status === 429 && i < retries) {
|
|
27
|
+
await delay(backoffMs * Math.pow(2, i));
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
if (!res.ok) {
|
|
31
|
+
const errorText = await res.text().catch(() => '');
|
|
32
|
+
throw new Error(`SmartProspect API error: ${res.status} - ${errorText}`);
|
|
33
|
+
}
|
|
34
|
+
return (await res.json());
|
|
35
|
+
}
|
|
36
|
+
catch (err) {
|
|
37
|
+
lastErr = err;
|
|
38
|
+
if (i < retries) {
|
|
39
|
+
await delay(backoffMs * Math.pow(2, i));
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
throw lastErr ?? new Error('smartprospect_http_error');
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Calculate match score between search input and contact result
|
|
48
|
+
*/
|
|
49
|
+
function calculateMatchScore(contact, searchName, company, title) {
|
|
50
|
+
let score = 0;
|
|
51
|
+
// Name match scoring (0-50 points)
|
|
52
|
+
const contactName = contact.fullName?.toLowerCase() || '';
|
|
53
|
+
const searchNameLower = searchName.toLowerCase();
|
|
54
|
+
if (contactName === searchNameLower) {
|
|
55
|
+
score += 50; // Exact match
|
|
56
|
+
}
|
|
57
|
+
else if (contactName.includes(searchNameLower) || searchNameLower.includes(contactName)) {
|
|
58
|
+
score += 30; // Partial match
|
|
59
|
+
}
|
|
60
|
+
// Company match scoring (0-30 points)
|
|
61
|
+
if (company) {
|
|
62
|
+
const contactCompany = contact.company?.name?.toLowerCase() || '';
|
|
63
|
+
const searchCompany = company.toLowerCase();
|
|
64
|
+
if (contactCompany === searchCompany) {
|
|
65
|
+
score += 30; // Exact match
|
|
66
|
+
}
|
|
67
|
+
else if (contactCompany.includes(searchCompany) || searchCompany.includes(contactCompany)) {
|
|
68
|
+
score += 15; // Partial match
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
// Title match scoring (0-10 points)
|
|
72
|
+
if (title) {
|
|
73
|
+
const contactTitle = contact.title?.toLowerCase() || '';
|
|
74
|
+
const searchTitle = title.toLowerCase();
|
|
75
|
+
if (contactTitle.includes(searchTitle) || searchTitle.includes(contactTitle)) {
|
|
76
|
+
score += 10;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
// Deliverability score bonus (0-10 points)
|
|
80
|
+
if (contact.emailDeliverability > 0) {
|
|
81
|
+
score += contact.emailDeliverability * 10;
|
|
82
|
+
}
|
|
83
|
+
return score;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Extract name components from candidate
|
|
87
|
+
*/
|
|
88
|
+
function extractNames(candidate) {
|
|
89
|
+
const firstName = candidate.firstName ||
|
|
90
|
+
candidate.first_name ||
|
|
91
|
+
candidate.first ||
|
|
92
|
+
candidate.name?.split(' ')?.[0] ||
|
|
93
|
+
'';
|
|
94
|
+
const lastName = candidate.lastName ||
|
|
95
|
+
candidate.last_name ||
|
|
96
|
+
candidate.last ||
|
|
97
|
+
candidate.name?.split(' ')?.slice(1).join(' ') ||
|
|
98
|
+
'';
|
|
99
|
+
return { firstName, lastName };
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Create the SmartProspect provider function
|
|
103
|
+
*/
|
|
104
|
+
function createSmartProspectProvider(config) {
|
|
105
|
+
const { apiToken, apiUrl = DEFAULT_API_URL } = config;
|
|
106
|
+
if (!apiToken) {
|
|
107
|
+
// Return a no-op provider if not configured
|
|
108
|
+
const noopProvider = async () => null;
|
|
109
|
+
noopProvider.__name = 'smartprospect';
|
|
110
|
+
return noopProvider;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Search for contacts matching filters (FREE - no credits used)
|
|
114
|
+
*/
|
|
115
|
+
async function searchContacts(filters) {
|
|
116
|
+
try {
|
|
117
|
+
const response = await requestWithRetry(`${apiUrl}/search-contacts`, {
|
|
118
|
+
method: 'POST',
|
|
119
|
+
headers: {
|
|
120
|
+
Authorization: `Bearer ${apiToken}`,
|
|
121
|
+
'Content-Type': 'application/json',
|
|
122
|
+
Accept: 'application/json',
|
|
123
|
+
},
|
|
124
|
+
body: JSON.stringify({
|
|
125
|
+
...filters,
|
|
126
|
+
dontDisplayOwnedContact: filters.dontDisplayOwnedContact ?? true,
|
|
127
|
+
limit: filters.limit ?? 10,
|
|
128
|
+
titleExactMatch: filters.titleExactMatch ?? false,
|
|
129
|
+
}),
|
|
130
|
+
});
|
|
131
|
+
return response;
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
return {
|
|
135
|
+
success: false,
|
|
136
|
+
message: 'Search failed',
|
|
137
|
+
data: { list: [], total_count: 0 },
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Fetch/enrich emails for specific contact IDs (COSTS CREDITS)
|
|
143
|
+
*/
|
|
144
|
+
async function fetchContacts(contactIds) {
|
|
145
|
+
try {
|
|
146
|
+
const response = await requestWithRetry(`${apiUrl}/fetch-contacts`, {
|
|
147
|
+
method: 'POST',
|
|
148
|
+
headers: {
|
|
149
|
+
Authorization: `Bearer ${apiToken}`,
|
|
150
|
+
'Content-Type': 'application/json',
|
|
151
|
+
Accept: 'application/json',
|
|
152
|
+
},
|
|
153
|
+
body: JSON.stringify({ contactIds }),
|
|
154
|
+
});
|
|
155
|
+
return response;
|
|
156
|
+
}
|
|
157
|
+
catch {
|
|
158
|
+
return {
|
|
159
|
+
success: false,
|
|
160
|
+
message: 'Fetch failed',
|
|
161
|
+
data: {
|
|
162
|
+
list: [],
|
|
163
|
+
total_count: 0,
|
|
164
|
+
metrics: {
|
|
165
|
+
totalContacts: 0,
|
|
166
|
+
totalEmails: 0,
|
|
167
|
+
noEmailFound: 0,
|
|
168
|
+
invalidEmails: 0,
|
|
169
|
+
catchAllEmails: 0,
|
|
170
|
+
verifiedEmails: 0,
|
|
171
|
+
completed: 0,
|
|
172
|
+
},
|
|
173
|
+
leads_found: 0,
|
|
174
|
+
email_fetched: 0,
|
|
175
|
+
verification_status_list: [],
|
|
176
|
+
},
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
async function fetchEmail(candidate) {
|
|
181
|
+
const { firstName, lastName } = extractNames(candidate);
|
|
182
|
+
if (!firstName) {
|
|
183
|
+
return null; // Minimum requirement
|
|
184
|
+
}
|
|
185
|
+
const fullName = `${firstName} ${lastName}`.trim();
|
|
186
|
+
const company = candidate.company || candidate.currentCompany || candidate.organization || undefined;
|
|
187
|
+
const title = candidate.title || candidate.currentRole || candidate.current_role || undefined;
|
|
188
|
+
const domain = candidate.domain || candidate.companyDomain || candidate.company_domain || undefined;
|
|
189
|
+
// Build search filters
|
|
190
|
+
const filters = {
|
|
191
|
+
name: [fullName],
|
|
192
|
+
limit: 5, // Get top 5 to find best match
|
|
193
|
+
};
|
|
194
|
+
if (company) {
|
|
195
|
+
filters.company = [company];
|
|
196
|
+
}
|
|
197
|
+
if (domain) {
|
|
198
|
+
filters.domain = [domain];
|
|
199
|
+
}
|
|
200
|
+
// Step 1: Search for contacts (FREE)
|
|
201
|
+
const searchResult = await searchContacts(filters);
|
|
202
|
+
if (!searchResult.success || searchResult.data.list.length === 0) {
|
|
203
|
+
return null;
|
|
204
|
+
}
|
|
205
|
+
// Find best match by scoring
|
|
206
|
+
const matches = searchResult.data.list;
|
|
207
|
+
let bestMatch = null;
|
|
208
|
+
let bestScore = 0;
|
|
209
|
+
for (const contact of matches) {
|
|
210
|
+
const score = calculateMatchScore(contact, fullName, company, title);
|
|
211
|
+
if (score > bestScore) {
|
|
212
|
+
bestScore = score;
|
|
213
|
+
bestMatch = contact;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
if (!bestMatch) {
|
|
217
|
+
return null;
|
|
218
|
+
}
|
|
219
|
+
// Step 2: Fetch email for best match (COSTS CREDITS)
|
|
220
|
+
const fetchResult = await fetchContacts([bestMatch.id]);
|
|
221
|
+
if (!fetchResult.success || fetchResult.data.list.length === 0) {
|
|
222
|
+
return null;
|
|
223
|
+
}
|
|
224
|
+
const enrichedContact = fetchResult.data.list[0];
|
|
225
|
+
const email = enrichedContact?.email;
|
|
226
|
+
if (!email) {
|
|
227
|
+
return null;
|
|
228
|
+
}
|
|
229
|
+
// Calculate verification confidence
|
|
230
|
+
const isVerified = enrichedContact.verificationStatus === 'valid';
|
|
231
|
+
const isCatchAll = enrichedContact.verificationStatus === 'catch_all';
|
|
232
|
+
const deliverability = enrichedContact.emailDeliverability || 0;
|
|
233
|
+
let confidence = deliverability * 100;
|
|
234
|
+
if (isVerified) {
|
|
235
|
+
confidence = Math.max(confidence, 90);
|
|
236
|
+
}
|
|
237
|
+
else if (isCatchAll) {
|
|
238
|
+
confidence = Math.min(confidence, 75);
|
|
239
|
+
}
|
|
240
|
+
return {
|
|
241
|
+
email,
|
|
242
|
+
verified: isVerified || isCatchAll, // Accept catch-all as semi-verified
|
|
243
|
+
score: confidence,
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
// Mark provider name for orchestrator
|
|
247
|
+
fetchEmail.__name = 'smartprospect';
|
|
248
|
+
return fetchEmail;
|
|
249
|
+
}
|
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Email Enrichment Types
|
|
3
|
+
*
|
|
4
|
+
* Core types for the email enrichment module. These types are designed to be
|
|
5
|
+
* stateless - consumers pass their own API keys and configuration.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Canonical email result returned by enrichment operations
|
|
9
|
+
*/
|
|
10
|
+
export interface CanonicalEmail {
|
|
11
|
+
/** The found business email, or null if not found */
|
|
12
|
+
business_email: string | null;
|
|
13
|
+
/** Which provider found the email */
|
|
14
|
+
business_email_source: string | null;
|
|
15
|
+
/** Whether the email was verified */
|
|
16
|
+
business_email_verified: boolean;
|
|
17
|
+
/** Confidence score (0-100) */
|
|
18
|
+
business_email_confidence?: number;
|
|
19
|
+
/** ISO timestamp of when the check was performed */
|
|
20
|
+
last_checked_at: string;
|
|
21
|
+
/** Status for debugging */
|
|
22
|
+
status?: string;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Raw result from a provider before normalization
|
|
26
|
+
*/
|
|
27
|
+
export interface ProviderResult {
|
|
28
|
+
email?: string | null;
|
|
29
|
+
verified?: boolean;
|
|
30
|
+
score?: number;
|
|
31
|
+
confidence?: number;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Provider function signature
|
|
35
|
+
*/
|
|
36
|
+
export type ProviderFunc = (candidate: EnrichmentCandidate) => Promise<ProviderResult | null>;
|
|
37
|
+
/**
|
|
38
|
+
* Input candidate for enrichment - flexible to accept various naming conventions
|
|
39
|
+
*/
|
|
40
|
+
export interface EnrichmentCandidate {
|
|
41
|
+
firstName?: string;
|
|
42
|
+
first_name?: string;
|
|
43
|
+
first?: string;
|
|
44
|
+
lastName?: string;
|
|
45
|
+
last_name?: string;
|
|
46
|
+
last?: string;
|
|
47
|
+
name?: string;
|
|
48
|
+
fullName?: string;
|
|
49
|
+
full_name?: string;
|
|
50
|
+
company?: string;
|
|
51
|
+
currentCompany?: string;
|
|
52
|
+
organization?: string;
|
|
53
|
+
org?: string;
|
|
54
|
+
domain?: string;
|
|
55
|
+
companyDomain?: string;
|
|
56
|
+
company_domain?: string;
|
|
57
|
+
linkedinUsername?: string;
|
|
58
|
+
linkedin_username?: string;
|
|
59
|
+
linkedinUrl?: string;
|
|
60
|
+
linkedin_url?: string;
|
|
61
|
+
linkedinId?: string;
|
|
62
|
+
linkedin_id?: string;
|
|
63
|
+
title?: string;
|
|
64
|
+
currentRole?: string;
|
|
65
|
+
current_role?: string;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Hunter.io provider configuration
|
|
69
|
+
*/
|
|
70
|
+
export interface HunterConfig {
|
|
71
|
+
apiKey: string;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Apollo.io provider configuration
|
|
75
|
+
*/
|
|
76
|
+
export interface ApolloConfig {
|
|
77
|
+
apiKey: string;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* SmartProspect/Smartlead provider configuration
|
|
81
|
+
*/
|
|
82
|
+
export interface SmartProspectConfig {
|
|
83
|
+
apiToken: string;
|
|
84
|
+
apiUrl?: string;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* LinkedIn Data Dump provider configuration
|
|
88
|
+
*/
|
|
89
|
+
export interface LddConfig {
|
|
90
|
+
apiUrl: string;
|
|
91
|
+
apiToken: string;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Dropcontact provider configuration
|
|
95
|
+
*/
|
|
96
|
+
export interface DropcontactConfig {
|
|
97
|
+
apiKey: string;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Email construction provider configuration (no API key needed)
|
|
101
|
+
*/
|
|
102
|
+
export interface ConstructConfig {
|
|
103
|
+
/** Maximum email pattern attempts (default: 8) */
|
|
104
|
+
maxAttempts?: number;
|
|
105
|
+
/** Timeout for MX verification in ms (default: 5000) */
|
|
106
|
+
timeoutMs?: number;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* All provider configurations
|
|
110
|
+
*/
|
|
111
|
+
export interface ProvidersConfig {
|
|
112
|
+
construct?: ConstructConfig;
|
|
113
|
+
ldd?: LddConfig;
|
|
114
|
+
smartprospect?: SmartProspectConfig;
|
|
115
|
+
hunter?: HunterConfig;
|
|
116
|
+
apollo?: ApolloConfig;
|
|
117
|
+
dropcontact?: DropcontactConfig;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Options for enrichment operations
|
|
121
|
+
*/
|
|
122
|
+
export interface EnrichmentOptions {
|
|
123
|
+
/** Maximum cost per email enrichment in USD (default: Infinity) */
|
|
124
|
+
maxCostPerEmail?: number;
|
|
125
|
+
/** Minimum confidence threshold 0-100 (default: 0) */
|
|
126
|
+
confidenceThreshold?: number;
|
|
127
|
+
/** Provider order (default: ['construct', 'ldd', 'smartprospect', 'hunter', 'apollo', 'dropcontact']) */
|
|
128
|
+
providerOrder?: ProviderName[];
|
|
129
|
+
/** Retry delay in ms on transient errors (default: 200) */
|
|
130
|
+
retryMs?: number;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Options for batch enrichment
|
|
134
|
+
*/
|
|
135
|
+
export interface BatchEnrichmentOptions extends EnrichmentOptions {
|
|
136
|
+
/** Batch size for parallel processing (default: 50) */
|
|
137
|
+
batchSize?: number;
|
|
138
|
+
/** Delay between batches in ms (default: 200) */
|
|
139
|
+
delayMs?: number;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Cache adapter interface - consumers provide their own caching implementation
|
|
143
|
+
*/
|
|
144
|
+
export interface CacheAdapter {
|
|
145
|
+
/** Get cached result for an email */
|
|
146
|
+
get: (email: string) => Promise<CanonicalEmail | null>;
|
|
147
|
+
/** Set cached result for an email */
|
|
148
|
+
set: (email: string, result: CanonicalEmail, ttlMs?: number) => Promise<void>;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Cost callback - called when a provider incurs a cost
|
|
152
|
+
*/
|
|
153
|
+
export type CostCallback = (provider: string, costUsd: number) => void | Promise<void>;
|
|
154
|
+
/**
|
|
155
|
+
* Logger interface - consumers can provide their own logger
|
|
156
|
+
*/
|
|
157
|
+
export interface EnrichmentLogger {
|
|
158
|
+
debug?: (message: string, context?: Record<string, unknown>) => void;
|
|
159
|
+
info?: (message: string, context?: Record<string, unknown>) => void;
|
|
160
|
+
warn?: (message: string, context?: Record<string, unknown>) => void;
|
|
161
|
+
error?: (message: string, context?: Record<string, unknown>) => void;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Configuration for createEnrichmentClient
|
|
165
|
+
*/
|
|
166
|
+
export interface EnrichmentClientConfig {
|
|
167
|
+
/** Provider API keys and configuration */
|
|
168
|
+
providers: ProvidersConfig;
|
|
169
|
+
/** Enrichment options */
|
|
170
|
+
options?: EnrichmentOptions;
|
|
171
|
+
/** Optional cache adapter */
|
|
172
|
+
cache?: CacheAdapter;
|
|
173
|
+
/** Optional cost tracking callback */
|
|
174
|
+
onCost?: CostCallback;
|
|
175
|
+
/** Optional logger */
|
|
176
|
+
logger?: EnrichmentLogger;
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Enrichment client interface
|
|
180
|
+
*/
|
|
181
|
+
export interface EnrichmentClient {
|
|
182
|
+
/** Enrich a single candidate */
|
|
183
|
+
enrich: (candidate: EnrichmentCandidate) => Promise<CanonicalEmail>;
|
|
184
|
+
/** Enrich multiple candidates in batches */
|
|
185
|
+
enrichBatch: (candidates: EnrichmentCandidate[], options?: BatchEnrichmentOptions) => Promise<Array<{
|
|
186
|
+
candidate: EnrichmentCandidate;
|
|
187
|
+
} & CanonicalEmail>>;
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Available provider names
|
|
191
|
+
*/
|
|
192
|
+
export type ProviderName = 'construct' | 'ldd' | 'smartprospect' | 'hunter' | 'apollo' | 'dropcontact';
|
|
193
|
+
/**
|
|
194
|
+
* Default provider order
|
|
195
|
+
*/
|
|
196
|
+
export declare const DEFAULT_PROVIDER_ORDER: ProviderName[];
|
|
197
|
+
/**
|
|
198
|
+
* Provider costs in USD per lookup
|
|
199
|
+
*/
|
|
200
|
+
export declare const PROVIDER_COSTS: Record<ProviderName, number>;
|
|
201
|
+
/**
|
|
202
|
+
* Email verification result
|
|
203
|
+
*/
|
|
204
|
+
export interface VerificationResult {
|
|
205
|
+
/** Whether the email is valid */
|
|
206
|
+
valid: boolean | null;
|
|
207
|
+
/** Confidence score 0-100 */
|
|
208
|
+
confidence: number;
|
|
209
|
+
/** Reason for the result */
|
|
210
|
+
reason?: 'valid' | 'invalid' | 'syntax' | 'mx_missing' | 'disposable' | 'role_account' | 'catch_all' | 'timeout' | 'error';
|
|
211
|
+
/** Whether this is a catch-all domain */
|
|
212
|
+
isCatchAll: boolean;
|
|
213
|
+
/** Whether this is a role account (info@, support@, etc.) */
|
|
214
|
+
isRoleAccount: boolean;
|
|
215
|
+
/** Whether this is a disposable email */
|
|
216
|
+
isDisposable: boolean;
|
|
217
|
+
/** MX records found */
|
|
218
|
+
mxRecords?: string[];
|
|
219
|
+
}
|
|
220
|
+
export interface SmartProspectCompany {
|
|
221
|
+
name: string;
|
|
222
|
+
website: string;
|
|
223
|
+
}
|
|
224
|
+
export interface SmartProspectContact {
|
|
225
|
+
id: string;
|
|
226
|
+
firstName: string;
|
|
227
|
+
lastName: string;
|
|
228
|
+
fullName: string;
|
|
229
|
+
title: string;
|
|
230
|
+
company: SmartProspectCompany;
|
|
231
|
+
department: string[];
|
|
232
|
+
level: string;
|
|
233
|
+
industry: string;
|
|
234
|
+
subIndustry: string;
|
|
235
|
+
companyHeadCount: string;
|
|
236
|
+
companyRevenue: string;
|
|
237
|
+
country: string;
|
|
238
|
+
state: string;
|
|
239
|
+
city: string;
|
|
240
|
+
email: string;
|
|
241
|
+
emailSource?: string;
|
|
242
|
+
linkedin: string;
|
|
243
|
+
emailDeliverability: number;
|
|
244
|
+
address: string;
|
|
245
|
+
status?: string;
|
|
246
|
+
verificationStatus?: string;
|
|
247
|
+
catchAllStatus?: string;
|
|
248
|
+
}
|
|
249
|
+
export interface SmartProspectSearchFilters {
|
|
250
|
+
name?: string[];
|
|
251
|
+
firstName?: string[];
|
|
252
|
+
lastName?: string[];
|
|
253
|
+
title?: string[];
|
|
254
|
+
company?: string[];
|
|
255
|
+
domain?: string[];
|
|
256
|
+
department?: string[];
|
|
257
|
+
level?: string[];
|
|
258
|
+
industry?: string[];
|
|
259
|
+
country?: string[];
|
|
260
|
+
state?: string[];
|
|
261
|
+
city?: string[];
|
|
262
|
+
companyHeadCount?: string[];
|
|
263
|
+
companyRevenue?: string[];
|
|
264
|
+
dontDisplayOwnedContact?: boolean;
|
|
265
|
+
limit?: number;
|
|
266
|
+
titleExactMatch?: boolean;
|
|
267
|
+
scroll_id?: string;
|
|
268
|
+
}
|
|
269
|
+
export interface SmartProspectSearchResponse {
|
|
270
|
+
success: boolean;
|
|
271
|
+
message: string;
|
|
272
|
+
data: {
|
|
273
|
+
list: SmartProspectContact[];
|
|
274
|
+
scroll_id?: string;
|
|
275
|
+
filter_id?: number;
|
|
276
|
+
total_count: number;
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
export interface SmartProspectFetchResponse {
|
|
280
|
+
success: boolean;
|
|
281
|
+
message: string;
|
|
282
|
+
data: {
|
|
283
|
+
list: SmartProspectContact[];
|
|
284
|
+
total_count: number;
|
|
285
|
+
metrics: {
|
|
286
|
+
totalContacts: number;
|
|
287
|
+
totalEmails: number;
|
|
288
|
+
noEmailFound: number;
|
|
289
|
+
invalidEmails: number;
|
|
290
|
+
catchAllEmails: number;
|
|
291
|
+
verifiedEmails: number;
|
|
292
|
+
completed: number;
|
|
293
|
+
};
|
|
294
|
+
leads_found: number;
|
|
295
|
+
email_fetched: number;
|
|
296
|
+
verification_status_list: string[];
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
export interface LddProfileEmail {
|
|
300
|
+
email_address: string;
|
|
301
|
+
email_type?: string | null;
|
|
302
|
+
}
|
|
303
|
+
export interface LddProfilePhone {
|
|
304
|
+
phone_number: string;
|
|
305
|
+
phone_type?: string | null;
|
|
306
|
+
}
|
|
307
|
+
export interface LddProfileData {
|
|
308
|
+
source_record_id: string;
|
|
309
|
+
linkedin_profile_url?: string | null;
|
|
310
|
+
linkedin_id?: string | null;
|
|
311
|
+
linkedin_username?: string | null;
|
|
312
|
+
first_name?: string | null;
|
|
313
|
+
last_name?: string | null;
|
|
314
|
+
full_name?: string | null;
|
|
315
|
+
profile_headline?: string | null;
|
|
316
|
+
profile_summary?: string | null;
|
|
317
|
+
profile_location?: string | null;
|
|
318
|
+
emails: LddProfileEmail[];
|
|
319
|
+
phones: LddProfilePhone[];
|
|
320
|
+
}
|
|
321
|
+
export type LddApiResponse = {
|
|
322
|
+
success: true;
|
|
323
|
+
data: LddProfileData;
|
|
324
|
+
} | {
|
|
325
|
+
success: false;
|
|
326
|
+
error: string;
|
|
327
|
+
message: string;
|
|
328
|
+
statusCode?: number;
|
|
329
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Email Enrichment Types
|
|
4
|
+
*
|
|
5
|
+
* Core types for the email enrichment module. These types are designed to be
|
|
6
|
+
* stateless - consumers pass their own API keys and configuration.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.PROVIDER_COSTS = exports.DEFAULT_PROVIDER_ORDER = void 0;
|
|
10
|
+
/**
|
|
11
|
+
* Default provider order
|
|
12
|
+
*/
|
|
13
|
+
exports.DEFAULT_PROVIDER_ORDER = [
|
|
14
|
+
'construct',
|
|
15
|
+
'ldd',
|
|
16
|
+
'smartprospect',
|
|
17
|
+
'hunter',
|
|
18
|
+
'apollo',
|
|
19
|
+
'dropcontact',
|
|
20
|
+
];
|
|
21
|
+
/**
|
|
22
|
+
* Provider costs in USD per lookup
|
|
23
|
+
*/
|
|
24
|
+
exports.PROVIDER_COSTS = {
|
|
25
|
+
construct: 0,
|
|
26
|
+
ldd: 0,
|
|
27
|
+
smartprospect: 0.01,
|
|
28
|
+
hunter: 0.005,
|
|
29
|
+
apollo: 0,
|
|
30
|
+
dropcontact: 0.01,
|
|
31
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Disposable Email Domain Detection
|
|
3
|
+
*
|
|
4
|
+
* Detects disposable/temporary email domains (Mailinator, Guerrillamail, etc.)
|
|
5
|
+
* that should be filtered out when enriching emails.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Set of known disposable email domains (600+)
|
|
9
|
+
*/
|
|
10
|
+
export declare const DISPOSABLE_DOMAINS: Set<string>;
|
|
11
|
+
/**
|
|
12
|
+
* Check if an email is from a disposable domain
|
|
13
|
+
*
|
|
14
|
+
* @param email - Email address to check
|
|
15
|
+
* @returns true if the email is from a disposable domain
|
|
16
|
+
*/
|
|
17
|
+
export declare function isDisposableEmail(email: string): boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Check if a domain is a disposable email domain
|
|
20
|
+
*
|
|
21
|
+
* @param domain - Domain to check
|
|
22
|
+
* @returns true if the domain is a disposable email domain
|
|
23
|
+
*/
|
|
24
|
+
export declare function isDisposableDomain(domain: string): boolean;
|