linkedin-secret-sauce 0.12.3 → 0.12.5

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 (163) hide show
  1. package/README.md +6 -49
  2. package/dist/enrichment/index.d.ts +3 -5
  3. package/dist/enrichment/index.js +4 -20
  4. package/dist/enrichment/matching.d.ts +4 -8
  5. package/dist/enrichment/matching.js +165 -84
  6. package/dist/enrichment/orchestrator.d.ts +1 -1
  7. package/dist/enrichment/orchestrator.js +51 -44
  8. package/dist/enrichment/providers/bounceban.js +19 -10
  9. package/dist/enrichment/providers/construct.js +97 -89
  10. package/dist/enrichment/providers/cosiall.js +2 -2
  11. package/dist/enrichment/providers/hunter.js +2 -1
  12. package/dist/enrichment/providers/index.d.ts +0 -2
  13. package/dist/enrichment/providers/index.js +1 -8
  14. package/dist/enrichment/providers/ldd.js +4 -2
  15. package/dist/enrichment/providers/smartprospect.d.ts +5 -2
  16. package/dist/enrichment/providers/smartprospect.js +64 -27
  17. package/dist/enrichment/providers/snovio.d.ts +4 -4
  18. package/dist/enrichment/providers/snovio.js +21 -17
  19. package/dist/enrichment/providers/trykitt.d.ts +2 -2
  20. package/dist/enrichment/providers/trykitt.js +86 -21
  21. package/dist/enrichment/types.d.ts +8 -71
  22. package/dist/enrichment/types.js +3 -8
  23. package/dist/enrichment/utils/rate-limiter.js +0 -2
  24. package/dist/index.d.ts +2 -4
  25. package/dist/index.js +2 -2
  26. package/docs/ENRICHMENT.md +3 -45
  27. package/docs/INTEGRATION.md +1 -3
  28. package/docs/PLAYGROUND.md +4 -9
  29. package/docs/api/assets/hierarchy.js +1 -1
  30. package/docs/api/assets/navigation.js +1 -1
  31. package/docs/api/assets/search.js +1 -1
  32. package/docs/api/classes/LinkedInClientError.html +4 -4
  33. package/docs/api/functions/_testGetAccountCookies.html +2 -2
  34. package/docs/api/functions/_testGetAccountEntry.html +2 -2
  35. package/docs/api/functions/_testGetAllAccountIds.html +2 -2
  36. package/docs/api/functions/_testGetPoolState.html +2 -2
  37. package/docs/api/functions/adminResetAccount.html +1 -1
  38. package/docs/api/functions/adminSetCooldown.html +1 -1
  39. package/docs/api/functions/buildCookieHeader.html +1 -1
  40. package/docs/api/functions/clearAllSmartLeadTokens.html +2 -2
  41. package/docs/api/functions/clearRequestHistory.html +1 -1
  42. package/docs/api/functions/clearSessionAccount.html +1 -1
  43. package/docs/api/functions/clearSmartLeadToken.html +2 -2
  44. package/docs/api/functions/createEnrichmentClient.html +3 -3
  45. package/docs/api/functions/extractCsrfToken.html +1 -1
  46. package/docs/api/functions/extractLinkedInHandle.html +2 -2
  47. package/docs/api/functions/fetchCookiesFromCosiall.html +2 -2
  48. package/docs/api/functions/fetchProfileEmailsFromCosiall.html +2 -2
  49. package/docs/api/functions/forceRefreshCookies.html +1 -1
  50. package/docs/api/functions/getAccountForSession.html +1 -1
  51. package/docs/api/functions/getAccountsSummary.html +1 -1
  52. package/docs/api/functions/getCompaniesBatch.html +2 -2
  53. package/docs/api/functions/getCompanyById.html +2 -2
  54. package/docs/api/functions/getCompanyByUrl.html +1 -1
  55. package/docs/api/functions/getConfig.html +1 -1
  56. package/docs/api/functions/getCookiePoolHealth.html +1 -1
  57. package/docs/api/functions/getProfileByUrn.html +2 -2
  58. package/docs/api/functions/getProfileByVanity.html +2 -2
  59. package/docs/api/functions/getProfilesBatch.html +1 -1
  60. package/docs/api/functions/getRequestHistory.html +1 -1
  61. package/docs/api/functions/getSalesNavigatorProfileDetails.html +1 -1
  62. package/docs/api/functions/getSalesNavigatorProfileFull.html +2 -2
  63. package/docs/api/functions/getSmartLeadToken.html +1 -1
  64. package/docs/api/functions/getSmartLeadTokenCacheStats.html +2 -2
  65. package/docs/api/functions/getSmartLeadUser.html +2 -2
  66. package/docs/api/functions/getSnapshot.html +1 -1
  67. package/docs/api/functions/getYearsAtCompanyOptions.html +2 -2
  68. package/docs/api/functions/getYearsInPositionOptions.html +2 -2
  69. package/docs/api/functions/getYearsOfExperienceOptions.html +2 -2
  70. package/docs/api/functions/incrementMetric.html +1 -1
  71. package/docs/api/functions/initializeCookiePool.html +1 -1
  72. package/docs/api/functions/initializeLinkedInClient.html +1 -1
  73. package/docs/api/functions/isBusinessEmail.html +2 -2
  74. package/docs/api/functions/isDisposableDomain.html +2 -2
  75. package/docs/api/functions/isDisposableEmail.html +2 -2
  76. package/docs/api/functions/isPersonalDomain.html +2 -2
  77. package/docs/api/functions/isPersonalEmail.html +2 -2
  78. package/docs/api/functions/isRoleAccount.html +2 -2
  79. package/docs/api/functions/isValidEmailSyntax.html +2 -2
  80. package/docs/api/functions/parseFullProfile.html +2 -2
  81. package/docs/api/functions/parseSalesSearchResults.html +1 -1
  82. package/docs/api/functions/reportAccountFailure.html +1 -1
  83. package/docs/api/functions/reportAccountSuccess.html +1 -1
  84. package/docs/api/functions/resolveCompanyUniversalName.html +1 -1
  85. package/docs/api/functions/searchSalesLeads.html +2 -2
  86. package/docs/api/functions/selectAccountForRequest.html +1 -1
  87. package/docs/api/functions/setAccountForSession.html +1 -1
  88. package/docs/api/functions/typeahead.html +1 -1
  89. package/docs/api/functions/verifyEmailMx.html +1 -1
  90. package/docs/api/hierarchy.html +1 -1
  91. package/docs/api/index.html +3 -3
  92. package/docs/api/interfaces/AccountCookies.html +2 -2
  93. package/docs/api/interfaces/BatchEnrichmentOptions.html +8 -8
  94. package/docs/api/interfaces/CacheAdapter.html +4 -4
  95. package/docs/api/interfaces/CanonicalEmail.html +8 -8
  96. package/docs/api/interfaces/Company.html +2 -2
  97. package/docs/api/interfaces/ConstructConfig.html +5 -5
  98. package/docs/api/interfaces/CosiallProfileEmailsResponse.html +6 -6
  99. package/docs/api/interfaces/EnrichmentCandidate.html +4 -4
  100. package/docs/api/interfaces/EnrichmentClient.html +6 -6
  101. package/docs/api/interfaces/EnrichmentClientConfig.html +7 -7
  102. package/docs/api/interfaces/EnrichmentLogger.html +3 -3
  103. package/docs/api/interfaces/EnrichmentOptions.html +6 -6
  104. package/docs/api/interfaces/HunterConfig.html +3 -3
  105. package/docs/api/interfaces/LddConfig.html +3 -3
  106. package/docs/api/interfaces/LddProfileData.html +2 -2
  107. package/docs/api/interfaces/LinkedInClientConfig.html +2 -2
  108. package/docs/api/interfaces/LinkedInCookie.html +2 -2
  109. package/docs/api/interfaces/LinkedInPosition.html +2 -2
  110. package/docs/api/interfaces/LinkedInProfile.html +2 -2
  111. package/docs/api/interfaces/LinkedInSpotlightBadge.html +2 -2
  112. package/docs/api/interfaces/LinkedInTenure.html +2 -2
  113. package/docs/api/interfaces/Metrics.html +2 -2
  114. package/docs/api/interfaces/MetricsSnapshot.html +2 -2
  115. package/docs/api/interfaces/ProfileEducation.html +2 -2
  116. package/docs/api/interfaces/ProfileEmailsLookupOptions.html +5 -5
  117. package/docs/api/interfaces/ProfilePosition.html +2 -2
  118. package/docs/api/interfaces/ProfileSkill.html +2 -2
  119. package/docs/api/interfaces/ProviderResult.html +6 -6
  120. package/docs/api/interfaces/ProvidersConfig.html +6 -9
  121. package/docs/api/interfaces/RequestHistoryEntry.html +2 -2
  122. package/docs/api/interfaces/SalesLeadSearchResult.html +2 -2
  123. package/docs/api/interfaces/SalesNavigatorContactInfo.html +2 -2
  124. package/docs/api/interfaces/SalesNavigatorPosition.html +2 -2
  125. package/docs/api/interfaces/SalesNavigatorProfile.html +2 -2
  126. package/docs/api/interfaces/SalesNavigatorProfileFull.html +4 -4
  127. package/docs/api/interfaces/SearchSalesResult.html +2 -2
  128. package/docs/api/interfaces/SmartLeadAuthConfig.html +4 -4
  129. package/docs/api/interfaces/SmartLeadCredentials.html +2 -2
  130. package/docs/api/interfaces/SmartLeadLoginResponse.html +2 -2
  131. package/docs/api/interfaces/SmartLeadUser.html +2 -2
  132. package/docs/api/interfaces/SmartProspectConfig.html +8 -8
  133. package/docs/api/interfaces/SmartProspectContact.html +2 -2
  134. package/docs/api/interfaces/SmartProspectSearchFilters.html +21 -21
  135. package/docs/api/interfaces/TypeaheadItem.html +2 -2
  136. package/docs/api/interfaces/TypeaheadResult.html +2 -2
  137. package/docs/api/interfaces/VerificationResult.html +9 -9
  138. package/docs/api/types/CostCallback.html +2 -2
  139. package/docs/api/types/Geo.html +2 -2
  140. package/docs/api/types/LddApiResponse.html +1 -1
  141. package/docs/api/types/ProviderFunc.html +2 -2
  142. package/docs/api/types/ProviderName.html +2 -2
  143. package/docs/api/types/SalesSearchFilters.html +2 -2
  144. package/docs/api/types/TypeaheadType.html +1 -1
  145. package/docs/api/variables/COMPANY_SIZE_OPTIONS.html +1 -1
  146. package/docs/api/variables/DEFAULT_PROVIDER_ORDER.html +3 -4
  147. package/docs/api/variables/DISPOSABLE_DOMAINS.html +2 -2
  148. package/docs/api/variables/FUNCTION_OPTIONS.html +1 -1
  149. package/docs/api/variables/INDUSTRY_OPTIONS.html +1 -1
  150. package/docs/api/variables/LANGUAGE_OPTIONS.html +1 -1
  151. package/docs/api/variables/PERSONAL_DOMAINS.html +2 -2
  152. package/docs/api/variables/PROVIDER_COSTS.html +3 -5
  153. package/docs/api/variables/REGION_OPTIONS.html +1 -1
  154. package/docs/api/variables/SENIORITY_OPTIONS.html +2 -2
  155. package/docs/api/variables/YEARS_OPTIONS.html +1 -1
  156. package/package.json +1 -1
  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
  163. package/docs/api/interfaces/DropcontactConfig.html +0 -3
