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.
Files changed (162) hide show
  1. package/README.md +276 -40
  2. package/dist/enrichment/index.d.ts +22 -3
  3. package/dist/enrichment/index.js +29 -38
  4. package/dist/enrichment/matching.d.ts +4 -8
  5. package/dist/enrichment/matching.js +6 -5
  6. package/dist/enrichment/orchestrator.d.ts +1 -1
  7. package/dist/enrichment/orchestrator.js +51 -44
  8. package/dist/enrichment/providers/index.d.ts +0 -2
  9. package/dist/enrichment/providers/index.js +1 -8
  10. package/dist/enrichment/providers/snovio.d.ts +4 -4
  11. package/dist/enrichment/providers/snovio.js +14 -14
  12. package/dist/enrichment/types.d.ts +18 -71
  13. package/dist/enrichment/types.js +3 -8
  14. package/dist/enrichment/utils/rate-limiter.js +0 -2
  15. package/dist/index.d.ts +2 -4
  16. package/dist/index.js +2 -2
  17. package/docs/COSIALL_PROFILE_EMAILS.md +342 -0
  18. package/docs/ENRICHMENT.md +580 -0
  19. package/docs/INTEGRATION.md +403 -0
  20. package/docs/PLAYGROUND.md +553 -0
  21. package/docs/SALES_SEARCH.md +171 -0
  22. package/docs/api/.nojekyll +1 -0
  23. package/docs/api/assets/hierarchy.js +1 -0
  24. package/docs/api/assets/highlight.css +92 -0
  25. package/docs/api/assets/icons.js +18 -0
  26. package/docs/api/assets/icons.svg +1 -0
  27. package/docs/api/assets/main.js +60 -0
  28. package/docs/api/assets/navigation.js +1 -0
  29. package/docs/api/assets/search.js +1 -0
  30. package/docs/api/assets/style.css +1633 -0
  31. package/docs/api/classes/LinkedInClientError.html +37 -0
  32. package/docs/api/functions/_testGetAccountCookies.html +4 -0
  33. package/docs/api/functions/_testGetAccountEntry.html +4 -0
  34. package/docs/api/functions/_testGetAllAccountIds.html +3 -0
  35. package/docs/api/functions/_testGetPoolState.html +3 -0
  36. package/docs/api/functions/adminResetAccount.html +1 -0
  37. package/docs/api/functions/adminSetCooldown.html +1 -0
  38. package/docs/api/functions/buildCookieHeader.html +1 -0
  39. package/docs/api/functions/clearAllSmartLeadTokens.html +2 -0
  40. package/docs/api/functions/clearRequestHistory.html +1 -0
  41. package/docs/api/functions/clearSessionAccount.html +1 -0
  42. package/docs/api/functions/clearSmartLeadToken.html +2 -0
  43. package/docs/api/functions/createEnrichmentClient.html +8 -0
  44. package/docs/api/functions/extractCsrfToken.html +1 -0
  45. package/docs/api/functions/extractLinkedInHandle.html +7 -0
  46. package/docs/api/functions/fetchCookiesFromCosiall.html +14 -0
  47. package/docs/api/functions/fetchProfileEmailsFromCosiall.html +18 -0
  48. package/docs/api/functions/forceRefreshCookies.html +1 -0
  49. package/docs/api/functions/getAccountForSession.html +1 -0
  50. package/docs/api/functions/getAccountsSummary.html +1 -0
  51. package/docs/api/functions/getCompaniesBatch.html +5 -0
  52. package/docs/api/functions/getCompanyById.html +9 -0
  53. package/docs/api/functions/getCompanyByUrl.html +1 -0
  54. package/docs/api/functions/getConfig.html +1 -0
  55. package/docs/api/functions/getCookiePoolHealth.html +1 -0
  56. package/docs/api/functions/getProfileByUrn.html +17 -0
  57. package/docs/api/functions/getProfileByVanity.html +10 -0
  58. package/docs/api/functions/getProfilesBatch.html +1 -0
  59. package/docs/api/functions/getRequestHistory.html +1 -0
  60. package/docs/api/functions/getSalesNavigatorProfileDetails.html +1 -0
  61. package/docs/api/functions/getSalesNavigatorProfileFull.html +16 -0
  62. package/docs/api/functions/getSmartLeadToken.html +1 -0
  63. package/docs/api/functions/getSmartLeadTokenCacheStats.html +2 -0
  64. package/docs/api/functions/getSmartLeadUser.html +2 -0
  65. package/docs/api/functions/getSnapshot.html +1 -0
  66. package/docs/api/functions/getYearsAtCompanyOptions.html +2 -0
  67. package/docs/api/functions/getYearsInPositionOptions.html +2 -0
  68. package/docs/api/functions/getYearsOfExperienceOptions.html +2 -0
  69. package/docs/api/functions/incrementMetric.html +1 -0
  70. package/docs/api/functions/initializeCookiePool.html +1 -0
  71. package/docs/api/functions/initializeLinkedInClient.html +1 -0
  72. package/docs/api/functions/isBusinessEmail.html +4 -0
  73. package/docs/api/functions/isDisposableDomain.html +4 -0
  74. package/docs/api/functions/isDisposableEmail.html +4 -0
  75. package/docs/api/functions/isPersonalDomain.html +4 -0
  76. package/docs/api/functions/isPersonalEmail.html +4 -0
  77. package/docs/api/functions/isRoleAccount.html +4 -0
  78. package/docs/api/functions/isValidEmailSyntax.html +4 -0
  79. package/docs/api/functions/parseFullProfile.html +15 -0
  80. package/docs/api/functions/parseSalesSearchResults.html +1 -0
  81. package/docs/api/functions/reportAccountFailure.html +1 -0
  82. package/docs/api/functions/reportAccountSuccess.html +1 -0
  83. package/docs/api/functions/resolveCompanyUniversalName.html +1 -0
  84. package/docs/api/functions/searchSalesLeads.html +16 -0
  85. package/docs/api/functions/selectAccountForRequest.html +1 -0
  86. package/docs/api/functions/setAccountForSession.html +1 -0
  87. package/docs/api/functions/typeahead.html +1 -0
  88. package/docs/api/functions/verifyEmailMx.html +1 -0
  89. package/docs/api/hierarchy.html +1 -0
  90. package/docs/api/index.html +12 -0
  91. package/docs/api/interfaces/AccountCookies.html +4 -0
  92. package/docs/api/interfaces/BatchEnrichmentOptions.html +14 -0
  93. package/docs/api/interfaces/CacheAdapter.html +6 -0
  94. package/docs/api/interfaces/CanonicalEmail.html +14 -0
  95. package/docs/api/interfaces/Company.html +17 -0
  96. package/docs/api/interfaces/ConstructConfig.html +8 -0
  97. package/docs/api/interfaces/CosiallProfileEmailsResponse.html +11 -0
  98. package/docs/api/interfaces/EnrichmentCandidate.html +34 -0
  99. package/docs/api/interfaces/EnrichmentClient.html +10 -0
  100. package/docs/api/interfaces/EnrichmentClientConfig.html +12 -0
  101. package/docs/api/interfaces/EnrichmentLogger.html +6 -0
  102. package/docs/api/interfaces/EnrichmentOptions.html +10 -0
  103. package/docs/api/interfaces/HunterConfig.html +3 -0
  104. package/docs/api/interfaces/LddConfig.html +4 -0
  105. package/docs/api/interfaces/LddProfileData.html +6 -0
  106. package/docs/api/interfaces/LinkedInClientConfig.html +20 -0
  107. package/docs/api/interfaces/LinkedInCookie.html +9 -0
  108. package/docs/api/interfaces/LinkedInPosition.html +14 -0
  109. package/docs/api/interfaces/LinkedInProfile.html +21 -0
  110. package/docs/api/interfaces/LinkedInSpotlightBadge.html +5 -0
  111. package/docs/api/interfaces/LinkedInTenure.html +3 -0
  112. package/docs/api/interfaces/Metrics.html +22 -0
  113. package/docs/api/interfaces/MetricsSnapshot.html +23 -0
  114. package/docs/api/interfaces/ProfileEducation.html +8 -0
  115. package/docs/api/interfaces/ProfileEmailsLookupOptions.html +9 -0
  116. package/docs/api/interfaces/ProfilePosition.html +12 -0
  117. package/docs/api/interfaces/ProfileSkill.html +3 -0
  118. package/docs/api/interfaces/ProviderResult.html +11 -0
  119. package/docs/api/interfaces/ProvidersConfig.html +14 -0
  120. package/docs/api/interfaces/RequestHistoryEntry.html +8 -0
  121. package/docs/api/interfaces/SalesLeadSearchResult.html +31 -0
  122. package/docs/api/interfaces/SalesNavigatorContactInfo.html +5 -0
  123. package/docs/api/interfaces/SalesNavigatorPosition.html +11 -0
  124. package/docs/api/interfaces/SalesNavigatorProfile.html +9 -0
  125. package/docs/api/interfaces/SalesNavigatorProfileFull.html +24 -0
  126. package/docs/api/interfaces/SearchSalesResult.html +5 -0
  127. package/docs/api/interfaces/SmartLeadAuthConfig.html +6 -0
  128. package/docs/api/interfaces/SmartLeadCredentials.html +3 -0
  129. package/docs/api/interfaces/SmartLeadLoginResponse.html +3 -0
  130. package/docs/api/interfaces/SmartLeadUser.html +8 -0
  131. package/docs/api/interfaces/SmartProspectConfig.html +19 -0
  132. package/docs/api/interfaces/SmartProspectContact.html +24 -0
  133. package/docs/api/interfaces/SmartProspectSearchFilters.html +42 -0
  134. package/docs/api/interfaces/TypeaheadItem.html +3 -0
  135. package/docs/api/interfaces/TypeaheadResult.html +3 -0
  136. package/docs/api/interfaces/VerificationResult.html +16 -0
  137. package/docs/api/types/CostCallback.html +2 -0
  138. package/docs/api/types/Geo.html +4 -0
  139. package/docs/api/types/LddApiResponse.html +1 -0
  140. package/docs/api/types/ProviderFunc.html +2 -0
  141. package/docs/api/types/ProviderName.html +2 -0
  142. package/docs/api/types/SalesSearchFilters.html +7 -0
  143. package/docs/api/types/TypeaheadType.html +1 -0
  144. package/docs/api/variables/COMPANY_SIZE_OPTIONS.html +1 -0
  145. package/docs/api/variables/DEFAULT_PROVIDER_ORDER.html +19 -0
  146. package/docs/api/variables/DISPOSABLE_DOMAINS.html +2 -0
  147. package/docs/api/variables/FUNCTION_OPTIONS.html +1 -0
  148. package/docs/api/variables/INDUSTRY_OPTIONS.html +1 -0
  149. package/docs/api/variables/LANGUAGE_OPTIONS.html +1 -0
  150. package/docs/api/variables/PERSONAL_DOMAINS.html +2 -0
  151. package/docs/api/variables/PROVIDER_COSTS.html +13 -0
  152. package/docs/api/variables/REGION_OPTIONS.html +1 -0
  153. package/docs/api/variables/SENIORITY_OPTIONS.html +3 -0
  154. package/docs/api/variables/YEARS_OPTIONS.html +1 -0
  155. package/docs/index.html +98 -0
  156. package/package.json +40 -28
  157. package/dist/enrichment/providers/apollo.d.ts +0 -11
  158. package/dist/enrichment/providers/apollo.js +0 -181
  159. package/dist/enrichment/providers/bouncer.d.ts +0 -67
  160. package/dist/enrichment/providers/bouncer.js +0 -231
  161. package/dist/enrichment/providers/dropcontact.d.ts +0 -22
  162. package/dist/enrichment/providers/dropcontact.js +0 -206
