linkedin-secret-sauce 0.12.3 → 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 +6 -49
- package/dist/enrichment/index.d.ts +3 -5
- package/dist/enrichment/index.js +4 -20
- 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 +8 -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/ENRICHMENT.md +3 -45
- package/docs/INTEGRATION.md +1 -3
- package/docs/PLAYGROUND.md +4 -9
- package/docs/api/assets/hierarchy.js +1 -1
- package/docs/api/assets/navigation.js +1 -1
- package/docs/api/assets/search.js +1 -1
- package/docs/api/classes/LinkedInClientError.html +4 -4
- package/docs/api/functions/_testGetAccountCookies.html +2 -2
- package/docs/api/functions/_testGetAccountEntry.html +2 -2
- package/docs/api/functions/_testGetAllAccountIds.html +2 -2
- package/docs/api/functions/_testGetPoolState.html +2 -2
- package/docs/api/functions/adminResetAccount.html +1 -1
- package/docs/api/functions/adminSetCooldown.html +1 -1
- package/docs/api/functions/buildCookieHeader.html +1 -1
- package/docs/api/functions/clearAllSmartLeadTokens.html +2 -2
- package/docs/api/functions/clearRequestHistory.html +1 -1
- package/docs/api/functions/clearSessionAccount.html +1 -1
- package/docs/api/functions/clearSmartLeadToken.html +2 -2
- package/docs/api/functions/createEnrichmentClient.html +3 -3
- package/docs/api/functions/extractCsrfToken.html +1 -1
- package/docs/api/functions/extractLinkedInHandle.html +2 -2
- package/docs/api/functions/fetchCookiesFromCosiall.html +2 -2
- package/docs/api/functions/fetchProfileEmailsFromCosiall.html +2 -2
- package/docs/api/functions/forceRefreshCookies.html +1 -1
- package/docs/api/functions/getAccountForSession.html +1 -1
- package/docs/api/functions/getAccountsSummary.html +1 -1
- package/docs/api/functions/getCompaniesBatch.html +2 -2
- package/docs/api/functions/getCompanyById.html +2 -2
- package/docs/api/functions/getCompanyByUrl.html +1 -1
- package/docs/api/functions/getConfig.html +1 -1
- package/docs/api/functions/getCookiePoolHealth.html +1 -1
- package/docs/api/functions/getProfileByUrn.html +2 -2
- package/docs/api/functions/getProfileByVanity.html +2 -2
- package/docs/api/functions/getProfilesBatch.html +1 -1
- package/docs/api/functions/getRequestHistory.html +1 -1
- package/docs/api/functions/getSalesNavigatorProfileDetails.html +1 -1
- package/docs/api/functions/getSalesNavigatorProfileFull.html +2 -2
- package/docs/api/functions/getSmartLeadToken.html +1 -1
- package/docs/api/functions/getSmartLeadTokenCacheStats.html +2 -2
- package/docs/api/functions/getSmartLeadUser.html +2 -2
- package/docs/api/functions/getSnapshot.html +1 -1
- package/docs/api/functions/getYearsAtCompanyOptions.html +2 -2
- package/docs/api/functions/getYearsInPositionOptions.html +2 -2
- package/docs/api/functions/getYearsOfExperienceOptions.html +2 -2
- package/docs/api/functions/incrementMetric.html +1 -1
- package/docs/api/functions/initializeCookiePool.html +1 -1
- package/docs/api/functions/initializeLinkedInClient.html +1 -1
- package/docs/api/functions/isBusinessEmail.html +2 -2
- package/docs/api/functions/isDisposableDomain.html +2 -2
- package/docs/api/functions/isDisposableEmail.html +2 -2
- package/docs/api/functions/isPersonalDomain.html +2 -2
- package/docs/api/functions/isPersonalEmail.html +2 -2
- package/docs/api/functions/isRoleAccount.html +2 -2
- package/docs/api/functions/isValidEmailSyntax.html +2 -2
- package/docs/api/functions/parseFullProfile.html +2 -2
- package/docs/api/functions/parseSalesSearchResults.html +1 -1
- package/docs/api/functions/reportAccountFailure.html +1 -1
- package/docs/api/functions/reportAccountSuccess.html +1 -1
- package/docs/api/functions/resolveCompanyUniversalName.html +1 -1
- package/docs/api/functions/searchSalesLeads.html +2 -2
- package/docs/api/functions/selectAccountForRequest.html +1 -1
- package/docs/api/functions/setAccountForSession.html +1 -1
- package/docs/api/functions/typeahead.html +1 -1
- package/docs/api/functions/verifyEmailMx.html +1 -1
- package/docs/api/hierarchy.html +1 -1
- package/docs/api/index.html +3 -3
- package/docs/api/interfaces/AccountCookies.html +2 -2
- package/docs/api/interfaces/BatchEnrichmentOptions.html +8 -8
- package/docs/api/interfaces/CacheAdapter.html +4 -4
- package/docs/api/interfaces/CanonicalEmail.html +8 -8
- package/docs/api/interfaces/Company.html +2 -2
- package/docs/api/interfaces/ConstructConfig.html +5 -5
- package/docs/api/interfaces/CosiallProfileEmailsResponse.html +6 -6
- package/docs/api/interfaces/EnrichmentCandidate.html +4 -4
- package/docs/api/interfaces/EnrichmentClient.html +6 -6
- package/docs/api/interfaces/EnrichmentClientConfig.html +7 -7
- package/docs/api/interfaces/EnrichmentLogger.html +3 -3
- package/docs/api/interfaces/EnrichmentOptions.html +6 -6
- package/docs/api/interfaces/HunterConfig.html +3 -3
- package/docs/api/interfaces/LddConfig.html +3 -3
- package/docs/api/interfaces/LddProfileData.html +2 -2
- package/docs/api/interfaces/LinkedInClientConfig.html +2 -2
- package/docs/api/interfaces/LinkedInCookie.html +2 -2
- package/docs/api/interfaces/LinkedInPosition.html +2 -2
- package/docs/api/interfaces/LinkedInProfile.html +2 -2
- package/docs/api/interfaces/LinkedInSpotlightBadge.html +2 -2
- package/docs/api/interfaces/LinkedInTenure.html +2 -2
- package/docs/api/interfaces/Metrics.html +2 -2
- package/docs/api/interfaces/MetricsSnapshot.html +2 -2
- package/docs/api/interfaces/ProfileEducation.html +2 -2
- package/docs/api/interfaces/ProfileEmailsLookupOptions.html +5 -5
- package/docs/api/interfaces/ProfilePosition.html +2 -2
- package/docs/api/interfaces/ProfileSkill.html +2 -2
- package/docs/api/interfaces/ProviderResult.html +6 -6
- package/docs/api/interfaces/ProvidersConfig.html +6 -9
- package/docs/api/interfaces/RequestHistoryEntry.html +2 -2
- package/docs/api/interfaces/SalesLeadSearchResult.html +2 -2
- package/docs/api/interfaces/SalesNavigatorContactInfo.html +2 -2
- package/docs/api/interfaces/SalesNavigatorPosition.html +2 -2
- package/docs/api/interfaces/SalesNavigatorProfile.html +2 -2
- package/docs/api/interfaces/SalesNavigatorProfileFull.html +4 -4
- package/docs/api/interfaces/SearchSalesResult.html +2 -2
- package/docs/api/interfaces/SmartLeadAuthConfig.html +4 -4
- package/docs/api/interfaces/SmartLeadCredentials.html +2 -2
- package/docs/api/interfaces/SmartLeadLoginResponse.html +2 -2
- package/docs/api/interfaces/SmartLeadUser.html +2 -2
- package/docs/api/interfaces/SmartProspectConfig.html +8 -8
- package/docs/api/interfaces/SmartProspectContact.html +2 -2
- package/docs/api/interfaces/SmartProspectSearchFilters.html +21 -21
- package/docs/api/interfaces/TypeaheadItem.html +2 -2
- package/docs/api/interfaces/TypeaheadResult.html +2 -2
- package/docs/api/interfaces/VerificationResult.html +9 -9
- package/docs/api/types/CostCallback.html +2 -2
- package/docs/api/types/Geo.html +2 -2
- package/docs/api/types/LddApiResponse.html +1 -1
- package/docs/api/types/ProviderFunc.html +2 -2
- package/docs/api/types/ProviderName.html +2 -2
- package/docs/api/types/SalesSearchFilters.html +2 -2
- package/docs/api/types/TypeaheadType.html +1 -1
- package/docs/api/variables/COMPANY_SIZE_OPTIONS.html +1 -1
- package/docs/api/variables/DEFAULT_PROVIDER_ORDER.html +3 -4
- package/docs/api/variables/DISPOSABLE_DOMAINS.html +2 -2
- package/docs/api/variables/FUNCTION_OPTIONS.html +1 -1
- package/docs/api/variables/INDUSTRY_OPTIONS.html +1 -1
- package/docs/api/variables/LANGUAGE_OPTIONS.html +1 -1
- package/docs/api/variables/PERSONAL_DOMAINS.html +2 -2
- package/docs/api/variables/PROVIDER_COSTS.html +3 -5
- package/docs/api/variables/REGION_OPTIONS.html +1 -1
- package/docs/api/variables/SENIORITY_OPTIONS.html +2 -2
- package/docs/api/variables/YEARS_OPTIONS.html +1 -1
- package/package.json +1 -1
- 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
- package/docs/api/interfaces/DropcontactConfig.html +0 -3
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ A complete LinkedIn Sales Navigator client + Email Enrichment library for Node.j
|
|
|
4
4
|
|
|
5
5
|
**Two main modules:**
|
|
6
6
|
1. **LinkedIn API** - Sales Navigator search, profiles, companies with automatic cookie/account rotation
|
|
7
|
-
2. **Email Enrichment** - Find business emails via multiple providers (
|
|
7
|
+
2. **Email Enrichment** - Find business emails via multiple providers (Hunter, TryKitt.ai, BounceBan, Snov.io, etc.)
|
|
8
8
|
|
|
9
9
|
## Documentation
|
|
10
10
|
|
|
@@ -250,9 +250,7 @@ Find and verify business emails using multiple providers with waterfall/parallel
|
|
|
250
250
|
| **TryKitt.ai** | AI Finder | FREE** | AI-powered email finding with catch-all verification |
|
|
251
251
|
| **Construct** | Pattern + MX | FREE | Pattern guessing with MX verification |
|
|
252
252
|
| **BounceBan** | Verification | FREE/$0.003 | Catch-all specialist (85-95% accuracy) |
|
|
253
|
-
| **
|
|
254
|
-
| **Hunter** | Email Finder | $0.005/email | Domain search, email finder |
|
|
255
|
-
| **Dropcontact** | Email Finder | $0.01/email | Email finding API |
|
|
253
|
+
| **Hunter** | Email Finder | $0.015/email | Domain search, email finder |
|
|
256
254
|
| **Snov.io** | Email Finder | $0.02/email | Finding emails by name+domain |
|
|
257
255
|
|
|
258
256
|
*SmartProspect is FREE if you have a SmartLead subscription
|
|
@@ -274,7 +272,7 @@ PHASE 2 - Pattern + Verification (if Phase 1 < 80% confidence):
|
|
|
274
272
|
└── BounceBan → Catch-all verification (FREE single)
|
|
275
273
|
|
|
276
274
|
PHASE 3 - Paid Finders (only if Phase 2 inconclusive):
|
|
277
|
-
├── Hunter → $0.
|
|
275
|
+
├── Hunter → $0.015/email
|
|
278
276
|
└── Snov.io → $0.02/email
|
|
279
277
|
```
|
|
280
278
|
|
|
@@ -335,12 +333,11 @@ const enrichment = createEnrichmentClient({
|
|
|
335
333
|
},
|
|
336
334
|
|
|
337
335
|
// PAID providers - only used if FREE providers don't find email
|
|
338
|
-
|
|
336
|
+
hunter: { apiKey: process.env.HUNTER_API_KEY },
|
|
339
337
|
snovio: {
|
|
340
338
|
clientId: process.env.SNOVIO_CLIENT_ID,
|
|
341
339
|
clientSecret: process.env.SNOVIO_CLIENT_SECRET
|
|
342
340
|
},
|
|
343
|
-
hunter: { apiKey: process.env.HUNTER_API_KEY },
|
|
344
341
|
},
|
|
345
342
|
|
|
346
343
|
// Optional: cost tracking callback
|
|
@@ -495,40 +492,6 @@ batchResults.forEach((result, email) => {
|
|
|
495
492
|
});
|
|
496
493
|
```
|
|
497
494
|
|
|
498
|
-
### Email Verification with Bouncer
|
|
499
|
-
|
|
500
|
-
```typescript
|
|
501
|
-
import {
|
|
502
|
-
verifyEmailWithBouncer,
|
|
503
|
-
checkCatchAllDomain,
|
|
504
|
-
verifyEmailsBatch
|
|
505
|
-
} from 'linkedin-secret-sauce';
|
|
506
|
-
|
|
507
|
-
// Verify a single email
|
|
508
|
-
const verification = await verifyEmailWithBouncer(
|
|
509
|
-
'john@example.com',
|
|
510
|
-
{ apiKey: process.env.BOUNCER_API_KEY }
|
|
511
|
-
);
|
|
512
|
-
|
|
513
|
-
console.log(verification.status); // 'deliverable' | 'undeliverable' | 'risky'
|
|
514
|
-
console.log(verification.reason); // 'accepted_email' | 'rejected_email' | etc
|
|
515
|
-
console.log(verification.acceptAll); // true = catch-all domain (can't verify)
|
|
516
|
-
console.log(verification.disposable); // Is this a temp email?
|
|
517
|
-
console.log(verification.role); // Is this info@, support@, etc?
|
|
518
|
-
|
|
519
|
-
// Check if domain is catch-all (accepts any email)
|
|
520
|
-
const isCatchAll = await checkCatchAllDomain('example.com', config);
|
|
521
|
-
if (isCatchAll) {
|
|
522
|
-
console.log('Cannot verify emails - domain accepts everything');
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
// Batch verify (with concurrency control)
|
|
526
|
-
const results = await verifyEmailsBatch(
|
|
527
|
-
['john@example.com', 'jane@example.com'],
|
|
528
|
-
{ apiKey: process.env.BOUNCER_API_KEY }
|
|
529
|
-
);
|
|
530
|
-
```
|
|
531
|
-
|
|
532
495
|
### Email Finding with Snov.io
|
|
533
496
|
|
|
534
497
|
```typescript
|
|
@@ -598,8 +561,7 @@ const enrichment = createEnrichmentClient({
|
|
|
598
561
|
'trykitt', // FREE - AI finder
|
|
599
562
|
'construct', // FREE - pattern matching
|
|
600
563
|
'bounceban', // FREE single / $0.003 bulk
|
|
601
|
-
'hunter', // $0.
|
|
602
|
-
'bouncer', // $0.006 (verification only)
|
|
564
|
+
'hunter', // $0.015
|
|
603
565
|
'snovio', // $0.02
|
|
604
566
|
],
|
|
605
567
|
|
|
@@ -671,8 +633,6 @@ const result = await getEmailsForLinkedInContact(
|
|
|
671
633
|
apiKey: process.env.BOUNCEBAN_API_KEY,
|
|
672
634
|
useDeepVerify: true,
|
|
673
635
|
},
|
|
674
|
-
// Bouncer for final verification
|
|
675
|
-
bouncer: { apiKey: process.env.BOUNCER_API_KEY },
|
|
676
636
|
}
|
|
677
637
|
);
|
|
678
638
|
|
|
@@ -716,7 +676,6 @@ const enrichment = createEnrichmentClient({
|
|
|
716
676
|
trykitt: { apiKey: process.env.TRYKITT_API_KEY },
|
|
717
677
|
bounceban: { apiKey: process.env.BOUNCEBAN_API_KEY, useDeepVerify: true },
|
|
718
678
|
hunter: { apiKey: process.env.HUNTER_API_KEY },
|
|
719
|
-
bouncer: { apiKey: process.env.BOUNCER_API_KEY },
|
|
720
679
|
},
|
|
721
680
|
});
|
|
722
681
|
|
|
@@ -786,9 +745,7 @@ TRYKITT_API_KEY=... # FREE for individuals
|
|
|
786
745
|
BOUNCEBAN_API_KEY=... # FREE single, $0.003 bulk
|
|
787
746
|
|
|
788
747
|
# Email Enrichment - PAID providers
|
|
789
|
-
HUNTER_API_KEY=... # $0.
|
|
790
|
-
BOUNCER_API_KEY=... # $0.006/email
|
|
791
|
-
DROPCONTACT_API_KEY=... # $0.01/email
|
|
748
|
+
HUNTER_API_KEY=... # $0.015/email
|
|
792
749
|
SNOVIO_CLIENT_ID=... # $0.02/email
|
|
793
750
|
SNOVIO_CLIENT_SECRET=...
|
|
794
751
|
```
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* providers: {
|
|
13
13
|
* ldd: { apiUrl: process.env.LDD_API_URL, apiToken: process.env.LDD_API_TOKEN },
|
|
14
14
|
* smartprospect: { email: process.env.SMARTLEAD_EMAIL, password: process.env.SMARTLEAD_PASSWORD },
|
|
15
|
-
*
|
|
15
|
+
* bounceban: { apiKey: process.env.BOUNCEBAN_API_KEY },
|
|
16
16
|
* },
|
|
17
17
|
* options: {
|
|
18
18
|
* maxCostPerEmail: 0.05,
|
|
@@ -44,10 +44,8 @@ import { type EnrichmentClientConfig, type EnrichmentClient } from "./types";
|
|
|
44
44
|
* - bounceban: BounceBan catch-all verification (FREE single / $0.003 bulk)
|
|
45
45
|
*
|
|
46
46
|
* PHASE 3 - Paid finders (only if Phase 2 inconclusive):
|
|
47
|
-
* - hunter: Hunter.io ($0.
|
|
47
|
+
* - hunter: Hunter.io ($0.015/email)
|
|
48
48
|
* - snovio: Snov.io ($0.02/email)
|
|
49
|
-
*
|
|
50
|
-
* Note: bouncer ($0.006) and dropcontact ($0.01) available but not in default order
|
|
51
49
|
*/
|
|
52
50
|
/**
|
|
53
51
|
* Create an enrichment client with the given configuration
|
|
@@ -62,7 +60,7 @@ export { isPersonalEmail, isBusinessEmail, isPersonalDomain, PERSONAL_DOMAINS, }
|
|
|
62
60
|
export { isDisposableEmail, isDisposableDomain, DISPOSABLE_DOMAINS, } from "./utils/disposable-domains";
|
|
63
61
|
export { isValidEmailSyntax, isRoleAccount, asciiFold, cleanNamePart, hostnameFromUrl, extractLinkedInUsername, } from "./utils/validation";
|
|
64
62
|
export { verifyEmailMx, checkDomainCatchAll, verifyEmailsExist, } from "./verification/mx";
|
|
65
|
-
export { createConstructProvider, createLddProvider, createSmartProspectProvider, createCosiallProvider, createTryKittProvider, createHunterProvider,
|
|
63
|
+
export { createConstructProvider, createLddProvider, createSmartProspectProvider, createCosiallProvider, createTryKittProvider, createHunterProvider, createBounceBanProvider, createSnovioProvider, findEmailWithTryKitt, verifyEmailWithTryKitt, verifyEmailWithBounceBan, verifyEmailsBatchWithBounceBan, checkCatchAllWithBounceBan, findEmailsWithSnovio, verifyEmailWithSnovio, clearSnovioTokenCache, } from "./providers";
|
|
66
64
|
export { extractNumericLinkedInId } from "./providers/ldd";
|
|
67
65
|
export { createSmartProspectClient, type SmartProspectClient, type SmartProspectLocationOptions, } from "./providers/smartprospect";
|
|
68
66
|
export { enrichBusinessEmail, enrichBatch, enrichAllEmails, enrichAllBatch, } from "./orchestrator";
|
package/dist/enrichment/index.js
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* providers: {
|
|
14
14
|
* ldd: { apiUrl: process.env.LDD_API_URL, apiToken: process.env.LDD_API_TOKEN },
|
|
15
15
|
* smartprospect: { email: process.env.SMARTLEAD_EMAIL, password: process.env.SMARTLEAD_PASSWORD },
|
|
16
|
-
*
|
|
16
|
+
* bounceban: { apiKey: process.env.BOUNCEBAN_API_KEY },
|
|
17
17
|
* },
|
|
18
18
|
* options: {
|
|
19
19
|
* maxCostPerEmail: 0.05,
|
|
@@ -43,8 +43,8 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
43
43
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
44
44
|
};
|
|
45
45
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
46
|
-
exports.
|
|
47
|
-
exports.salesLeadToContact = exports.getEmailsForLinkedInContactsBatch = exports.getEmailsForLinkedInContact = exports.createLinkedInEnricher = exports.enrichLinkedInContactsBatch = exports.enrichLinkedInContact = exports.parseLinkedInSearchResponse = exports.buildSmartProspectFiltersFromLinkedIn = exports.matchContacts = exports.findBestMatch = exports.classifyMatchQuality =
|
|
46
|
+
exports.calculateMatchConfidence = exports.clearFileCache = exports.isFileCacheEnabled = exports.disableFileCache = exports.enableFileCache = exports.getSmartLeadTokenCacheStats = exports.clearAllSmartLeadTokens = exports.clearSmartLeadToken = exports.getSmartLeadUser = exports.getSmartLeadToken = exports.enrichAllBatch = exports.enrichAllEmails = exports.enrichBatch = exports.enrichBusinessEmail = exports.createSmartProspectClient = exports.extractNumericLinkedInId = exports.clearSnovioTokenCache = exports.verifyEmailWithSnovio = exports.findEmailsWithSnovio = exports.checkCatchAllWithBounceBan = exports.verifyEmailsBatchWithBounceBan = exports.verifyEmailWithBounceBan = exports.verifyEmailWithTryKitt = exports.findEmailWithTryKitt = exports.createSnovioProvider = exports.createBounceBanProvider = exports.createHunterProvider = exports.createTryKittProvider = exports.createCosiallProvider = exports.createSmartProspectProvider = exports.createLddProvider = exports.createConstructProvider = exports.verifyEmailsExist = exports.checkDomainCatchAll = exports.verifyEmailMx = exports.extractLinkedInUsername = exports.hostnameFromUrl = exports.cleanNamePart = exports.asciiFold = exports.isRoleAccount = exports.isValidEmailSyntax = exports.DISPOSABLE_DOMAINS = exports.isDisposableDomain = exports.isDisposableEmail = exports.PERSONAL_DOMAINS = exports.isPersonalDomain = exports.isBusinessEmail = exports.isPersonalEmail = exports.DEFAULT_PROVIDER_ORDER = exports.PROVIDER_COSTS = void 0;
|
|
47
|
+
exports.salesLeadToContact = exports.getEmailsForLinkedInContactsBatch = exports.getEmailsForLinkedInContact = exports.createLinkedInEnricher = exports.enrichLinkedInContactsBatch = exports.enrichLinkedInContact = exports.parseLinkedInSearchResponse = exports.buildSmartProspectFiltersFromLinkedIn = exports.matchContacts = exports.findBestMatch = exports.classifyMatchQuality = void 0;
|
|
48
48
|
exports.createEnrichmentClient = createEnrichmentClient;
|
|
49
49
|
const types_1 = require("./types");
|
|
50
50
|
const orchestrator_1 = require("./orchestrator");
|
|
@@ -54,8 +54,6 @@ const smartprospect_1 = require("./providers/smartprospect");
|
|
|
54
54
|
const cosiall_1 = require("./providers/cosiall");
|
|
55
55
|
const trykitt_1 = require("./providers/trykitt");
|
|
56
56
|
const hunter_1 = require("./providers/hunter");
|
|
57
|
-
const dropcontact_1 = require("./providers/dropcontact");
|
|
58
|
-
const bouncer_1 = require("./providers/bouncer");
|
|
59
57
|
const bounceban_1 = require("./providers/bounceban");
|
|
60
58
|
const snovio_1 = require("./providers/snovio");
|
|
61
59
|
/**
|
|
@@ -74,10 +72,8 @@ const snovio_1 = require("./providers/snovio");
|
|
|
74
72
|
* - bounceban: BounceBan catch-all verification (FREE single / $0.003 bulk)
|
|
75
73
|
*
|
|
76
74
|
* PHASE 3 - Paid finders (only if Phase 2 inconclusive):
|
|
77
|
-
* - hunter: Hunter.io ($0.
|
|
75
|
+
* - hunter: Hunter.io ($0.015/email)
|
|
78
76
|
* - snovio: Snov.io ($0.02/email)
|
|
79
|
-
*
|
|
80
|
-
* Note: bouncer ($0.006) and dropcontact ($0.01) available but not in default order
|
|
81
77
|
*/
|
|
82
78
|
/**
|
|
83
79
|
* Create an enrichment client with the given configuration
|
|
@@ -109,12 +105,6 @@ function createEnrichmentClient(config) {
|
|
|
109
105
|
if (providerConfigs.hunter) {
|
|
110
106
|
providerFuncs.set("hunter", (0, hunter_1.createHunterProvider)(providerConfigs.hunter));
|
|
111
107
|
}
|
|
112
|
-
if (providerConfigs.dropcontact) {
|
|
113
|
-
providerFuncs.set("dropcontact", (0, dropcontact_1.createDropcontactProvider)(providerConfigs.dropcontact));
|
|
114
|
-
}
|
|
115
|
-
if (providerConfigs.bouncer) {
|
|
116
|
-
providerFuncs.set("bouncer", (0, bouncer_1.createBouncerProvider)(providerConfigs.bouncer));
|
|
117
|
-
}
|
|
118
108
|
if (providerConfigs.snovio) {
|
|
119
109
|
providerFuncs.set("snovio", (0, snovio_1.createSnovioProvider)(providerConfigs.snovio));
|
|
120
110
|
}
|
|
@@ -287,17 +277,11 @@ Object.defineProperty(exports, "createSmartProspectProvider", { enumerable: true
|
|
|
287
277
|
Object.defineProperty(exports, "createCosiallProvider", { enumerable: true, get: function () { return providers_1.createCosiallProvider; } });
|
|
288
278
|
Object.defineProperty(exports, "createTryKittProvider", { enumerable: true, get: function () { return providers_1.createTryKittProvider; } });
|
|
289
279
|
Object.defineProperty(exports, "createHunterProvider", { enumerable: true, get: function () { return providers_1.createHunterProvider; } });
|
|
290
|
-
Object.defineProperty(exports, "createDropcontactProvider", { enumerable: true, get: function () { return providers_1.createDropcontactProvider; } });
|
|
291
|
-
Object.defineProperty(exports, "createBouncerProvider", { enumerable: true, get: function () { return providers_1.createBouncerProvider; } });
|
|
292
280
|
Object.defineProperty(exports, "createBounceBanProvider", { enumerable: true, get: function () { return providers_1.createBounceBanProvider; } });
|
|
293
281
|
Object.defineProperty(exports, "createSnovioProvider", { enumerable: true, get: function () { return providers_1.createSnovioProvider; } });
|
|
294
282
|
// TryKitt utilities
|
|
295
283
|
Object.defineProperty(exports, "findEmailWithTryKitt", { enumerable: true, get: function () { return providers_1.findEmailWithTryKitt; } });
|
|
296
284
|
Object.defineProperty(exports, "verifyEmailWithTryKitt", { enumerable: true, get: function () { return providers_1.verifyEmailWithTryKitt; } });
|
|
297
|
-
// Bouncer utilities
|
|
298
|
-
Object.defineProperty(exports, "verifyEmailWithBouncer", { enumerable: true, get: function () { return providers_1.verifyEmailWithBouncer; } });
|
|
299
|
-
Object.defineProperty(exports, "checkCatchAllDomain", { enumerable: true, get: function () { return providers_1.checkCatchAllDomain; } });
|
|
300
|
-
Object.defineProperty(exports, "verifyEmailsBatch", { enumerable: true, get: function () { return providers_1.verifyEmailsBatch; } });
|
|
301
285
|
// BounceBan utilities
|
|
302
286
|
Object.defineProperty(exports, "verifyEmailWithBounceBan", { enumerable: true, get: function () { return providers_1.verifyEmailWithBounceBan; } });
|
|
303
287
|
Object.defineProperty(exports, "verifyEmailsBatchWithBounceBan", { enumerable: true, get: function () { return providers_1.verifyEmailsBatchWithBounceBan; } });
|
|
@@ -258,7 +258,7 @@ export declare function createLinkedInEnricher(smartProspectConfig: SmartProspec
|
|
|
258
258
|
/**
|
|
259
259
|
* Email source - where the email was found
|
|
260
260
|
*/
|
|
261
|
-
export type EmailSource = "ldd" | "smartprospect" | "cosiall" | "trykitt" | "linkedin" | "pattern" | "hunter" | "
|
|
261
|
+
export type EmailSource = "ldd" | "smartprospect" | "cosiall" | "trykitt" | "linkedin" | "pattern" | "hunter" | "bounceban" | "snovio";
|
|
262
262
|
/**
|
|
263
263
|
* Email result from unified lookup
|
|
264
264
|
*/
|
|
@@ -327,10 +327,6 @@ export interface GetEmailsConfig {
|
|
|
327
327
|
hunter?: {
|
|
328
328
|
apiKey: string;
|
|
329
329
|
};
|
|
330
|
-
/** Bouncer configuration (PAID - SMTP verification) */
|
|
331
|
-
bouncer?: {
|
|
332
|
-
apiKey: string;
|
|
333
|
-
};
|
|
334
330
|
/** BounceBan configuration (FREE single / catch-all specialist) */
|
|
335
331
|
bounceban?: {
|
|
336
332
|
apiKey: string;
|
|
@@ -339,8 +335,8 @@ export interface GetEmailsConfig {
|
|
|
339
335
|
};
|
|
340
336
|
/** Snov.io configuration (PAID - email finder) */
|
|
341
337
|
snovio?: {
|
|
342
|
-
|
|
343
|
-
|
|
338
|
+
clientId: string;
|
|
339
|
+
clientSecret: string;
|
|
344
340
|
};
|
|
345
341
|
}
|
|
346
342
|
/**
|
|
@@ -376,7 +372,7 @@ export interface GetEmailsOptions {
|
|
|
376
372
|
* Strategy (optimized for FlexIQ):
|
|
377
373
|
* 1. Query LDD + SmartProspect in PARALLEL (both FREE for you)
|
|
378
374
|
* 2. Try email pattern guessing with MX verification (FREE)
|
|
379
|
-
* 3. Only use Hunter/
|
|
375
|
+
* 3. Only use Hunter/Snov.io if confidence is below threshold (PAID - last resort)
|
|
380
376
|
*
|
|
381
377
|
* @example
|
|
382
378
|
* ```ts
|
|
@@ -738,7 +738,7 @@ function createLinkedInEnricher(smartProspectConfig, defaultOptions = {}) {
|
|
|
738
738
|
* Strategy (optimized for FlexIQ):
|
|
739
739
|
* 1. Query LDD + SmartProspect in PARALLEL (both FREE for you)
|
|
740
740
|
* 2. Try email pattern guessing with MX verification (FREE)
|
|
741
|
-
* 3. Only use Hunter/
|
|
741
|
+
* 3. Only use Hunter/Snov.io if confidence is below threshold (PAID - last resort)
|
|
742
742
|
*
|
|
743
743
|
* @example
|
|
744
744
|
* ```ts
|
|
@@ -957,7 +957,7 @@ async function getEmailsForLinkedInContact(contactOrLead, config, options = {})
|
|
|
957
957
|
// ==========================================================================
|
|
958
958
|
if (!skipPaidProviders && finalBestConfidence < paidProviderThreshold) {
|
|
959
959
|
// Only use paid providers if we have low confidence or no results
|
|
960
|
-
// Hunter.io - Email Finder ($0.
|
|
960
|
+
// Hunter.io - Email Finder ($0.015/lookup)
|
|
961
961
|
if (config.hunter?.apiKey) {
|
|
962
962
|
result.providersQueried.push("hunter");
|
|
963
963
|
// Extract linkedin handle from contact if available
|
|
@@ -981,7 +981,9 @@ async function getEmailsForLinkedInContact(contactOrLead, config, options = {})
|
|
|
981
981
|
result.emails = mergeAndBoostEmails();
|
|
982
982
|
}
|
|
983
983
|
// Snovio - Email Finder ($0.02/lookup) - best for catch-all domains
|
|
984
|
-
if (config.snovio?.
|
|
984
|
+
if (config.snovio?.clientId &&
|
|
985
|
+
config.snovio?.clientSecret &&
|
|
986
|
+
companyDomain) {
|
|
985
987
|
result.providersQueried.push("snovio");
|
|
986
988
|
await querySnovio(contact, config.snovio, companyDomain, addEmail, result);
|
|
987
989
|
// Re-merge after Snovio
|
|
@@ -1004,8 +1006,7 @@ async function getEmailsForLinkedInContact(contactOrLead, config, options = {})
|
|
|
1004
1006
|
pattern: 5,
|
|
1005
1007
|
bounceban: 6, // BounceBan catch-all verification (FREE single)
|
|
1006
1008
|
hunter: 7,
|
|
1007
|
-
|
|
1008
|
-
snovio: 9,
|
|
1009
|
+
snovio: 8,
|
|
1009
1010
|
};
|
|
1010
1011
|
result.emails.sort((a, b) => {
|
|
1011
1012
|
if (b.confidence !== a.confidence) {
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Runs providers in sequence until a verified email is found that meets
|
|
5
5
|
* the confidence threshold. Supports budget tracking and cost callbacks.
|
|
6
6
|
*/
|
|
7
|
-
import type { CanonicalEmail, EnrichmentCandidate, ProviderFunc, BatchEnrichmentOptions, CostCallback, EnrichmentLogger, MultiEmailResult } from
|
|
7
|
+
import type { CanonicalEmail, EnrichmentCandidate, ProviderFunc, BatchEnrichmentOptions, CostCallback, EnrichmentLogger, MultiEmailResult } from "./types";
|
|
8
8
|
export interface OrchestratorOptions {
|
|
9
9
|
/** Provider functions in order */
|
|
10
10
|
providers: ProviderFunc[];
|
|
@@ -20,18 +20,16 @@ const validation_1 = require("./utils/validation");
|
|
|
20
20
|
* - ldd: FREE (subscription-based)
|
|
21
21
|
* - smartprospect: FREE (included in SmartLead subscription)
|
|
22
22
|
* - construct: FREE (pattern guessing + MX check)
|
|
23
|
-
* -
|
|
23
|
+
* - bounceban: FREE single / $0.003 bulk (catch-all specialist)
|
|
24
|
+
* - hunter: $0.015/email (Growth tier)
|
|
24
25
|
* - snovio: $0.02/email (email finding + verification)
|
|
25
|
-
* - hunter: $0.005/email
|
|
26
|
-
* - dropcontact: $0.01/email (not in default order)
|
|
27
26
|
*/
|
|
28
27
|
const _PROVIDER_COSTS = {
|
|
29
28
|
construct: 0,
|
|
30
29
|
ldd: 0,
|
|
31
30
|
smartprospect: 0,
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
bouncer: 0.006,
|
|
31
|
+
bounceban: 0.003,
|
|
32
|
+
hunter: 0.015,
|
|
35
33
|
snovio: 0.02,
|
|
36
34
|
};
|
|
37
35
|
/**
|
|
@@ -49,9 +47,9 @@ function normalizeProviderResult(providerName, raw) {
|
|
|
49
47
|
};
|
|
50
48
|
}
|
|
51
49
|
const email = String(raw.email);
|
|
52
|
-
const confidence = typeof raw.score ===
|
|
50
|
+
const confidence = typeof raw.score === "number"
|
|
53
51
|
? raw.score
|
|
54
|
-
: typeof raw.confidence ===
|
|
52
|
+
: typeof raw.confidence === "number"
|
|
55
53
|
? raw.confidence
|
|
56
54
|
: undefined;
|
|
57
55
|
const verified = Boolean(raw.verified);
|
|
@@ -67,7 +65,8 @@ function normalizeProviderResult(providerName, raw) {
|
|
|
67
65
|
* Get provider name from function
|
|
68
66
|
*/
|
|
69
67
|
function getProviderName(provider, index) {
|
|
70
|
-
return provider.__name ??
|
|
68
|
+
return (provider.__name ??
|
|
69
|
+
`provider-${index}`);
|
|
71
70
|
}
|
|
72
71
|
/**
|
|
73
72
|
* Get provider cost
|
|
@@ -79,7 +78,7 @@ function getProviderCost(providerName) {
|
|
|
79
78
|
* Check if a provider result is a multi-result (returns multiple emails)
|
|
80
79
|
*/
|
|
81
80
|
function isMultiResult(result) {
|
|
82
|
-
return result !== null &&
|
|
81
|
+
return result !== null && "emails" in result && Array.isArray(result.emails);
|
|
83
82
|
}
|
|
84
83
|
/**
|
|
85
84
|
* Enrich a single candidate with business email
|
|
@@ -87,8 +86,10 @@ function isMultiResult(result) {
|
|
|
87
86
|
async function enrichBusinessEmail(candidate, options) {
|
|
88
87
|
const { providers, maxCostPerEmail = Infinity, confidenceThreshold = 0, retryMs = 200, onCost, logger, } = options;
|
|
89
88
|
// Track remaining budget
|
|
90
|
-
let remaining = Number.isFinite(maxCostPerEmail) && maxCostPerEmail >= 0
|
|
91
|
-
|
|
89
|
+
let remaining = Number.isFinite(maxCostPerEmail) && maxCostPerEmail >= 0
|
|
90
|
+
? maxCostPerEmail
|
|
91
|
+
: Infinity;
|
|
92
|
+
logger?.debug?.("enrichment.start", {
|
|
92
93
|
providers: providers.length,
|
|
93
94
|
confidenceThreshold,
|
|
94
95
|
maxCostPerEmail,
|
|
@@ -99,7 +100,7 @@ async function enrichBusinessEmail(candidate, options) {
|
|
|
99
100
|
const stepCost = getProviderCost(providerName);
|
|
100
101
|
// Skip if cost exceeds remaining budget
|
|
101
102
|
if (stepCost > 0 && remaining < stepCost) {
|
|
102
|
-
logger?.debug?.(
|
|
103
|
+
logger?.debug?.("enrichment.skip_budget", {
|
|
103
104
|
provider: providerName,
|
|
104
105
|
cost: stepCost,
|
|
105
106
|
remaining,
|
|
@@ -108,15 +109,15 @@ async function enrichBusinessEmail(candidate, options) {
|
|
|
108
109
|
}
|
|
109
110
|
let raw = null;
|
|
110
111
|
try {
|
|
111
|
-
logger?.debug?.(
|
|
112
|
+
logger?.debug?.("enrichment.provider.start", { provider: providerName });
|
|
112
113
|
raw = await provider(candidate);
|
|
113
|
-
logger?.debug?.(
|
|
114
|
+
logger?.debug?.("enrichment.provider.done", {
|
|
114
115
|
provider: providerName,
|
|
115
116
|
hasResult: !!(raw && (isMultiResult(raw) ? raw.emails.length > 0 : raw.email)),
|
|
116
117
|
});
|
|
117
118
|
}
|
|
118
119
|
catch (error) {
|
|
119
|
-
logger?.warn?.(
|
|
120
|
+
logger?.warn?.("enrichment.provider.error", {
|
|
120
121
|
provider: providerName,
|
|
121
122
|
error: error instanceof Error ? error.message : String(error),
|
|
122
123
|
});
|
|
@@ -125,7 +126,7 @@ async function enrichBusinessEmail(candidate, options) {
|
|
|
125
126
|
await new Promise((r) => setTimeout(r, retryMs));
|
|
126
127
|
try {
|
|
127
128
|
raw = await provider(candidate);
|
|
128
|
-
logger?.debug?.(
|
|
129
|
+
logger?.debug?.("enrichment.provider.retry.done", {
|
|
129
130
|
provider: providerName,
|
|
130
131
|
hasResult: !!(raw && (isMultiResult(raw) ? raw.emails.length > 0 : raw.email)),
|
|
131
132
|
});
|
|
@@ -160,7 +161,7 @@ async function enrichBusinessEmail(candidate, options) {
|
|
|
160
161
|
// Ignore cost callback errors
|
|
161
162
|
}
|
|
162
163
|
}
|
|
163
|
-
logger?.debug?.(
|
|
164
|
+
logger?.debug?.("enrichment.cost.debit", {
|
|
164
165
|
provider: providerName,
|
|
165
166
|
cost: stepCost,
|
|
166
167
|
remaining,
|
|
@@ -174,7 +175,7 @@ async function enrichBusinessEmail(candidate, options) {
|
|
|
174
175
|
}
|
|
175
176
|
// Filter out personal emails
|
|
176
177
|
if ((0, personal_domains_1.isPersonalEmail)(normalized.business_email)) {
|
|
177
|
-
logger?.debug?.(
|
|
178
|
+
logger?.debug?.("enrichment.skip_personal", {
|
|
178
179
|
provider: providerName,
|
|
179
180
|
email: normalized.business_email,
|
|
180
181
|
});
|
|
@@ -182,7 +183,7 @@ async function enrichBusinessEmail(candidate, options) {
|
|
|
182
183
|
}
|
|
183
184
|
// Filter out disposable emails
|
|
184
185
|
if ((0, disposable_domains_1.isDisposableEmail)(normalized.business_email)) {
|
|
185
|
-
logger?.debug?.(
|
|
186
|
+
logger?.debug?.("enrichment.skip_disposable", {
|
|
186
187
|
provider: providerName,
|
|
187
188
|
email: normalized.business_email,
|
|
188
189
|
});
|
|
@@ -190,8 +191,10 @@ async function enrichBusinessEmail(candidate, options) {
|
|
|
190
191
|
}
|
|
191
192
|
// Check confidence threshold
|
|
192
193
|
const score = normalized.business_email_confidence;
|
|
193
|
-
if (confidenceThreshold > 0 &&
|
|
194
|
-
|
|
194
|
+
if (confidenceThreshold > 0 &&
|
|
195
|
+
score !== undefined &&
|
|
196
|
+
score < confidenceThreshold) {
|
|
197
|
+
logger?.debug?.("enrichment.skip_low_confidence", {
|
|
195
198
|
provider: providerName,
|
|
196
199
|
score,
|
|
197
200
|
threshold: confidenceThreshold,
|
|
@@ -200,14 +203,14 @@ async function enrichBusinessEmail(candidate, options) {
|
|
|
200
203
|
}
|
|
201
204
|
// Check if verified
|
|
202
205
|
if (!normalized.business_email_verified) {
|
|
203
|
-
logger?.debug?.(
|
|
206
|
+
logger?.debug?.("enrichment.skip_unverified", {
|
|
204
207
|
provider: providerName,
|
|
205
208
|
email: normalized.business_email,
|
|
206
209
|
});
|
|
207
210
|
continue;
|
|
208
211
|
}
|
|
209
212
|
// Success!
|
|
210
|
-
logger?.info?.(
|
|
213
|
+
logger?.info?.("enrichment.success", {
|
|
211
214
|
provider: providerName,
|
|
212
215
|
email: normalized.business_email,
|
|
213
216
|
confidence: normalized.business_email_confidence,
|
|
@@ -215,7 +218,7 @@ async function enrichBusinessEmail(candidate, options) {
|
|
|
215
218
|
return normalized;
|
|
216
219
|
}
|
|
217
220
|
// No provider found a valid email
|
|
218
|
-
logger?.debug?.(
|
|
221
|
+
logger?.debug?.("enrichment.not_found", {
|
|
219
222
|
candidateHasName: !!(candidate.firstName || candidate.name),
|
|
220
223
|
candidateHasDomain: !!(candidate.domain || candidate.company),
|
|
221
224
|
});
|
|
@@ -224,7 +227,7 @@ async function enrichBusinessEmail(candidate, options) {
|
|
|
224
227
|
business_email_source: null,
|
|
225
228
|
business_email_verified: false,
|
|
226
229
|
last_checked_at: new Date().toISOString(),
|
|
227
|
-
status:
|
|
230
|
+
status: "not_found",
|
|
228
231
|
};
|
|
229
232
|
}
|
|
230
233
|
/**
|
|
@@ -241,7 +244,7 @@ async function enrichBatch(candidates, options) {
|
|
|
241
244
|
// Combine results with candidates (handle both fulfilled and rejected)
|
|
242
245
|
batchResults.forEach((result, idx) => {
|
|
243
246
|
const candidate = chunk[idx];
|
|
244
|
-
if (result.status ===
|
|
247
|
+
if (result.status === "fulfilled") {
|
|
245
248
|
results.push({ candidate, ...result.value });
|
|
246
249
|
}
|
|
247
250
|
else {
|
|
@@ -252,7 +255,7 @@ async function enrichBatch(candidates, options) {
|
|
|
252
255
|
business_email_source: null,
|
|
253
256
|
business_email_verified: false,
|
|
254
257
|
last_checked_at: new Date().toISOString(),
|
|
255
|
-
status: `error: ${result.reason instanceof Error ? result.reason.message :
|
|
258
|
+
status: `error: ${result.reason instanceof Error ? result.reason.message : "unknown"}`,
|
|
256
259
|
});
|
|
257
260
|
}
|
|
258
261
|
});
|
|
@@ -271,16 +274,16 @@ async function enrichBatch(candidates, options) {
|
|
|
271
274
|
*/
|
|
272
275
|
function classifyEmailType(email) {
|
|
273
276
|
if ((0, disposable_domains_1.isDisposableEmail)(email)) {
|
|
274
|
-
return
|
|
277
|
+
return "disposable";
|
|
275
278
|
}
|
|
276
279
|
if ((0, personal_domains_1.isPersonalEmail)(email)) {
|
|
277
|
-
return
|
|
280
|
+
return "personal";
|
|
278
281
|
}
|
|
279
282
|
if ((0, validation_1.isRoleAccount)(email)) {
|
|
280
|
-
return
|
|
283
|
+
return "role";
|
|
281
284
|
}
|
|
282
285
|
// If it's not personal, disposable, or role, it's likely business
|
|
283
|
-
return
|
|
286
|
+
return "business";
|
|
284
287
|
}
|
|
285
288
|
/**
|
|
286
289
|
* Enrich a candidate and collect ALL emails from ALL providers
|
|
@@ -296,10 +299,12 @@ async function enrichAllEmails(candidate, options) {
|
|
|
296
299
|
const providersQueried = [];
|
|
297
300
|
let totalCost = 0;
|
|
298
301
|
// Track remaining budget
|
|
299
|
-
let remaining = Number.isFinite(maxCostPerEmail) && maxCostPerEmail >= 0
|
|
302
|
+
let remaining = Number.isFinite(maxCostPerEmail) && maxCostPerEmail >= 0
|
|
303
|
+
? maxCostPerEmail
|
|
304
|
+
: Infinity;
|
|
300
305
|
// Track seen emails to avoid duplicates
|
|
301
306
|
const seenEmails = new Set();
|
|
302
|
-
logger?.debug?.(
|
|
307
|
+
logger?.debug?.("enrichment.all.start", {
|
|
303
308
|
providers: providers.length,
|
|
304
309
|
maxCostPerEmail,
|
|
305
310
|
});
|
|
@@ -309,7 +314,7 @@ async function enrichAllEmails(candidate, options) {
|
|
|
309
314
|
const stepCost = getProviderCost(providerName);
|
|
310
315
|
// Skip if cost exceeds remaining budget
|
|
311
316
|
if (stepCost > 0 && remaining < stepCost) {
|
|
312
|
-
logger?.debug?.(
|
|
317
|
+
logger?.debug?.("enrichment.all.skip_budget", {
|
|
313
318
|
provider: providerName,
|
|
314
319
|
cost: stepCost,
|
|
315
320
|
remaining,
|
|
@@ -319,16 +324,18 @@ async function enrichAllEmails(candidate, options) {
|
|
|
319
324
|
providersQueried.push(providerName);
|
|
320
325
|
let raw = null;
|
|
321
326
|
try {
|
|
322
|
-
logger?.debug?.(
|
|
327
|
+
logger?.debug?.("enrichment.all.provider.start", {
|
|
328
|
+
provider: providerName,
|
|
329
|
+
});
|
|
323
330
|
raw = await provider(candidate);
|
|
324
|
-
logger?.debug?.(
|
|
331
|
+
logger?.debug?.("enrichment.all.provider.done", {
|
|
325
332
|
provider: providerName,
|
|
326
333
|
hasResult: !!raw,
|
|
327
334
|
isMulti: isMultiResult(raw),
|
|
328
335
|
});
|
|
329
336
|
}
|
|
330
337
|
catch (error) {
|
|
331
|
-
logger?.warn?.(
|
|
338
|
+
logger?.warn?.("enrichment.all.provider.error", {
|
|
332
339
|
provider: providerName,
|
|
333
340
|
error: error instanceof Error ? error.message : String(error),
|
|
334
341
|
});
|
|
@@ -337,7 +344,7 @@ async function enrichAllEmails(candidate, options) {
|
|
|
337
344
|
await new Promise((r) => setTimeout(r, retryMs));
|
|
338
345
|
try {
|
|
339
346
|
raw = await provider(candidate);
|
|
340
|
-
logger?.debug?.(
|
|
347
|
+
logger?.debug?.("enrichment.all.provider.retry.done", {
|
|
341
348
|
provider: providerName,
|
|
342
349
|
hasResult: !!raw,
|
|
343
350
|
});
|
|
@@ -359,7 +366,7 @@ async function enrichAllEmails(candidate, options) {
|
|
|
359
366
|
// Ignore cost callback errors
|
|
360
367
|
}
|
|
361
368
|
}
|
|
362
|
-
logger?.debug?.(
|
|
369
|
+
logger?.debug?.("enrichment.all.cost.debit", {
|
|
363
370
|
provider: providerName,
|
|
364
371
|
cost: stepCost,
|
|
365
372
|
remaining,
|
|
@@ -393,9 +400,9 @@ async function enrichAllEmails(candidate, options) {
|
|
|
393
400
|
const emailLower = raw.email.toLowerCase();
|
|
394
401
|
if (!seenEmails.has(emailLower)) {
|
|
395
402
|
seenEmails.add(emailLower);
|
|
396
|
-
const confidence = typeof raw.score ===
|
|
403
|
+
const confidence = typeof raw.score === "number"
|
|
397
404
|
? raw.score
|
|
398
|
-
: typeof raw.confidence ===
|
|
405
|
+
: typeof raw.confidence === "number"
|
|
399
406
|
? raw.confidence
|
|
400
407
|
: 50;
|
|
401
408
|
allEmails.push({
|
|
@@ -425,7 +432,7 @@ async function enrichAllEmails(candidate, options) {
|
|
|
425
432
|
// Then by type priority
|
|
426
433
|
return typeOrder[a.type] - typeOrder[b.type];
|
|
427
434
|
});
|
|
428
|
-
logger?.info?.(
|
|
435
|
+
logger?.info?.("enrichment.all.complete", {
|
|
429
436
|
emailsFound: allEmails.length,
|
|
430
437
|
providersQueried: providersQueried.length,
|
|
431
438
|
totalCost,
|
|
@@ -451,7 +458,7 @@ async function enrichAllBatch(candidates, options) {
|
|
|
451
458
|
const batchResults = await Promise.allSettled(chunk.map((candidate) => enrichAllEmails(candidate, options)));
|
|
452
459
|
// Handle both fulfilled and rejected results
|
|
453
460
|
batchResults.forEach((result, idx) => {
|
|
454
|
-
if (result.status ===
|
|
461
|
+
if (result.status === "fulfilled") {
|
|
455
462
|
results.push(result.value);
|
|
456
463
|
}
|
|
457
464
|
else {
|
|
@@ -7,7 +7,5 @@ export { createSmartProspectProvider } from "./smartprospect";
|
|
|
7
7
|
export { createCosiallProvider } from "./cosiall";
|
|
8
8
|
export { createTryKittProvider, findEmailWithTryKitt, verifyEmailWithTryKitt, } from "./trykitt";
|
|
9
9
|
export { createHunterProvider } from "./hunter";
|
|
10
|
-
export { createDropcontactProvider } from "./dropcontact";
|
|
11
|
-
export { createBouncerProvider, verifyEmailWithBouncer, checkCatchAllDomain, verifyEmailsBatch, } from "./bouncer";
|
|
12
10
|
export { createBounceBanProvider, verifyEmailWithBounceBan, verifyEmailsBatchWithBounceBan, checkCatchAllWithBounceBan, } from "./bounceban";
|
|
13
11
|
export { createSnovioProvider, findEmailsWithSnovio, verifyEmailWithSnovio, clearSnovioTokenCache, } from "./snovio";
|