@@ -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 {
@@ -99,14 +99,16 @@ function extractEmailsToVerify(candidate) {
99
99
  */
100
100
  async function verifyEmailStandard(email, apiKey, apiUrl, timeoutMs, useDeepVerify) {
101
101
  const params = new URLSearchParams({
102
- api_key: apiKey,
103
102
  email: email,
104
103
  });
105
104
  if (useDeepVerify) {
106
105
  params.set("mode", "deepverify");
107
106
  }
107
+ const url = `${apiUrl}/v1/verify/single?${params.toString()}`;
108
108
  try {
109
- const response = await (0, http_retry_1.getWithRetry)(`${apiUrl}/v1/verify/single?${params.toString()}`, undefined, {
109
+ const response = await (0, http_retry_1.getWithRetry)(url, {
110
+ Authorization: apiKey,
111
+ }, {
110
112
  retries: 1,
111
113
  backoffMs: 500,
112
114
  timeoutMs,
@@ -114,7 +116,8 @@ async function verifyEmailStandard(email, apiKey, apiUrl, timeoutMs, useDeepVeri
114
116
  });
115
117
  return response;
116
118
  }
117
- catch {
119
+ catch (err) {
120
+ console.error("[BounceBan] Standard verification failed:", err instanceof Error ? err.message : err);
118
121
  return null;
119
122
  }
120
123
  }
@@ -128,15 +131,17 @@ async function verifyEmailStandard(email, apiKey, apiUrl, timeoutMs, useDeepVeri
128
131
  */
129
132
  async function verifyEmailWaterfall(email, apiKey, waterfallApiUrl, timeoutMs, useDeepVerify) {
130
133
  const params = new URLSearchParams({
131
- api_key: apiKey,
132
134
  email: email,
133
135
  timeout: String(Math.floor(timeoutMs / 1000)), // Convert to seconds
134
136
  });
135
137
  if (useDeepVerify) {
136
138
  params.set("mode", "deepverify");
137
139
  }
140
+ const url = `${waterfallApiUrl}/v1/verify/single?${params.toString()}`;
138
141
  try {
139
- const response = await (0, http_retry_1.getWithRetry)(`${waterfallApiUrl}/v1/verify/single?${params.toString()}`, undefined, {
142
+ const response = await (0, http_retry_1.getWithRetry)(url, {
143
+ Authorization: apiKey,
144
+ }, {
140
145
  retries: 2, // Waterfall allows free retries within 30 mins
141
146
  backoffMs: 5000, // Wait 5s between retries as recommended
142
147
  timeoutMs: timeoutMs + 10000, // Add buffer for network
@@ -144,7 +149,8 @@ async function verifyEmailWaterfall(email, apiKey, waterfallApiUrl, timeoutMs, u
144
149
  });
145
150
  return response;
146
151
  }
147
- catch {
152
+ catch (err) {
153
+ console.error("[BounceBan] Waterfall verification failed:", err instanceof Error ? err.message : err);
148
154
  return null;
149
155
  }
150
156
  }
@@ -256,9 +262,9 @@ async function submitBulkVerification(emails, apiKey, apiUrl, useDeepVerify) {
256
262
  method: "POST",
257
263
  headers: {
258
264
  "Content-Type": "application/json",
265
+ Authorization: apiKey,
259
266
  },
260
267
  body: JSON.stringify({
261
- api_key: apiKey,
262
268
  emails: emails,
263
269
  mode: useDeepVerify ? "deepverify" : "regular",
264
270
  }),
@@ -279,10 +285,13 @@ async function submitBulkVerification(emails, apiKey, apiUrl, useDeepVerify) {
279
285
  async function checkBulkStatus(taskId, apiKey, apiUrl) {
280
286
  try {
281
287
  const params = new URLSearchParams({
282
- api_key: apiKey,
283
288
  id: taskId,
284
289
  });
285
- const response = await fetch(`${apiUrl}/v1/verify/bulk/status?${params.toString()}`);
290
+ const response = await fetch(`${apiUrl}/v1/verify/bulk/status?${params.toString()}`, {
291
+ headers: {
292
+ Authorization: apiKey,
293
+ },
294
+ });
286
295
  if (!response.ok) {
287
296
  return null;
288
297
  }
@@ -301,9 +310,9 @@ async function fetchBulkResults(taskId, apiKey, apiUrl, page = 1, perPage = 100)
301
310
  method: "POST",
302
311
  headers: {
303
312
  "Content-Type": "application/json",
313
+ Authorization: apiKey,
304
314
  },
305
315
  body: JSON.stringify({
306
- api_key: apiKey,
307
316
  id: taskId,
308
317
  page: page,
309
318
  per_page: perPage,
@@ -68,104 +68,112 @@ function createConstructProvider(config) {
68
68
  const timeoutMs = config?.timeoutMs ?? 5000;
69
69
  const smtpVerifyDelayMs = config?.smtpVerifyDelayMs ?? 2000; // Delay between SMTP checks
70
70
  async function fetchEmail(candidate) {
71
- const { firstName: first, lastName: last } = (0, candidate_parser_1.extractName)(candidate);
72
- const { domain } = (0, candidate_parser_1.extractCompany)(candidate);
73
- // Skip if missing required fields
74
- if (!first || !domain) {
75
- return null;
76
- }
77
- // Skip personal domains
78
- if ((0, personal_domains_1.isPersonalDomain)(domain)) {
79
- return null;
80
- }
81
- const candidates = buildCandidates({ first, last, domain });
82
- const max = Math.min(candidates.length, maxAttempts);
83
- // First, check if domain is catch-all
84
- const catchAllResult = await (0, mx_1.checkDomainCatchAll)(domain, {
85
- timeoutMs: 10000,
86
- });
87
- const isCatchAll = catchAllResult.isCatchAll;
88
- // Collect ALL valid email patterns (not just first match)
89
- const validEmails = [];
90
- // Track all attempted patterns for debugging
91
- const attemptedPatterns = [];
92
- // If NOT catch-all, we can verify each email via SMTP
93
- if (isCatchAll === false) {
94
- // Verify emails one by one, stop when we find a valid one
95
- const emailsToVerify = candidates.slice(0, max);
96
- for (let i = 0; i < emailsToVerify.length; i++) {
97
- const email = emailsToVerify[i];
98
- // If we already found a valid email, skip the rest
99
- if (validEmails.length > 0) {
100
- attemptedPatterns.push({ email, status: "skipped" });
101
- continue;
102
- }
103
- // Add delay between checks (except first one)
104
- if (i > 0) {
105
- await new Promise((resolve) => setTimeout(resolve, smtpVerifyDelayMs));
106
- }
107
- // Verify single email
108
- const results = await (0, mx_1.verifyEmailsExist)([email], {
109
- delayMs: 0,
110
- timeoutMs,
111
- });
112
- const result = results[0];
113
- if (result.exists === true) {
114
- // Email confirmed to exist!
115
- attemptedPatterns.push({ email, status: "exists" });
116
- validEmails.push({
117
- email: result.email,
118
- verified: true,
119
- confidence: 95, // High confidence - SMTP verified
120
- isCatchAll: false,
121
- metadata: {
122
- pattern: result.email.split("@")[0],
123
- mxRecords: catchAllResult.mxRecords,
124
- smtpVerified: true,
125
- attemptedPatterns, // Include what was tried
126
- },
71
+ try {
72
+ const { firstName: first, lastName: last } = (0, candidate_parser_1.extractName)(candidate);
73
+ const { domain } = (0, candidate_parser_1.extractCompany)(candidate);
74
+ // Skip if missing required fields
75
+ if (!first || !domain) {
76
+ return null;
77
+ }
78
+ // Skip personal domains
79
+ if ((0, personal_domains_1.isPersonalDomain)(domain)) {
80
+ return null;
81
+ }
82
+ const candidates = buildCandidates({ first, last, domain });
83
+ const max = Math.min(candidates.length, maxAttempts);
84
+ // First, check if domain is catch-all
85
+ const catchAllResult = await (0, mx_1.checkDomainCatchAll)(domain, {
86
+ timeoutMs: 10000,
87
+ });
88
+ const isCatchAll = catchAllResult.isCatchAll;
89
+ // Collect ALL valid email patterns (not just first match)
90
+ const validEmails = [];
91
+ // Track all attempted patterns for debugging
92
+ const attemptedPatterns = [];
93
+ // If NOT catch-all, we can verify each email via SMTP
94
+ if (isCatchAll === false) {
95
+ // Verify emails one by one, stop when we find a valid one
96
+ const emailsToVerify = candidates.slice(0, max);
97
+ for (let i = 0; i < emailsToVerify.length; i++) {
98
+ const email = emailsToVerify[i];
99
+ // If we already found a valid email, skip the rest
100
+ if (validEmails.length > 0) {
101
+ attemptedPatterns.push({ email, status: "skipped" });
102
+ continue;
103
+ }
104
+ // Add delay between checks (except first one)
105
+ if (i > 0) {
106
+ await new Promise((resolve) => setTimeout(resolve, smtpVerifyDelayMs));
107
+ }
108
+ // Verify single email
109
+ const results = await (0, mx_1.verifyEmailsExist)([email], {
110
+ delayMs: 0,
111
+ timeoutMs,
127
112
  });
128
- // Found one! Stop checking more
129
- break;
130
- }
131
- else if (result.exists === false) {
132
- attemptedPatterns.push({ email, status: "not_found" });
113
+ const result = results[0];
114
+ if (result.exists === true) {
115
+ // Email confirmed to exist!
116
+ attemptedPatterns.push({ email, status: "exists" });
117
+ validEmails.push({
118
+ email: result.email,
119
+ verified: true,
120
+ confidence: 80, // Good confidence - SMTP verified pattern guess
121
+ // Note: 80% is appropriate for pattern guessing with SMTP verification
122
+ // Higher-priority sources like SmartProspect (90%) should rank above
123
+ isCatchAll: false,
124
+ metadata: {
125
+ pattern: result.email.split("@")[0],
126
+ mxRecords: catchAllResult.mxRecords,
127
+ smtpVerified: true,
128
+ attemptedPatterns, // Include what was tried
129
+ },
130
+ });
131
+ // Found one! Stop checking more
132
+ break;
133
+ }
134
+ else if (result.exists === false) {
135
+ attemptedPatterns.push({ email, status: "not_found" });
136
+ }
137
+ else {
138
+ attemptedPatterns.push({ email, status: "unknown" });
139
+ }
133
140
  }
134
- else {
135
- attemptedPatterns.push({ email, status: "unknown" });
141
+ // If no valid email found, include attempted patterns in metadata
142
+ if (validEmails.length === 0 && attemptedPatterns.length > 0) {
143
+ // Return null but could add metadata about attempts
136
144
  }
137
145
  }
138
- // If no valid email found, include attempted patterns in metadata
139
- if (validEmails.length === 0 && attemptedPatterns.length > 0) {
140
- // Return null but could add metadata about attempts
141
- }
142
- }
143
- else {
144
- // Catch-all or unknown - fall back to MX verification only
145
- for (let i = 0; i < max; i++) {
146
- const email = candidates[i];
147
- const verification = await (0, mx_1.verifyEmailMx)(email, { timeoutMs });
148
- if (verification.valid === true && verification.confidence >= 50) {
149
- validEmails.push({
150
- email,
151
- verified: true,
152
- confidence: verification.confidence,
153
- isCatchAll: isCatchAll ?? undefined,
154
- metadata: {
155
- pattern: email.split("@")[0],
156
- mxRecords: verification.mxRecords,
157
- smtpVerified: false,
158
- },
159
- });
146
+ else {
147
+ // Catch-all or unknown - fall back to MX verification only
148
+ for (let i = 0; i < max; i++) {
149
+ const email = candidates[i];
150
+ const verification = await (0, mx_1.verifyEmailMx)(email, { timeoutMs });
151
+ if (verification.valid === true && verification.confidence >= 50) {
152
+ validEmails.push({
153
+ email,
154
+ verified: true,
155
+ confidence: verification.confidence,
156
+ isCatchAll: isCatchAll ?? undefined,
157
+ metadata: {
158
+ pattern: email.split("@")[0],
159
+ mxRecords: verification.mxRecords,
160
+ smtpVerified: false,
161
+ },
162
+ });
163
+ }
160
164
  }
161
165
  }
166
+ if (validEmails.length === 0) {
167
+ return null;
168
+ }
169
+ // Sort by confidence
170
+ validEmails.sort((a, b) => (b.confidence ?? 0) - (a.confidence ?? 0));
171
+ return { emails: validEmails };
162
172
  }
163
- if (validEmails.length === 0) {
173
+ catch (err) {
174
+ console.error("[Construct] Email pattern generation failed:", err instanceof Error ? err.message : err);
164
175
  return null;
165
176
  }
166
- // Sort by confidence
167
- validEmails.sort((a, b) => (b.confidence ?? 0) - (a.confidence ?? 0));
168
- return { emails: validEmails };
169
177
  }
170
178
  // Mark provider name for orchestrator
171
179
  fetchEmail.__name = "construct";
@@ -98,8 +98,8 @@ function createCosiallProvider(config) {
98
98
  }));
99
99
  return { emails };
100
100
  }
101
- catch {
102
- // Silently fail - provider failures shouldn't stop the enrichment flow
101
+ catch (err) {
102
+ console.error("[Cosiall] Profile email lookup failed:", err instanceof Error ? err.message : err);
103
103
  return null;
104
104
  }
105
105
  }
@@ -148,7 +148,8 @@ function createHunterProvider(config) {
148
148
  }
149
149
  return null;
150
150
  }
151
- catch {
151
+ catch (err) {
152
+ console.error("[Hunter] Email lookup failed:", err instanceof Error ? err.message : err);
152
153
  return null;
153
154
  }
154
155
  }
@@ -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";