linkedin-secret-sauce 0.12.2 → 0.12.4
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/README.md +276 -40
- package/dist/enrichment/index.d.ts +22 -3
- package/dist/enrichment/index.js +29 -38
- package/dist/enrichment/matching.d.ts +4 -8
- package/dist/enrichment/matching.js +6 -5
- package/dist/enrichment/orchestrator.d.ts +1 -1
- package/dist/enrichment/orchestrator.js +51 -44
- package/dist/enrichment/providers/index.d.ts +0 -2
- package/dist/enrichment/providers/index.js +1 -8
- package/dist/enrichment/providers/snovio.d.ts +4 -4
- package/dist/enrichment/providers/snovio.js +14 -14
- package/dist/enrichment/types.d.ts +18 -71
- package/dist/enrichment/types.js +3 -8
- package/dist/enrichment/utils/rate-limiter.js +0 -2
- package/dist/index.d.ts +2 -4
- package/dist/index.js +2 -2
- package/docs/COSIALL_PROFILE_EMAILS.md +342 -0
- package/docs/ENRICHMENT.md +580 -0
- package/docs/INTEGRATION.md +403 -0
- package/docs/PLAYGROUND.md +553 -0
- package/docs/SALES_SEARCH.md +171 -0
- package/docs/api/.nojekyll +1 -0
- package/docs/api/assets/hierarchy.js +1 -0
- package/docs/api/assets/highlight.css +92 -0
- package/docs/api/assets/icons.js +18 -0
- package/docs/api/assets/icons.svg +1 -0
- package/docs/api/assets/main.js +60 -0
- package/docs/api/assets/navigation.js +1 -0
- package/docs/api/assets/search.js +1 -0
- package/docs/api/assets/style.css +1633 -0
- package/docs/api/classes/LinkedInClientError.html +37 -0
- package/docs/api/functions/_testGetAccountCookies.html +4 -0
- package/docs/api/functions/_testGetAccountEntry.html +4 -0
- package/docs/api/functions/_testGetAllAccountIds.html +3 -0
- package/docs/api/functions/_testGetPoolState.html +3 -0
- package/docs/api/functions/adminResetAccount.html +1 -0
- package/docs/api/functions/adminSetCooldown.html +1 -0
- package/docs/api/functions/buildCookieHeader.html +1 -0
- package/docs/api/functions/clearAllSmartLeadTokens.html +2 -0
- package/docs/api/functions/clearRequestHistory.html +1 -0
- package/docs/api/functions/clearSessionAccount.html +1 -0
- package/docs/api/functions/clearSmartLeadToken.html +2 -0
- package/docs/api/functions/createEnrichmentClient.html +8 -0
- package/docs/api/functions/extractCsrfToken.html +1 -0
- package/docs/api/functions/extractLinkedInHandle.html +7 -0
- package/docs/api/functions/fetchCookiesFromCosiall.html +14 -0
- package/docs/api/functions/fetchProfileEmailsFromCosiall.html +18 -0
- package/docs/api/functions/forceRefreshCookies.html +1 -0
- package/docs/api/functions/getAccountForSession.html +1 -0
- package/docs/api/functions/getAccountsSummary.html +1 -0
- package/docs/api/functions/getCompaniesBatch.html +5 -0
- package/docs/api/functions/getCompanyById.html +9 -0
- package/docs/api/functions/getCompanyByUrl.html +1 -0
- package/docs/api/functions/getConfig.html +1 -0
- package/docs/api/functions/getCookiePoolHealth.html +1 -0
- package/docs/api/functions/getProfileByUrn.html +17 -0
- package/docs/api/functions/getProfileByVanity.html +10 -0
- package/docs/api/functions/getProfilesBatch.html +1 -0
- package/docs/api/functions/getRequestHistory.html +1 -0
- package/docs/api/functions/getSalesNavigatorProfileDetails.html +1 -0
- package/docs/api/functions/getSalesNavigatorProfileFull.html +16 -0
- package/docs/api/functions/getSmartLeadToken.html +1 -0
- package/docs/api/functions/getSmartLeadTokenCacheStats.html +2 -0
- package/docs/api/functions/getSmartLeadUser.html +2 -0
- package/docs/api/functions/getSnapshot.html +1 -0
- package/docs/api/functions/getYearsAtCompanyOptions.html +2 -0
- package/docs/api/functions/getYearsInPositionOptions.html +2 -0
- package/docs/api/functions/getYearsOfExperienceOptions.html +2 -0
- package/docs/api/functions/incrementMetric.html +1 -0
- package/docs/api/functions/initializeCookiePool.html +1 -0
- package/docs/api/functions/initializeLinkedInClient.html +1 -0
- package/docs/api/functions/isBusinessEmail.html +4 -0
- package/docs/api/functions/isDisposableDomain.html +4 -0
- package/docs/api/functions/isDisposableEmail.html +4 -0
- package/docs/api/functions/isPersonalDomain.html +4 -0
- package/docs/api/functions/isPersonalEmail.html +4 -0
- package/docs/api/functions/isRoleAccount.html +4 -0
- package/docs/api/functions/isValidEmailSyntax.html +4 -0
- package/docs/api/functions/parseFullProfile.html +15 -0
- package/docs/api/functions/parseSalesSearchResults.html +1 -0
- package/docs/api/functions/reportAccountFailure.html +1 -0
- package/docs/api/functions/reportAccountSuccess.html +1 -0
- package/docs/api/functions/resolveCompanyUniversalName.html +1 -0
- package/docs/api/functions/searchSalesLeads.html +16 -0
- package/docs/api/functions/selectAccountForRequest.html +1 -0
- package/docs/api/functions/setAccountForSession.html +1 -0
- package/docs/api/functions/typeahead.html +1 -0
- package/docs/api/functions/verifyEmailMx.html +1 -0
- package/docs/api/hierarchy.html +1 -0
- package/docs/api/index.html +12 -0
- package/docs/api/interfaces/AccountCookies.html +4 -0
- package/docs/api/interfaces/BatchEnrichmentOptions.html +14 -0
- package/docs/api/interfaces/CacheAdapter.html +6 -0
- package/docs/api/interfaces/CanonicalEmail.html +14 -0
- package/docs/api/interfaces/Company.html +17 -0
- package/docs/api/interfaces/ConstructConfig.html +8 -0
- package/docs/api/interfaces/CosiallProfileEmailsResponse.html +11 -0
- package/docs/api/interfaces/EnrichmentCandidate.html +34 -0
- package/docs/api/interfaces/EnrichmentClient.html +10 -0
- package/docs/api/interfaces/EnrichmentClientConfig.html +12 -0
- package/docs/api/interfaces/EnrichmentLogger.html +6 -0
- package/docs/api/interfaces/EnrichmentOptions.html +10 -0
- package/docs/api/interfaces/HunterConfig.html +3 -0
- package/docs/api/interfaces/LddConfig.html +4 -0
- package/docs/api/interfaces/LddProfileData.html +6 -0
- package/docs/api/interfaces/LinkedInClientConfig.html +20 -0
- package/docs/api/interfaces/LinkedInCookie.html +9 -0
- package/docs/api/interfaces/LinkedInPosition.html +14 -0
- package/docs/api/interfaces/LinkedInProfile.html +21 -0
- package/docs/api/interfaces/LinkedInSpotlightBadge.html +5 -0
- package/docs/api/interfaces/LinkedInTenure.html +3 -0
- package/docs/api/interfaces/Metrics.html +22 -0
- package/docs/api/interfaces/MetricsSnapshot.html +23 -0
- package/docs/api/interfaces/ProfileEducation.html +8 -0
- package/docs/api/interfaces/ProfileEmailsLookupOptions.html +9 -0
- package/docs/api/interfaces/ProfilePosition.html +12 -0
- package/docs/api/interfaces/ProfileSkill.html +3 -0
- package/docs/api/interfaces/ProviderResult.html +11 -0
- package/docs/api/interfaces/ProvidersConfig.html +14 -0
- package/docs/api/interfaces/RequestHistoryEntry.html +8 -0
- package/docs/api/interfaces/SalesLeadSearchResult.html +31 -0
- package/docs/api/interfaces/SalesNavigatorContactInfo.html +5 -0
- package/docs/api/interfaces/SalesNavigatorPosition.html +11 -0
- package/docs/api/interfaces/SalesNavigatorProfile.html +9 -0
- package/docs/api/interfaces/SalesNavigatorProfileFull.html +24 -0
- package/docs/api/interfaces/SearchSalesResult.html +5 -0
- package/docs/api/interfaces/SmartLeadAuthConfig.html +6 -0
- package/docs/api/interfaces/SmartLeadCredentials.html +3 -0
- package/docs/api/interfaces/SmartLeadLoginResponse.html +3 -0
- package/docs/api/interfaces/SmartLeadUser.html +8 -0
- package/docs/api/interfaces/SmartProspectConfig.html +19 -0
- package/docs/api/interfaces/SmartProspectContact.html +24 -0
- package/docs/api/interfaces/SmartProspectSearchFilters.html +42 -0
- package/docs/api/interfaces/TypeaheadItem.html +3 -0
- package/docs/api/interfaces/TypeaheadResult.html +3 -0
- package/docs/api/interfaces/VerificationResult.html +16 -0
- package/docs/api/types/CostCallback.html +2 -0
- package/docs/api/types/Geo.html +4 -0
- package/docs/api/types/LddApiResponse.html +1 -0
- package/docs/api/types/ProviderFunc.html +2 -0
- package/docs/api/types/ProviderName.html +2 -0
- package/docs/api/types/SalesSearchFilters.html +7 -0
- package/docs/api/types/TypeaheadType.html +1 -0
- package/docs/api/variables/COMPANY_SIZE_OPTIONS.html +1 -0
- package/docs/api/variables/DEFAULT_PROVIDER_ORDER.html +19 -0
- package/docs/api/variables/DISPOSABLE_DOMAINS.html +2 -0
- package/docs/api/variables/FUNCTION_OPTIONS.html +1 -0
- package/docs/api/variables/INDUSTRY_OPTIONS.html +1 -0
- package/docs/api/variables/LANGUAGE_OPTIONS.html +1 -0
- package/docs/api/variables/PERSONAL_DOMAINS.html +2 -0
- package/docs/api/variables/PROVIDER_COSTS.html +13 -0
- package/docs/api/variables/REGION_OPTIONS.html +1 -0
- package/docs/api/variables/SENIORITY_OPTIONS.html +3 -0
- package/docs/api/variables/YEARS_OPTIONS.html +1 -0
- package/docs/index.html +98 -0
- package/package.json +40 -28
- package/dist/enrichment/providers/apollo.d.ts +0 -11
- package/dist/enrichment/providers/apollo.js +0 -181
- package/dist/enrichment/providers/bouncer.d.ts +0 -67
- package/dist/enrichment/providers/bouncer.js +0 -231
- package/dist/enrichment/providers/dropcontact.d.ts +0 -22
- package/dist/enrichment/providers/dropcontact.js +0 -206
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Bouncer.io Email Verification Provider
|
|
3
|
-
*
|
|
4
|
-
* Provides SMTP-level email verification with 99%+ accuracy.
|
|
5
|
-
* Best for verifying pattern-guessed emails on non-catch-all domains.
|
|
6
|
-
*
|
|
7
|
-
* Features:
|
|
8
|
-
* - SMTP verification (checks if mailbox exists)
|
|
9
|
-
* - Catch-all domain detection
|
|
10
|
-
* - Disposable email detection
|
|
11
|
-
* - Role account detection
|
|
12
|
-
* - Toxicity scoring (0-5)
|
|
13
|
-
*
|
|
14
|
-
* @see https://docs.usebouncer.com
|
|
15
|
-
*/
|
|
16
|
-
import type { EnrichmentCandidate, ProviderResult, ProviderMultiResult, BouncerConfig, BouncerVerifyResponse } from "../types";
|
|
17
|
-
/**
|
|
18
|
-
* Create the Bouncer verification provider
|
|
19
|
-
*
|
|
20
|
-
* NOTE: This provider is a VERIFIER, not a FINDER. It verifies emails
|
|
21
|
-
* that were generated by the construct provider or passed in the candidate.
|
|
22
|
-
*
|
|
23
|
-
* Usage in the enrichment flow:
|
|
24
|
-
* 1. construct provider generates email patterns
|
|
25
|
-
* 2. bouncer provider verifies which patterns are deliverable
|
|
26
|
-
* 3. Only verified emails are returned
|
|
27
|
-
*/
|
|
28
|
-
export declare function createBouncerProvider(config: BouncerConfig): (candidate: EnrichmentCandidate) => Promise<ProviderResult | ProviderMultiResult | null>;
|
|
29
|
-
/**
|
|
30
|
-
* Standalone function to verify a single email via Bouncer
|
|
31
|
-
*
|
|
32
|
-
* Useful for ad-hoc verification outside the enrichment flow.
|
|
33
|
-
*
|
|
34
|
-
* @example
|
|
35
|
-
* ```typescript
|
|
36
|
-
* const result = await verifyEmailWithBouncer('test@example.com', {
|
|
37
|
-
* apiKey: process.env.BOUNCER_API_KEY,
|
|
38
|
-
* });
|
|
39
|
-
* console.log(result.status); // 'deliverable' | 'undeliverable' | 'risky' | 'unknown'
|
|
40
|
-
* ```
|
|
41
|
-
*/
|
|
42
|
-
export declare function verifyEmailWithBouncer(email: string, config: BouncerConfig): Promise<BouncerVerifyResponse | null>;
|
|
43
|
-
/**
|
|
44
|
-
* Check if a domain is catch-all via Bouncer
|
|
45
|
-
*
|
|
46
|
-
* Sends a verification request for a random email and checks acceptAll flag.
|
|
47
|
-
*
|
|
48
|
-
* @example
|
|
49
|
-
* ```typescript
|
|
50
|
-
* const isCatchAll = await checkCatchAllDomain('example.com', {
|
|
51
|
-
* apiKey: process.env.BOUNCER_API_KEY,
|
|
52
|
-
* });
|
|
53
|
-
* ```
|
|
54
|
-
*/
|
|
55
|
-
export declare function checkCatchAllDomain(domain: string, config: BouncerConfig): Promise<boolean | null>;
|
|
56
|
-
/**
|
|
57
|
-
* Verify multiple emails in batch via Bouncer
|
|
58
|
-
*
|
|
59
|
-
* @example
|
|
60
|
-
* ```typescript
|
|
61
|
-
* const results = await verifyEmailsBatch(
|
|
62
|
-
* ['john@example.com', 'jane@example.com'],
|
|
63
|
-
* { apiKey: process.env.BOUNCER_API_KEY }
|
|
64
|
-
* );
|
|
65
|
-
* ```
|
|
66
|
-
*/
|
|
67
|
-
export declare function verifyEmailsBatch(emails: string[], config: BouncerConfig): Promise<Map<string, BouncerVerifyResponse | null>>;
|
|
@@ -1,231 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Bouncer.io Email Verification Provider
|
|
4
|
-
*
|
|
5
|
-
* Provides SMTP-level email verification with 99%+ accuracy.
|
|
6
|
-
* Best for verifying pattern-guessed emails on non-catch-all domains.
|
|
7
|
-
*
|
|
8
|
-
* Features:
|
|
9
|
-
* - SMTP verification (checks if mailbox exists)
|
|
10
|
-
* - Catch-all domain detection
|
|
11
|
-
* - Disposable email detection
|
|
12
|
-
* - Role account detection
|
|
13
|
-
* - Toxicity scoring (0-5)
|
|
14
|
-
*
|
|
15
|
-
* @see https://docs.usebouncer.com
|
|
16
|
-
*/
|
|
17
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
-
exports.createBouncerProvider = createBouncerProvider;
|
|
19
|
-
exports.verifyEmailWithBouncer = verifyEmailWithBouncer;
|
|
20
|
-
exports.checkCatchAllDomain = checkCatchAllDomain;
|
|
21
|
-
exports.verifyEmailsBatch = verifyEmailsBatch;
|
|
22
|
-
const noop_provider_1 = require("../utils/noop-provider");
|
|
23
|
-
const DEFAULT_API_URL = "https://api.usebouncer.com/v1.1";
|
|
24
|
-
const DEFAULT_TIMEOUT_MS = 30000;
|
|
25
|
-
/**
|
|
26
|
-
* Map Bouncer status to confidence score
|
|
27
|
-
*/
|
|
28
|
-
function statusToConfidence(status, response) {
|
|
29
|
-
switch (status) {
|
|
30
|
-
case "deliverable":
|
|
31
|
-
// High confidence - email verified as deliverable
|
|
32
|
-
// Reduce slightly if catch-all or high toxicity
|
|
33
|
-
let score = 95;
|
|
34
|
-
if (response.acceptAll)
|
|
35
|
-
score -= 20; // Catch-all reduces confidence
|
|
36
|
-
if (response.toxicity && response.toxicity >= 3)
|
|
37
|
-
score -= 10;
|
|
38
|
-
if (response.role)
|
|
39
|
-
score -= 5; // Role accounts slightly lower
|
|
40
|
-
return Math.max(50, score);
|
|
41
|
-
case "risky":
|
|
42
|
-
// Medium confidence - risky but might work
|
|
43
|
-
return response.acceptAll ? 40 : 50;
|
|
44
|
-
case "undeliverable":
|
|
45
|
-
// Email does not exist
|
|
46
|
-
return 0;
|
|
47
|
-
case "unknown":
|
|
48
|
-
// Could not verify
|
|
49
|
-
return 30;
|
|
50
|
-
default:
|
|
51
|
-
return 0;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
/**
|
|
55
|
-
* Extract email candidates from the enrichment candidate
|
|
56
|
-
* Bouncer is a VERIFICATION provider, so it needs emails to verify
|
|
57
|
-
*/
|
|
58
|
-
function extractEmailsToVerify(candidate) {
|
|
59
|
-
const emails = [];
|
|
60
|
-
// Check if candidate has pre-generated email patterns in metadata
|
|
61
|
-
const metadata = candidate._emailCandidates;
|
|
62
|
-
if (Array.isArray(metadata)) {
|
|
63
|
-
emails.push(...metadata.filter((e) => typeof e === "string"));
|
|
64
|
-
}
|
|
65
|
-
return emails;
|
|
66
|
-
}
|
|
67
|
-
/**
|
|
68
|
-
* Verify a single email via Bouncer API
|
|
69
|
-
*/
|
|
70
|
-
async function verifyEmail(email, apiKey, apiUrl, timeoutMs) {
|
|
71
|
-
const controller = new AbortController();
|
|
72
|
-
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
73
|
-
try {
|
|
74
|
-
const url = `${apiUrl}/email/verify?email=${encodeURIComponent(email)}`;
|
|
75
|
-
const response = await fetch(url, {
|
|
76
|
-
method: "GET",
|
|
77
|
-
headers: {
|
|
78
|
-
"x-api-key": apiKey,
|
|
79
|
-
Accept: "application/json",
|
|
80
|
-
},
|
|
81
|
-
signal: controller.signal,
|
|
82
|
-
});
|
|
83
|
-
if (!response.ok) {
|
|
84
|
-
// All HTTP errors return null - let caller decide how to handle
|
|
85
|
-
return null;
|
|
86
|
-
}
|
|
87
|
-
const data = (await response.json());
|
|
88
|
-
return data;
|
|
89
|
-
}
|
|
90
|
-
catch {
|
|
91
|
-
// All errors (network, timeout, etc.) return null
|
|
92
|
-
return null;
|
|
93
|
-
}
|
|
94
|
-
finally {
|
|
95
|
-
clearTimeout(timeoutId);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
/**
|
|
99
|
-
* Create the Bouncer verification provider
|
|
100
|
-
*
|
|
101
|
-
* NOTE: This provider is a VERIFIER, not a FINDER. It verifies emails
|
|
102
|
-
* that were generated by the construct provider or passed in the candidate.
|
|
103
|
-
*
|
|
104
|
-
* Usage in the enrichment flow:
|
|
105
|
-
* 1. construct provider generates email patterns
|
|
106
|
-
* 2. bouncer provider verifies which patterns are deliverable
|
|
107
|
-
* 3. Only verified emails are returned
|
|
108
|
-
*/
|
|
109
|
-
function createBouncerProvider(config) {
|
|
110
|
-
const { apiKey, apiUrl = DEFAULT_API_URL, timeoutMs = DEFAULT_TIMEOUT_MS, } = config;
|
|
111
|
-
if (!apiKey) {
|
|
112
|
-
return (0, noop_provider_1.createNoOpProvider)("bouncer");
|
|
113
|
-
}
|
|
114
|
-
async function verifyEmails(candidate) {
|
|
115
|
-
const emailsToVerify = extractEmailsToVerify(candidate);
|
|
116
|
-
// If no emails to verify, return null
|
|
117
|
-
// The orchestrator will need to pass emails from construct
|
|
118
|
-
if (emailsToVerify.length === 0) {
|
|
119
|
-
return null;
|
|
120
|
-
}
|
|
121
|
-
const verifiedEmails = [];
|
|
122
|
-
// Verify each email candidate
|
|
123
|
-
for (const email of emailsToVerify) {
|
|
124
|
-
try {
|
|
125
|
-
const result = await verifyEmail(email, apiKey, apiUrl, timeoutMs);
|
|
126
|
-
if (result) {
|
|
127
|
-
const confidence = statusToConfidence(result.status, result);
|
|
128
|
-
// Only include emails that are potentially deliverable
|
|
129
|
-
if (result.status === "deliverable" || result.status === "risky") {
|
|
130
|
-
verifiedEmails.push({
|
|
131
|
-
email: result.email,
|
|
132
|
-
verified: result.status === "deliverable",
|
|
133
|
-
confidence,
|
|
134
|
-
isCatchAll: result.acceptAll,
|
|
135
|
-
metadata: {
|
|
136
|
-
bouncerStatus: result.status,
|
|
137
|
-
bouncerReason: result.reason,
|
|
138
|
-
toxicity: result.toxicity,
|
|
139
|
-
isDisposable: result.disposable,
|
|
140
|
-
isRole: result.role,
|
|
141
|
-
isFreeProvider: result.free,
|
|
142
|
-
},
|
|
143
|
-
});
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
catch {
|
|
148
|
-
// Continue with other emails on error
|
|
149
|
-
continue;
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
if (verifiedEmails.length === 0) {
|
|
153
|
-
return null;
|
|
154
|
-
}
|
|
155
|
-
// Sort by confidence
|
|
156
|
-
verifiedEmails.sort((a, b) => (b.confidence ?? 0) - (a.confidence ?? 0));
|
|
157
|
-
return { emails: verifiedEmails };
|
|
158
|
-
}
|
|
159
|
-
// Mark provider name for orchestrator
|
|
160
|
-
verifyEmails.__name = "bouncer";
|
|
161
|
-
return verifyEmails;
|
|
162
|
-
}
|
|
163
|
-
/**
|
|
164
|
-
* Standalone function to verify a single email via Bouncer
|
|
165
|
-
*
|
|
166
|
-
* Useful for ad-hoc verification outside the enrichment flow.
|
|
167
|
-
*
|
|
168
|
-
* @example
|
|
169
|
-
* ```typescript
|
|
170
|
-
* const result = await verifyEmailWithBouncer('test@example.com', {
|
|
171
|
-
* apiKey: process.env.BOUNCER_API_KEY,
|
|
172
|
-
* });
|
|
173
|
-
* console.log(result.status); // 'deliverable' | 'undeliverable' | 'risky' | 'unknown'
|
|
174
|
-
* ```
|
|
175
|
-
*/
|
|
176
|
-
async function verifyEmailWithBouncer(email, config) {
|
|
177
|
-
const { apiKey, apiUrl = DEFAULT_API_URL, timeoutMs = DEFAULT_TIMEOUT_MS, } = config;
|
|
178
|
-
return verifyEmail(email, apiKey, apiUrl, timeoutMs);
|
|
179
|
-
}
|
|
180
|
-
/**
|
|
181
|
-
* Check if a domain is catch-all via Bouncer
|
|
182
|
-
*
|
|
183
|
-
* Sends a verification request for a random email and checks acceptAll flag.
|
|
184
|
-
*
|
|
185
|
-
* @example
|
|
186
|
-
* ```typescript
|
|
187
|
-
* const isCatchAll = await checkCatchAllDomain('example.com', {
|
|
188
|
-
* apiKey: process.env.BOUNCER_API_KEY,
|
|
189
|
-
* });
|
|
190
|
-
* ```
|
|
191
|
-
*/
|
|
192
|
-
async function checkCatchAllDomain(domain, config) {
|
|
193
|
-
const { apiKey, apiUrl = DEFAULT_API_URL, timeoutMs = DEFAULT_TIMEOUT_MS, } = config;
|
|
194
|
-
// Generate a random email that almost certainly doesn't exist
|
|
195
|
-
const randomLocal = `bounce-test-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
|
|
196
|
-
const testEmail = `${randomLocal}@${domain}`;
|
|
197
|
-
const result = await verifyEmail(testEmail, apiKey, apiUrl, timeoutMs);
|
|
198
|
-
if (!result) {
|
|
199
|
-
return null; // Could not determine
|
|
200
|
-
}
|
|
201
|
-
// If acceptAll is true, the domain accepts all emails (catch-all)
|
|
202
|
-
return result.acceptAll;
|
|
203
|
-
}
|
|
204
|
-
/**
|
|
205
|
-
* Verify multiple emails in batch via Bouncer
|
|
206
|
-
*
|
|
207
|
-
* @example
|
|
208
|
-
* ```typescript
|
|
209
|
-
* const results = await verifyEmailsBatch(
|
|
210
|
-
* ['john@example.com', 'jane@example.com'],
|
|
211
|
-
* { apiKey: process.env.BOUNCER_API_KEY }
|
|
212
|
-
* );
|
|
213
|
-
* ```
|
|
214
|
-
*/
|
|
215
|
-
async function verifyEmailsBatch(emails, config) {
|
|
216
|
-
const { apiKey, apiUrl = DEFAULT_API_URL, timeoutMs = DEFAULT_TIMEOUT_MS, } = config;
|
|
217
|
-
const results = new Map();
|
|
218
|
-
// Verify in parallel with concurrency limit
|
|
219
|
-
const CONCURRENCY = 5;
|
|
220
|
-
for (let i = 0; i < emails.length; i += CONCURRENCY) {
|
|
221
|
-
const batch = emails.slice(i, i + CONCURRENCY);
|
|
222
|
-
const batchResults = await Promise.all(batch.map(async (email) => {
|
|
223
|
-
const result = await verifyEmail(email, apiKey, apiUrl, timeoutMs);
|
|
224
|
-
return { email, result };
|
|
225
|
-
}));
|
|
226
|
-
for (const { email, result } of batchResults) {
|
|
227
|
-
results.set(email, result);
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
return results;
|
|
231
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Dropcontact Provider
|
|
3
|
-
*
|
|
4
|
-
* Dropcontact API for email finding and enrichment.
|
|
5
|
-
* Uses the batch enrichment API with polling for results.
|
|
6
|
-
*
|
|
7
|
-
* API Flow:
|
|
8
|
-
* 1. POST /batch to submit contacts for enrichment
|
|
9
|
-
* 2. GET /batch/{request_id} to poll for results
|
|
10
|
-
*
|
|
11
|
-
* Features:
|
|
12
|
-
* - Email finding from name + company/website
|
|
13
|
-
* - Email verification (deliverable/undeliverable)
|
|
14
|
-
* - GDPR compliant (EU-based)
|
|
15
|
-
*
|
|
16
|
-
* @see https://developer.dropcontact.com/
|
|
17
|
-
*/
|
|
18
|
-
import type { EnrichmentCandidate, ProviderResult, DropcontactConfig } from "../types";
|
|
19
|
-
/**
|
|
20
|
-
* Create the Dropcontact provider function
|
|
21
|
-
*/
|
|
22
|
-
export declare function createDropcontactProvider(config: DropcontactConfig): (candidate: EnrichmentCandidate) => Promise<ProviderResult | null>;
|
|
@@ -1,206 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Dropcontact Provider
|
|
4
|
-
*
|
|
5
|
-
* Dropcontact API for email finding and enrichment.
|
|
6
|
-
* Uses the batch enrichment API with polling for results.
|
|
7
|
-
*
|
|
8
|
-
* API Flow:
|
|
9
|
-
* 1. POST /batch to submit contacts for enrichment
|
|
10
|
-
* 2. GET /batch/{request_id} to poll for results
|
|
11
|
-
*
|
|
12
|
-
* Features:
|
|
13
|
-
* - Email finding from name + company/website
|
|
14
|
-
* - Email verification (deliverable/undeliverable)
|
|
15
|
-
* - GDPR compliant (EU-based)
|
|
16
|
-
*
|
|
17
|
-
* @see https://developer.dropcontact.com/
|
|
18
|
-
*/
|
|
19
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
-
exports.createDropcontactProvider = createDropcontactProvider;
|
|
21
|
-
const http_retry_1 = require("../utils/http-retry");
|
|
22
|
-
const noop_provider_1 = require("../utils/noop-provider");
|
|
23
|
-
const API_BASE = "https://api.dropcontact.io";
|
|
24
|
-
/**
|
|
25
|
-
* Extract inputs from candidate for Dropcontact API
|
|
26
|
-
*/
|
|
27
|
-
function extractInputs(candidate) {
|
|
28
|
-
const name = candidate.name || candidate.fullName || candidate.full_name;
|
|
29
|
-
const first = candidate.first ||
|
|
30
|
-
candidate.first_name ||
|
|
31
|
-
candidate.firstName ||
|
|
32
|
-
name?.split?.(" ")?.[0];
|
|
33
|
-
const last = candidate.last ||
|
|
34
|
-
candidate.last_name ||
|
|
35
|
-
candidate.lastName ||
|
|
36
|
-
name?.split?.(" ")?.slice(1).join(" ") ||
|
|
37
|
-
undefined;
|
|
38
|
-
const domain = candidate.domain ||
|
|
39
|
-
candidate.companyDomain ||
|
|
40
|
-
candidate.company_domain ||
|
|
41
|
-
undefined;
|
|
42
|
-
const company = candidate.company ||
|
|
43
|
-
candidate.currentCompany ||
|
|
44
|
-
candidate.organization ||
|
|
45
|
-
candidate.org ||
|
|
46
|
-
undefined;
|
|
47
|
-
const linkedin = candidate.linkedinUrl ||
|
|
48
|
-
candidate.linkedin_url ||
|
|
49
|
-
(candidate.linkedinHandle
|
|
50
|
-
? `https://www.linkedin.com/in/${candidate.linkedinHandle}`
|
|
51
|
-
: undefined) ||
|
|
52
|
-
(candidate.linkedin_handle
|
|
53
|
-
? `https://www.linkedin.com/in/${candidate.linkedin_handle}`
|
|
54
|
-
: undefined) ||
|
|
55
|
-
undefined;
|
|
56
|
-
return {
|
|
57
|
-
first_name: first,
|
|
58
|
-
last_name: last,
|
|
59
|
-
full_name: name,
|
|
60
|
-
company: company,
|
|
61
|
-
website: domain,
|
|
62
|
-
linkedin: linkedin,
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
/**
|
|
66
|
-
* Create the Dropcontact provider function
|
|
67
|
-
*/
|
|
68
|
-
function createDropcontactProvider(config) {
|
|
69
|
-
const { apiKey } = config;
|
|
70
|
-
if (!apiKey) {
|
|
71
|
-
return (0, noop_provider_1.createNoOpProvider)("dropcontact");
|
|
72
|
-
}
|
|
73
|
-
/**
|
|
74
|
-
* Submit a batch request to Dropcontact
|
|
75
|
-
*/
|
|
76
|
-
async function submitBatch(contacts) {
|
|
77
|
-
const body = {
|
|
78
|
-
data: contacts,
|
|
79
|
-
siren: false, // We don't need French company registration numbers
|
|
80
|
-
language: "en",
|
|
81
|
-
};
|
|
82
|
-
try {
|
|
83
|
-
const response = await (0, http_retry_1.postWithRetry)(`${API_BASE}/batch`, body, { "X-Access-Token": apiKey }, { retries: 2, backoffMs: 500, timeoutMs: 30000 });
|
|
84
|
-
if (response.error) {
|
|
85
|
-
return null;
|
|
86
|
-
}
|
|
87
|
-
return response.request_id || null;
|
|
88
|
-
}
|
|
89
|
-
catch {
|
|
90
|
-
return null;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
/**
|
|
94
|
-
* Poll for batch results with exponential backoff
|
|
95
|
-
*/
|
|
96
|
-
async function pollResults(requestId, maxAttempts = 10, initialDelayMs = 2000) {
|
|
97
|
-
let delayMs = initialDelayMs;
|
|
98
|
-
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
99
|
-
await (0, http_retry_1.delay)(delayMs);
|
|
100
|
-
try {
|
|
101
|
-
const response = await (0, http_retry_1.getWithRetry)(`${API_BASE}/batch/${requestId}`, { "X-Access-Token": apiKey }, { retries: 1, backoffMs: 500, timeoutMs: 30000 });
|
|
102
|
-
// Check if processing is complete
|
|
103
|
-
if (response.error) {
|
|
104
|
-
return null;
|
|
105
|
-
}
|
|
106
|
-
// If we have data and status is finished (or we got data back), return it
|
|
107
|
-
if (response.data && response.data.length > 0) {
|
|
108
|
-
// Check if still processing
|
|
109
|
-
if (response.status === "pending") {
|
|
110
|
-
// Increase delay for next poll (max 10 seconds)
|
|
111
|
-
delayMs = Math.min(delayMs * 1.5, 10000);
|
|
112
|
-
continue;
|
|
113
|
-
}
|
|
114
|
-
return response.data;
|
|
115
|
-
}
|
|
116
|
-
// If explicitly finished but no data
|
|
117
|
-
if (response.status === "finished") {
|
|
118
|
-
return response.data || null;
|
|
119
|
-
}
|
|
120
|
-
// Still processing, increase delay
|
|
121
|
-
delayMs = Math.min(delayMs * 1.5, 10000);
|
|
122
|
-
}
|
|
123
|
-
catch {
|
|
124
|
-
// Network error, retry with backoff
|
|
125
|
-
delayMs = Math.min(delayMs * 2, 10000);
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
return null; // Timeout after max attempts
|
|
129
|
-
}
|
|
130
|
-
/**
|
|
131
|
-
* Fetch email for a candidate
|
|
132
|
-
*/
|
|
133
|
-
async function fetchEmail(candidate) {
|
|
134
|
-
const contact = extractInputs(candidate);
|
|
135
|
-
// Need at least a name and company/website to search
|
|
136
|
-
const hasName = (0, http_retry_1.truthy)(contact.first_name) || (0, http_retry_1.truthy)(contact.full_name);
|
|
137
|
-
const hasCompanyInfo = (0, http_retry_1.truthy)(contact.company) || (0, http_retry_1.truthy)(contact.website);
|
|
138
|
-
const hasLinkedIn = (0, http_retry_1.truthy)(contact.linkedin);
|
|
139
|
-
if (!hasName && !hasLinkedIn) {
|
|
140
|
-
return null; // Need at least name or LinkedIn URL
|
|
141
|
-
}
|
|
142
|
-
if (!hasCompanyInfo && !hasLinkedIn) {
|
|
143
|
-
return null; // Need company info or LinkedIn URL
|
|
144
|
-
}
|
|
145
|
-
// Submit single contact as batch
|
|
146
|
-
const requestId = await submitBatch([contact]);
|
|
147
|
-
if (!requestId) {
|
|
148
|
-
return null;
|
|
149
|
-
}
|
|
150
|
-
// Poll for results
|
|
151
|
-
const results = await pollResults(requestId);
|
|
152
|
-
if (!results || results.length === 0) {
|
|
153
|
-
return null;
|
|
154
|
-
}
|
|
155
|
-
// Extract email from first result
|
|
156
|
-
const enriched = results[0];
|
|
157
|
-
if (!enriched.email || enriched.email.length === 0) {
|
|
158
|
-
return null;
|
|
159
|
-
}
|
|
160
|
-
// Find the best email (prefer valid ones)
|
|
161
|
-
let bestEmail = null;
|
|
162
|
-
for (const emailObj of enriched.email) {
|
|
163
|
-
if (!emailObj.email)
|
|
164
|
-
continue;
|
|
165
|
-
// Prefer valid emails
|
|
166
|
-
if (emailObj.qualification === "valid") {
|
|
167
|
-
bestEmail = emailObj;
|
|
168
|
-
break;
|
|
169
|
-
}
|
|
170
|
-
// Take catch-all if no valid found yet
|
|
171
|
-
if (!bestEmail ||
|
|
172
|
-
(bestEmail.qualification !== "valid" &&
|
|
173
|
-
emailObj.qualification === "catch_all")) {
|
|
174
|
-
bestEmail = emailObj;
|
|
175
|
-
}
|
|
176
|
-
// Take any email if nothing else
|
|
177
|
-
if (!bestEmail) {
|
|
178
|
-
bestEmail = emailObj;
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
if (!bestEmail || !bestEmail.email) {
|
|
182
|
-
return null;
|
|
183
|
-
}
|
|
184
|
-
// Map qualification to verified status
|
|
185
|
-
const verified = (0, http_retry_1.mapVerifiedStatus)(bestEmail.qualification);
|
|
186
|
-
// Confidence based on qualification
|
|
187
|
-
let score = 50;
|
|
188
|
-
if (bestEmail.qualification === "valid") {
|
|
189
|
-
score = 95;
|
|
190
|
-
}
|
|
191
|
-
else if (bestEmail.qualification === "catch_all") {
|
|
192
|
-
score = 60;
|
|
193
|
-
}
|
|
194
|
-
else if (bestEmail.qualification === "invalid") {
|
|
195
|
-
score = 10;
|
|
196
|
-
}
|
|
197
|
-
return {
|
|
198
|
-
email: bestEmail.email,
|
|
199
|
-
verified,
|
|
200
|
-
score,
|
|
201
|
-
};
|
|
202
|
-
}
|
|
203
|
-
// Mark provider name for orchestrator
|
|
204
|
-
fetchEmail.__name = "dropcontact";
|
|
205
|
-
return fetchEmail;
|
|
206
|
-
}
|