@@ -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/Apollo if confidence is below threshold (PAID - last resort)
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.005/lookup)
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?.userId && config.snovio?.apiSecret && companyDomain) {
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
- bouncer: 8,
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 './types';
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
- * - bouncer: $0.006/email (SMTP verification, 99%+ accuracy)
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
- hunter: 0.005,
33
- dropcontact: 0.01,
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 === 'number'
50
+ const confidence = typeof raw.score === "number"
53
51
  ? raw.score
54
- : typeof raw.confidence === 'number'
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 ?? `provider-${index}`;
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 && 'emails' in result && Array.isArray(result.emails);
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 ? maxCostPerEmail : Infinity;
91
- logger?.debug?.('enrichment.start', {
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?.('enrichment.skip_budget', {
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?.('enrichment.provider.start', { provider: providerName });
112
+ logger?.debug?.("enrichment.provider.start", { provider: providerName });
112
113
  raw = await provider(candidate);
113
- logger?.debug?.('enrichment.provider.done', {
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?.('enrichment.provider.error', {
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?.('enrichment.provider.retry.done', {
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?.('enrichment.cost.debit', {
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?.('enrichment.skip_personal', {
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?.('enrichment.skip_disposable', {
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 && score !== undefined && score < confidenceThreshold) {
194
- logger?.debug?.('enrichment.skip_low_confidence', {
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?.('enrichment.skip_unverified', {
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?.('enrichment.success', {
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?.('enrichment.not_found', {
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: 'not_found',
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 === 'fulfilled') {
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 : 'unknown'}`,
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 'disposable';
277
+ return "disposable";
275
278
  }
276
279
  if ((0, personal_domains_1.isPersonalEmail)(email)) {
277
- return 'personal';
280
+ return "personal";
278
281
  }
279
282
  if ((0, validation_1.isRoleAccount)(email)) {
280
- return 'role';
283
+ return "role";
281
284
  }
282
285
  // If it's not personal, disposable, or role, it's likely business
283
- return 'business';
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 ? maxCostPerEmail : Infinity;
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?.('enrichment.all.start', {
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?.('enrichment.all.skip_budget', {
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?.('enrichment.all.provider.start', { provider: providerName });
327
+ logger?.debug?.("enrichment.all.provider.start", {
328
+ provider: providerName,
329
+ });
323
330
  raw = await provider(candidate);
324
- logger?.debug?.('enrichment.all.provider.done', {
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?.('enrichment.all.provider.error', {
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?.('enrichment.all.provider.retry.done', {
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?.('enrichment.all.cost.debit', {
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 === 'number'
403
+ const confidence = typeof raw.score === "number"
397
404
  ? raw.score
398
- : typeof raw.confidence === 'number'
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?.('enrichment.all.complete', {
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 === 'fulfilled') {
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";
@@ -3,7 +3,7 @@
3
3
  * Email Enrichment Providers
4
4
  */
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.clearSnovioTokenCache = exports.verifyEmailWithSnovio = exports.findEmailsWithSnovio = exports.createSnovioProvider = exports.checkCatchAllWithBounceBan = exports.verifyEmailsBatchWithBounceBan = exports.verifyEmailWithBounceBan = exports.createBounceBanProvider = exports.verifyEmailsBatch = exports.checkCatchAllDomain = exports.verifyEmailWithBouncer = exports.createBouncerProvider = exports.createDropcontactProvider = exports.createHunterProvider = exports.verifyEmailWithTryKitt = exports.findEmailWithTryKitt = exports.createTryKittProvider = exports.createCosiallProvider = exports.createSmartProspectProvider = exports.createLddProvider = exports.createConstructProvider = void 0;
6
+ exports.clearSnovioTokenCache = exports.verifyEmailWithSnovio = exports.findEmailsWithSnovio = exports.createSnovioProvider = exports.checkCatchAllWithBounceBan = exports.verifyEmailsBatchWithBounceBan = exports.verifyEmailWithBounceBan = exports.createBounceBanProvider = exports.createHunterProvider = exports.verifyEmailWithTryKitt = exports.findEmailWithTryKitt = exports.createTryKittProvider = exports.createCosiallProvider = exports.createSmartProspectProvider = exports.createLddProvider = exports.createConstructProvider = void 0;
7
7
  var construct_1 = require("./construct");
8
8
  Object.defineProperty(exports, "createConstructProvider", { enumerable: true, get: function () { return construct_1.createConstructProvider; } });
9
9
  var ldd_1 = require("./ldd");
@@ -18,13 +18,6 @@ Object.defineProperty(exports, "findEmailWithTryKitt", { enumerable: true, get:
18
18
  Object.defineProperty(exports, "verifyEmailWithTryKitt", { enumerable: true, get: function () { return trykitt_1.verifyEmailWithTryKitt; } });
19
19
  var hunter_1 = require("./hunter");
20
20
  Object.defineProperty(exports, "createHunterProvider", { enumerable: true, get: function () { return hunter_1.createHunterProvider; } });
21
- var dropcontact_1 = require("./dropcontact");
22
- Object.defineProperty(exports, "createDropcontactProvider", { enumerable: true, get: function () { return dropcontact_1.createDropcontactProvider; } });
23
- var bouncer_1 = require("./bouncer");
24
- Object.defineProperty(exports, "createBouncerProvider", { enumerable: true, get: function () { return bouncer_1.createBouncerProvider; } });
25
- Object.defineProperty(exports, "verifyEmailWithBouncer", { enumerable: true, get: function () { return bouncer_1.verifyEmailWithBouncer; } });
26
- Object.defineProperty(exports, "checkCatchAllDomain", { enumerable: true, get: function () { return bouncer_1.checkCatchAllDomain; } });
27
- Object.defineProperty(exports, "verifyEmailsBatch", { enumerable: true, get: function () { return bouncer_1.verifyEmailsBatch; } });
28
21
  var bounceban_1 = require("./bounceban");
29
22
  Object.defineProperty(exports, "createBounceBanProvider", { enumerable: true, get: function () { return bounceban_1.createBounceBanProvider; } });
30
23
  Object.defineProperty(exports, "verifyEmailWithBounceBan", { enumerable: true, get: function () { return bounceban_1.verifyEmailWithBounceBan; } });
@@ -30,8 +30,8 @@ export declare function createSnovioProvider(config: SnovioConfig): (candidate:
30
30
  * 'Doe',
31
31
  * 'example.com',
32
32
  * {
33
- * userId: process.env.SNOVIO_USER_ID,
34
- * apiSecret: process.env.SNOVIO_API_SECRET,
33
+ * clientId: process.env.SNOVIO_CLIENT_ID,
34
+ * clientSecret: process.env.SNOVIO_CLIENT_SECRET,
35
35
  * }
36
36
  * );
37
37
  * ```
@@ -43,8 +43,8 @@ export declare function findEmailsWithSnovio(firstName: string, lastName: string
43
43
  * @example
44
44
  * ```typescript
45
45
  * const result = await verifyEmailWithSnovio('test@example.com', {
46
- * userId: process.env.SNOVIO_USER_ID,
47
- * apiSecret: process.env.SNOVIO_API_SECRET,
46
+ * clientId: process.env.SNOVIO_CLIENT_ID,
47
+ * clientSecret: process.env.SNOVIO_CLIENT_SECRET,
48
48
  * });
49
49
  * ```
50
50
  */
@@ -68,7 +68,7 @@ function extractNameAndDomain(candidate) {
68
68
  /**
69
69
  * Get OAuth access token from Snov.io
70
70
  */
71
- async function getAccessToken(userId, apiSecret, apiUrl, timeoutMs) {
71
+ async function getAccessToken(clientId, clientSecret, apiUrl, timeoutMs) {
72
72
  // Return cached token if valid
73
73
  if (cachedAccessToken && Date.now() < tokenExpiresAt) {
74
74
  return cachedAccessToken;
@@ -83,8 +83,8 @@ async function getAccessToken(userId, apiSecret, apiUrl, timeoutMs) {
83
83
  },
84
84
  body: JSON.stringify({
85
85
  grant_type: "client_credentials",
86
- client_id: userId,
87
- client_secret: apiSecret,
86
+ client_id: clientId,
87
+ client_secret: clientSecret,
88
88
  }),
89
89
  signal: controller.signal,
90
90
  });
@@ -151,8 +151,8 @@ async function findEmailsByName(firstName, lastName, domain, accessToken, apiUrl
151
151
  * when pattern guessing fails or for catch-all domains.
152
152
  */
153
153
  function createSnovioProvider(config) {
154
- const { userId, apiSecret, apiUrl = DEFAULT_API_URL, timeoutMs = DEFAULT_TIMEOUT_MS, } = config;
155
- if (!userId || !apiSecret) {
154
+ const { clientId, clientSecret, apiUrl = DEFAULT_API_URL, timeoutMs = DEFAULT_TIMEOUT_MS, } = config;
155
+ if (!clientId || !clientSecret) {
156
156
  return (0, noop_provider_1.createNoOpProvider)("snovio");
157
157
  }
158
158
  async function findEmails(candidate) {
@@ -162,7 +162,7 @@ function createSnovioProvider(config) {
162
162
  }
163
163
  const { firstName, lastName, domain } = nameAndDomain;
164
164
  // Get access token
165
- const accessToken = await getAccessToken(userId, apiSecret, apiUrl, timeoutMs);
165
+ const accessToken = await getAccessToken(clientId, clientSecret, apiUrl, timeoutMs);
166
166
  if (!accessToken) {
167
167
  return null;
168
168
  }
@@ -210,15 +210,15 @@ function createSnovioProvider(config) {
210
210
  * 'Doe',
211
211
  * 'example.com',
212
212
  * {
213
- * userId: process.env.SNOVIO_USER_ID,
214
- * apiSecret: process.env.SNOVIO_API_SECRET,
213
+ * clientId: process.env.SNOVIO_CLIENT_ID,
214
+ * clientSecret: process.env.SNOVIO_CLIENT_SECRET,
215
215
  * }
216
216
  * );
217
217
  * ```
218
218
  */
219
219
  async function findEmailsWithSnovio(firstName, lastName, domain, config) {
220
- const { userId, apiSecret, apiUrl = DEFAULT_API_URL, timeoutMs = DEFAULT_TIMEOUT_MS, } = config;
221
- const accessToken = await getAccessToken(userId, apiSecret, apiUrl, timeoutMs);
220
+ const { clientId, clientSecret, apiUrl = DEFAULT_API_URL, timeoutMs = DEFAULT_TIMEOUT_MS, } = config;
221
+ const accessToken = await getAccessToken(clientId, clientSecret, apiUrl, timeoutMs);
222
222
  if (!accessToken) {
223
223
  return null;
224
224
  }
@@ -230,14 +230,14 @@ async function findEmailsWithSnovio(firstName, lastName, domain, config) {
230
230
  * @example
231
231
  * ```typescript
232
232
  * const result = await verifyEmailWithSnovio('test@example.com', {
233
- * userId: process.env.SNOVIO_USER_ID,
234
- * apiSecret: process.env.SNOVIO_API_SECRET,
233
+ * clientId: process.env.SNOVIO_CLIENT_ID,
234
+ * clientSecret: process.env.SNOVIO_CLIENT_SECRET,
235
235
  * });
236
236
  * ```
237
237
  */
238
238
  async function verifyEmailWithSnovio(email, config) {
239
- const { userId, apiSecret, apiUrl = DEFAULT_API_URL, timeoutMs = DEFAULT_TIMEOUT_MS, } = config;
240
- const accessToken = await getAccessToken(userId, apiSecret, apiUrl, timeoutMs);
239
+ const { clientId, clientSecret, apiUrl = DEFAULT_API_URL, timeoutMs = DEFAULT_TIMEOUT_MS, } = config;
240
+ const accessToken = await getAccessToken(clientId, clientSecret, apiUrl, timeoutMs);
241
241
  if (!accessToken) {
242
242
  return null;
243
243
  }
@@ -24,10 +24,20 @@ export interface CanonicalEmail {
24
24
  /**
25
25
  * Raw result from a provider before normalization
26
26
  */
27
+ /**
28
+ * Result returned by a single-email provider
29
+ */
27
30
  export interface ProviderResult {
31
+ /** The email address found */
28
32
  email?: string | null;
33
+ /** Whether the email was verified */
29
34
  verified?: boolean;
35
+ /**
36
+ * Confidence score (0-100)
37
+ * @deprecated Use `confidence` instead - both are supported for backwards compatibility
38
+ */
30
39
  score?: number;
40
+ /** Confidence score (0-100) */
31
41
  confidence?: number;
32
42
  }
33
43
  /**
@@ -157,12 +167,6 @@ export interface LddConfig {
157
167
  apiUrl: string;
158
168
  apiToken: string;
159
169
  }
160
- /**
161
- * Dropcontact provider configuration
162
- */
163
- export interface DropcontactConfig {
164
- apiKey: string;
165
- }
166
170
  /**
167
171
  * TryKitt.ai provider configuration
168
172
  *
@@ -354,57 +358,6 @@ export interface BounceBanVerifyResponse {
354
358
  /** Remaining credits */
355
359
  credits_remaining?: number;
356
360
  }
357
- /**
358
- * Bouncer.io provider configuration
359
- *
360
- * Bouncer provides SMTP email verification with 99%+ accuracy.
361
- * Best for verifying pattern-guessed emails on non-catch-all domains.
362
- *
363
- * @see https://docs.usebouncer.com
364
- */
365
- export interface BouncerConfig {
366
- /** Bouncer API key */
367
- apiKey: string;
368
- /** API URL override (default: https://api.usebouncer.com/v1.1) */
369
- apiUrl?: string;
370
- /** Timeout in ms (default: 30000) */
371
- timeoutMs?: number;
372
- }
373
- /**
374
- * Bouncer verification result status
375
- */
376
- export type BouncerStatus = "deliverable" | "undeliverable" | "risky" | "unknown";
377
- /**
378
- * Bouncer verification result reason
379
- */
380
- export type BouncerReason = "accepted_email" | "rejected_email" | "invalid_domain" | "invalid_email" | "unavailable_smtp" | "dns_error" | "low_deliverability" | "low_quality" | "catch_all" | "full_mailbox" | "role_account" | "disposable" | "timeout" | "unknown";
381
- /**
382
- * Bouncer API response for single email verification
383
- */
384
- export interface BouncerVerifyResponse {
385
- status: BouncerStatus;
386
- reason: BouncerReason;
387
- email: string;
388
- domain: string;
389
- account: string;
390
- /** Whether this is a free email provider (Gmail, Yahoo, etc.) */
391
- free: boolean;
392
- /** Whether this is a disposable email */
393
- disposable: boolean;
394
- /** Whether this is a role-based email */
395
- role: boolean;
396
- /** Whether domain is catch-all (accepts all emails) */
397
- acceptAll: boolean;
398
- /** Did you mean suggestion for typos */
399
- didYouMean?: string;
400
- /** DNS information */
401
- dns?: {
402
- type: string;
403
- record: string;
404
- };
405
- /** Toxicity score (0-5, higher = more risky) */
406
- toxicity?: number;
407
- }
408
361
  /**
409
362
  * Snov.io provider configuration
410
363
  *
@@ -414,10 +367,10 @@ export interface BouncerVerifyResponse {
414
367
  * @see https://snov.io/api
415
368
  */
416
369
  export interface SnovioConfig {
417
- /** Snov.io API user ID */
418
- userId: string;
419
- /** Snov.io API secret */
420
- apiSecret: string;
370
+ /** Snov.io OAuth client ID (get from https://app.snov.io/api) */
371
+ clientId: string;
372
+ /** Snov.io OAuth client secret */
373
+ clientSecret: string;
421
374
  /** API URL override (default: https://api.snov.io) */
422
375
  apiUrl?: string;
423
376
  /** Timeout in ms (default: 30000) */
@@ -490,9 +443,6 @@ export interface ProvidersConfig {
490
443
  /** TryKitt.ai AI email finder (FREE for individuals, unlimited) */
491
444
  trykitt?: TryKittConfig;
492
445
  hunter?: HunterConfig;
493
- dropcontact?: DropcontactConfig;
494
- /** Bouncer.io for SMTP email verification (99%+ accuracy) */
495
- bouncer?: BouncerConfig;
496
446
  /** BounceBan catch-all verification (FREE single, 85-95% accuracy) */
497
447
  bounceban?: BounceBanConfig;
498
448
  /** Snov.io for email finding (98% delivery rate) */
@@ -506,7 +456,7 @@ export interface EnrichmentOptions {
506
456
  maxCostPerEmail?: number;
507
457
  /** Minimum confidence threshold 0-100 (default: 0) */
508
458
  confidenceThreshold?: number;
509
- /** Provider order (default: ['ldd', 'smartprospect', 'construct', 'bouncer', 'snovio', 'hunter']) */
459
+ /** Provider order (default: ['ldd', 'smartprospect', 'cosiall', 'trykitt', 'construct', 'bounceban', 'hunter', 'snovio']) */
510
460
  providerOrder?: ProviderName[];
511
461
  /** Retry delay in ms on transient errors (default: 200) */
512
462
  retryMs?: number;
@@ -575,7 +525,7 @@ export interface EnrichmentClient {
575
525
  /**
576
526
  * Available provider names
577
527
  */
578
- export type ProviderName = "construct" | "ldd" | "smartprospect" | "cosiall" | "trykitt" | "hunter" | "dropcontact" | "bouncer" | "bounceban" | "snovio";
528
+ export type ProviderName = "construct" | "ldd" | "smartprospect" | "cosiall" | "trykitt" | "hunter" | "bounceban" | "snovio";
579
529
  /**
580
530
  * Default provider order - 3-Phase Strategy
581
531
  *
@@ -590,10 +540,9 @@ export type ProviderName = "construct" | "ldd" | "smartprospect" | "cosiall" | "
590
540
  * - bounceban: BounceBan catch-all verification (FREE single, $0.003/bulk)
591
541
  *
592
542
  * PHASE 3 - Paid finders (only if Phase 2 inconclusive):
593
- * - hunter: Hunter.io email finder ($0.005/email)
543
+ * - hunter: Hunter.io email finder ($0.015/email)
594
544
  * - snovio: Snov.io email finder ($0.02/email)
595
545
  *
596
- * Note: bouncer, dropcontact available but not in default order
597
546
  */
598
547
  export declare const DEFAULT_PROVIDER_ORDER: ProviderName[];
599
548
  /**
@@ -606,10 +555,8 @@ export declare const DEFAULT_PROVIDER_ORDER: ProviderName[];
606
555
  * - trykitt: FREE (unlimited for individuals, $0.005/email high volume)
607
556
  * - construct: FREE (pattern guessing + MX check)
608
557
  * - bounceban: FREE single / $0.003/email bulk (catch-all specialist)
609
- * - bouncer: $0.006/email (SMTP verification, 99%+ accuracy)
558
+ * - hunter: $0.015/email (Growth tier)
610
559
  * - snovio: $0.02/email (email finding + verification)
611
- * - hunter: $0.005/email
612
- * - dropcontact: $0.01/email (not in default order)
613
560
  */
614
561
  export declare const PROVIDER_COSTS: Record<ProviderName, number>;
615
562
  /**