linkedin-secret-sauce 0.12.0 → 0.12.2

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 (44) hide show
  1. package/README.md +50 -21
  2. package/dist/cosiall-client.d.ts +1 -1
  3. package/dist/cosiall-client.js +1 -1
  4. package/dist/enrichment/index.d.ts +3 -3
  5. package/dist/enrichment/index.js +19 -2
  6. package/dist/enrichment/matching.d.ts +29 -9
  7. package/dist/enrichment/matching.js +545 -142
  8. package/dist/enrichment/providers/bounceban.d.ts +82 -0
  9. package/dist/enrichment/providers/bounceban.js +447 -0
  10. package/dist/enrichment/providers/bouncer.d.ts +1 -1
  11. package/dist/enrichment/providers/bouncer.js +19 -21
  12. package/dist/enrichment/providers/construct.d.ts +1 -1
  13. package/dist/enrichment/providers/construct.js +22 -38
  14. package/dist/enrichment/providers/cosiall.d.ts +27 -0
  15. package/dist/enrichment/providers/cosiall.js +109 -0
  16. package/dist/enrichment/providers/dropcontact.d.ts +15 -9
  17. package/dist/enrichment/providers/dropcontact.js +188 -19
  18. package/dist/enrichment/providers/hunter.d.ts +8 -1
  19. package/dist/enrichment/providers/hunter.js +52 -28
  20. package/dist/enrichment/providers/index.d.ts +10 -7
  21. package/dist/enrichment/providers/index.js +12 -1
  22. package/dist/enrichment/providers/ldd.d.ts +1 -10
  23. package/dist/enrichment/providers/ldd.js +20 -97
  24. package/dist/enrichment/providers/smartprospect.js +28 -48
  25. package/dist/enrichment/providers/snovio.d.ts +1 -1
  26. package/dist/enrichment/providers/snovio.js +29 -31
  27. package/dist/enrichment/providers/trykitt.d.ts +63 -0
  28. package/dist/enrichment/providers/trykitt.js +210 -0
  29. package/dist/enrichment/types.d.ts +234 -17
  30. package/dist/enrichment/types.js +60 -48
  31. package/dist/enrichment/utils/candidate-parser.d.ts +107 -0
  32. package/dist/enrichment/utils/candidate-parser.js +173 -0
  33. package/dist/enrichment/utils/noop-provider.d.ts +39 -0
  34. package/dist/enrichment/utils/noop-provider.js +37 -0
  35. package/dist/enrichment/utils/rate-limiter.d.ts +103 -0
  36. package/dist/enrichment/utils/rate-limiter.js +204 -0
  37. package/dist/enrichment/utils/validation.d.ts +75 -3
  38. package/dist/enrichment/utils/validation.js +164 -11
  39. package/dist/linkedin-api.d.ts +40 -1
  40. package/dist/linkedin-api.js +160 -27
  41. package/dist/types.d.ts +50 -1
  42. package/dist/utils/lru-cache.d.ts +105 -0
  43. package/dist/utils/lru-cache.js +175 -0
  44. package/package.json +25 -26
@@ -1,10 +1,13 @@
1
1
  /**
2
2
  * Email Enrichment Providers
3
3
  */
4
- export { createConstructProvider } from './construct';
5
- export { createLddProvider } from './ldd';
6
- export { createSmartProspectProvider } from './smartprospect';
7
- export { createHunterProvider } from './hunter';
8
- export { createDropcontactProvider } from './dropcontact';
9
- export { createBouncerProvider, verifyEmailWithBouncer, checkCatchAllDomain, verifyEmailsBatch } from './bouncer';
10
- export { createSnovioProvider, findEmailsWithSnovio, verifyEmailWithSnovio, clearSnovioTokenCache } from './snovio';
4
+ export { createConstructProvider } from "./construct";
5
+ export { createLddProvider } from "./ldd";
6
+ export { createSmartProspectProvider } from "./smartprospect";
7
+ export { createCosiallProvider } from "./cosiall";
8
+ export { createTryKittProvider, findEmailWithTryKitt, verifyEmailWithTryKitt, } from "./trykitt";
9
+ export { createHunterProvider } from "./hunter";
10
+ export { createDropcontactProvider } from "./dropcontact";
11
+ export { createBouncerProvider, verifyEmailWithBouncer, checkCatchAllDomain, verifyEmailsBatch, } from "./bouncer";
12
+ export { createBounceBanProvider, verifyEmailWithBounceBan, verifyEmailsBatchWithBounceBan, checkCatchAllWithBounceBan, } from "./bounceban";
13
+ export { createSnovioProvider, findEmailsWithSnovio, verifyEmailWithSnovio, clearSnovioTokenCache, } from "./snovio";
@@ -3,13 +3,19 @@
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.verifyEmailsBatch = exports.checkCatchAllDomain = exports.verifyEmailWithBouncer = exports.createBouncerProvider = exports.createDropcontactProvider = exports.createHunterProvider = 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.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;
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");
10
10
  Object.defineProperty(exports, "createLddProvider", { enumerable: true, get: function () { return ldd_1.createLddProvider; } });
11
11
  var smartprospect_1 = require("./smartprospect");
12
12
  Object.defineProperty(exports, "createSmartProspectProvider", { enumerable: true, get: function () { return smartprospect_1.createSmartProspectProvider; } });
13
+ var cosiall_1 = require("./cosiall");
14
+ Object.defineProperty(exports, "createCosiallProvider", { enumerable: true, get: function () { return cosiall_1.createCosiallProvider; } });
15
+ var trykitt_1 = require("./trykitt");
16
+ Object.defineProperty(exports, "createTryKittProvider", { enumerable: true, get: function () { return trykitt_1.createTryKittProvider; } });
17
+ Object.defineProperty(exports, "findEmailWithTryKitt", { enumerable: true, get: function () { return trykitt_1.findEmailWithTryKitt; } });
18
+ Object.defineProperty(exports, "verifyEmailWithTryKitt", { enumerable: true, get: function () { return trykitt_1.verifyEmailWithTryKitt; } });
13
19
  var hunter_1 = require("./hunter");
14
20
  Object.defineProperty(exports, "createHunterProvider", { enumerable: true, get: function () { return hunter_1.createHunterProvider; } });
15
21
  var dropcontact_1 = require("./dropcontact");
@@ -19,6 +25,11 @@ Object.defineProperty(exports, "createBouncerProvider", { enumerable: true, get:
19
25
  Object.defineProperty(exports, "verifyEmailWithBouncer", { enumerable: true, get: function () { return bouncer_1.verifyEmailWithBouncer; } });
20
26
  Object.defineProperty(exports, "checkCatchAllDomain", { enumerable: true, get: function () { return bouncer_1.checkCatchAllDomain; } });
21
27
  Object.defineProperty(exports, "verifyEmailsBatch", { enumerable: true, get: function () { return bouncer_1.verifyEmailsBatch; } });
28
+ var bounceban_1 = require("./bounceban");
29
+ Object.defineProperty(exports, "createBounceBanProvider", { enumerable: true, get: function () { return bounceban_1.createBounceBanProvider; } });
30
+ Object.defineProperty(exports, "verifyEmailWithBounceBan", { enumerable: true, get: function () { return bounceban_1.verifyEmailWithBounceBan; } });
31
+ Object.defineProperty(exports, "verifyEmailsBatchWithBounceBan", { enumerable: true, get: function () { return bounceban_1.verifyEmailsBatchWithBounceBan; } });
32
+ Object.defineProperty(exports, "checkCatchAllWithBounceBan", { enumerable: true, get: function () { return bounceban_1.checkCatchAllWithBounceBan; } });
22
33
  var snovio_1 = require("./snovio");
23
34
  Object.defineProperty(exports, "createSnovioProvider", { enumerable: true, get: function () { return snovio_1.createSnovioProvider; } });
24
35
  Object.defineProperty(exports, "findEmailsWithSnovio", { enumerable: true, get: function () { return snovio_1.findEmailsWithSnovio; } });
@@ -9,16 +9,7 @@
9
9
  * while usernames can be changed by users at any time.
10
10
  */
11
11
  import type { EnrichmentCandidate, ProviderResult, ProviderMultiResult, LddConfig } from "../types";
12
- /**
13
- * Extract numeric LinkedIn ID from various formats:
14
- * - Direct number: "307567"
15
- * - URN format: "urn:li:member:307567"
16
- * - Full URN: "urn:li:fs_salesProfile:(ACwAAAAEsW8B...,NAME_SEARCH,PJOV)"
17
- * - objectUrn: "urn:li:member:307567"
18
- *
19
- * Returns the numeric ID as a string, or null if not found.
20
- */
21
- export declare function extractNumericLinkedInId(input: string | undefined | null): string | null;
12
+ export { extractNumericLinkedInId } from "../utils/candidate-parser";
22
13
  /**
23
14
  * Create the LDD provider function
24
15
  *
@@ -10,87 +10,14 @@
10
10
  * while usernames can be changed by users at any time.
11
11
  */
12
12
  Object.defineProperty(exports, "__esModule", { value: true });
13
- exports.extractNumericLinkedInId = extractNumericLinkedInId;
13
+ exports.extractNumericLinkedInId = void 0;
14
14
  exports.createLddProvider = createLddProvider;
15
- const validation_1 = require("../utils/validation");
16
15
  const http_retry_1 = require("../utils/http-retry");
17
- /**
18
- * Extract numeric LinkedIn ID from various formats:
19
- * - Direct number: "307567"
20
- * - URN format: "urn:li:member:307567"
21
- * - Full URN: "urn:li:fs_salesProfile:(ACwAAAAEsW8B...,NAME_SEARCH,PJOV)"
22
- * - objectUrn: "urn:li:member:307567"
23
- *
24
- * Returns the numeric ID as a string, or null if not found.
25
- */
26
- function extractNumericLinkedInId(input) {
27
- if (!input)
28
- return null;
29
- const trimmed = input.trim();
30
- // Direct numeric ID
31
- if (/^\d+$/.test(trimmed)) {
32
- return trimmed;
33
- }
34
- // URN format: urn:li:member:307567
35
- const memberMatch = trimmed.match(/urn:li:member:(\d+)/i);
36
- if (memberMatch) {
37
- return memberMatch[1];
38
- }
39
- // Sales profile URN with numeric ID
40
- const salesMatch = trimmed.match(/urn:li:fs_salesProfile:\((\d+),/i);
41
- if (salesMatch) {
42
- return salesMatch[1];
43
- }
44
- return null;
45
- }
46
- /**
47
- * Extract LinkedIn username from candidate
48
- */
49
- function extractUsername(candidate) {
50
- // Direct username
51
- const directUsername = candidate.linkedinUsername || candidate.linkedin_username;
52
- if (directUsername)
53
- return directUsername;
54
- // Extract from URL
55
- const url = candidate.linkedinUrl || candidate.linkedin_url;
56
- if (url) {
57
- const extracted = (0, validation_1.extractLinkedInUsername)(url);
58
- if (extracted)
59
- return extracted;
60
- }
61
- return null;
62
- }
63
- /**
64
- * Extract numeric LinkedIn ID from candidate.
65
- * Checks multiple fields in order of preference:
66
- * 1. numericLinkedInId / numeric_linkedin_id (direct numeric ID)
67
- * 2. objectUrn / object_urn (URN format like "urn:li:member:307567")
68
- * 3. linkedinId / linkedin_id (may contain URN or numeric ID)
69
- */
70
- function extractNumericId(candidate) {
71
- // Direct numeric ID field
72
- const numericId = candidate.numericLinkedInId || candidate.numeric_linkedin_id;
73
- if (numericId) {
74
- const extracted = extractNumericLinkedInId(numericId);
75
- if (extracted)
76
- return extracted;
77
- }
78
- // objectUrn field (from Sales Navigator search results)
79
- const objectUrn = candidate.objectUrn || candidate.object_urn;
80
- if (objectUrn) {
81
- const extracted = extractNumericLinkedInId(objectUrn);
82
- if (extracted)
83
- return extracted;
84
- }
85
- // linkedinId field (may contain URN or numeric ID)
86
- const linkedinId = candidate.linkedinId || candidate.linkedin_id;
87
- if (linkedinId) {
88
- const extracted = extractNumericLinkedInId(linkedinId);
89
- if (extracted)
90
- return extracted;
91
- }
92
- return null;
93
- }
16
+ const candidate_parser_1 = require("../utils/candidate-parser");
17
+ const noop_provider_1 = require("../utils/noop-provider");
18
+ // Re-export for backwards compatibility
19
+ var candidate_parser_2 = require("../utils/candidate-parser");
20
+ Object.defineProperty(exports, "extractNumericLinkedInId", { enumerable: true, get: function () { return candidate_parser_2.extractNumericLinkedInId; } });
94
21
  /**
95
22
  * Create the LDD provider function
96
23
  *
@@ -101,23 +28,19 @@ function extractNumericId(candidate) {
101
28
  function createLddProvider(config) {
102
29
  const { apiUrl, apiToken } = config;
103
30
  if (!apiUrl || !apiToken) {
104
- // Return a no-op provider if not configured
105
- const noopProvider = async () => null;
106
- noopProvider.__name = "ldd";
107
- return noopProvider;
31
+ return (0, noop_provider_1.createNoOpProvider)("ldd");
108
32
  }
109
33
  async function fetchEmail(candidate) {
34
+ const linkedin = (0, candidate_parser_1.extractLinkedIn)(candidate);
110
35
  // PRIORITY 1: Try numeric ID first (stable identifier)
111
- const numericId = extractNumericId(candidate);
112
- if (numericId) {
113
- const result = await lookupByNumericId(numericId);
36
+ if (linkedin.numericId) {
37
+ const result = await lookupByNumericId(linkedin.numericId);
114
38
  if (result)
115
39
  return result;
116
40
  }
117
41
  // PRIORITY 2: Fall back to username (less reliable)
118
- const username = extractUsername(candidate);
119
- if (username) {
120
- const result = await lookupByUsername(username);
42
+ if (linkedin.username) {
43
+ const result = await lookupByUsername(linkedin.username);
121
44
  if (result)
122
45
  return result;
123
46
  }
@@ -152,11 +75,11 @@ function createLddProvider(config) {
152
75
  return 75; // Unknown type
153
76
  const type = emailType.toLowerCase();
154
77
  // Professional/business emails - highest confidence
155
- if (type === 'professional' || type === 'business' || type === 'work') {
78
+ if (type === "professional" || type === "business" || type === "work") {
156
79
  return 95;
157
80
  }
158
81
  // Personal emails - still valid but lower priority for B2B
159
- if (type === 'personal' || type === 'private') {
82
+ if (type === "personal" || type === "private") {
160
83
  return 70;
161
84
  }
162
85
  // Other types (e.g., "other", "unknown")
@@ -167,15 +90,15 @@ function createLddProvider(config) {
167
90
  */
168
91
  function classifyEmailType(emailType) {
169
92
  if (!emailType)
170
- return 'unknown';
93
+ return "unknown";
171
94
  const type = emailType.toLowerCase();
172
- if (type === 'professional' || type === 'business' || type === 'work') {
173
- return 'business';
95
+ if (type === "professional" || type === "business" || type === "work") {
96
+ return "business";
174
97
  }
175
- if (type === 'personal' || type === 'private') {
176
- return 'personal';
98
+ if (type === "personal" || type === "private") {
99
+ return "personal";
177
100
  }
178
- return 'unknown';
101
+ return "unknown";
179
102
  }
180
103
  function parseResponse(data) {
181
104
  if (!data.success || !data.data) {
@@ -18,6 +18,8 @@ exports.createSmartProspectProvider = createSmartProspectProvider;
18
18
  exports.createSmartProspectClient = createSmartProspectClient;
19
19
  const smartlead_auth_1 = require("../auth/smartlead-auth");
20
20
  const http_retry_1 = require("../utils/http-retry");
21
+ const candidate_parser_1 = require("../utils/candidate-parser");
22
+ const noop_provider_1 = require("../utils/noop-provider");
21
23
  const DEFAULT_API_URL = "https://prospect-api.smartlead.ai/api/search-email-leads";
22
24
  const DEFAULT_POLLING_CONFIG = {
23
25
  initialDelay: 500,
@@ -96,22 +98,6 @@ function calculateMatchScore(contact, searchName, company, title) {
96
98
  }
97
99
  return score;
98
100
  }
99
- /**
100
- * Extract name components from candidate
101
- */
102
- function extractNames(candidate) {
103
- const firstName = candidate.firstName ||
104
- candidate.first_name ||
105
- candidate.first ||
106
- candidate.name?.split(" ")?.[0] ||
107
- "";
108
- const lastName = candidate.lastName ||
109
- candidate.last_name ||
110
- candidate.last ||
111
- candidate.name?.split(" ")?.slice(1).join(" ") ||
112
- "";
113
- return { firstName, lastName };
114
- }
115
101
  /**
116
102
  * Create the SmartProspect provider function
117
103
  *
@@ -125,10 +111,7 @@ function createSmartProspectProvider(config) {
125
111
  const hasDirectToken = !!apiToken;
126
112
  const hasCredentials = !!email && !!password;
127
113
  if (!hasDirectToken && !hasCredentials) {
128
- // Return a no-op provider if not configured
129
- const noopProvider = async () => null;
130
- noopProvider.__name = "smartprospect";
131
- return noopProvider;
114
+ return (0, noop_provider_1.createNoOpProvider)("smartprospect");
132
115
  }
133
116
  /**
134
117
  * Get the current auth token (either direct or via login)
@@ -241,7 +224,9 @@ function createSmartProspectProvider(config) {
241
224
  await (0, http_retry_1.delay)(pollInterval);
242
225
  }
243
226
  catch (err) {
244
- if (err instanceof Error && err.message.includes("401") && hasCredentials) {
227
+ if (err instanceof Error &&
228
+ err.message.includes("401") &&
229
+ hasCredentials) {
245
230
  await handleAuthError();
246
231
  }
247
232
  await (0, http_retry_1.delay)(pollInterval);
@@ -372,23 +357,12 @@ function createSmartProspectProvider(config) {
372
357
  return confidence;
373
358
  }
374
359
  async function fetchEmail(candidate) {
375
- const { firstName, lastName } = extractNames(candidate);
360
+ const { firstName, fullName } = (0, candidate_parser_1.extractName)(candidate);
376
361
  if (!firstName) {
377
362
  return null; // Minimum requirement
378
363
  }
379
- const fullName = `${firstName} ${lastName}`.trim();
380
- const company = candidate.company ||
381
- candidate.currentCompany ||
382
- candidate.organization ||
383
- undefined;
384
- const title = candidate.title ||
385
- candidate.currentRole ||
386
- candidate.current_role ||
387
- undefined;
388
- const domain = candidate.domain ||
389
- candidate.companyDomain ||
390
- candidate.company_domain ||
391
- undefined;
364
+ const { company, domain } = (0, candidate_parser_1.extractCompany)(candidate);
365
+ const title = (0, candidate_parser_1.extractTitle)(candidate) ?? undefined;
392
366
  // Build search filters using correct API field names
393
367
  const filters = {
394
368
  name: [fullName],
@@ -410,7 +384,7 @@ function createSmartProspectProvider(config) {
410
384
  const scoredMatches = matches
411
385
  .map((contact) => ({
412
386
  contact,
413
- score: calculateMatchScore(contact, fullName, company, title),
387
+ score: calculateMatchScore(contact, fullName, company ?? undefined, title),
414
388
  }))
415
389
  .filter((m) => m.score > 0) // Only include matches with some relevance
416
390
  .sort((a, b) => b.score - a.score);
@@ -547,20 +521,26 @@ function createSmartProspectClient(config) {
547
521
  if (filters.level?.length)
548
522
  payload.level = filters.level;
549
523
  // Company filters (note: API uses companyName and companyDomain)
550
- if (filters.companyName?.length)
524
+ if (filters.companyName?.length) {
551
525
  payload.companyName = filters.companyName;
552
- if (filters.companyDomain?.length)
526
+ }
527
+ if (filters.companyDomain?.length) {
553
528
  payload.companyDomain = filters.companyDomain;
529
+ }
554
530
  // Industry filters (note: API uses companyIndustry and companySubIndustry)
555
- if (filters.companyIndustry?.length)
531
+ if (filters.companyIndustry?.length) {
556
532
  payload.companyIndustry = filters.companyIndustry;
557
- if (filters.companySubIndustry?.length)
533
+ }
534
+ if (filters.companySubIndustry?.length) {
558
535
  payload.companySubIndustry = filters.companySubIndustry;
536
+ }
559
537
  // Company size filters
560
- if (filters.companyHeadCount?.length)
538
+ if (filters.companyHeadCount?.length) {
561
539
  payload.companyHeadCount = filters.companyHeadCount;
562
- if (filters.companyRevenue?.length)
540
+ }
541
+ if (filters.companyRevenue?.length) {
563
542
  payload.companyRevenue = filters.companyRevenue;
543
+ }
564
544
  // Location filters
565
545
  if (filters.country?.length)
566
546
  payload.country = filters.country;
@@ -826,11 +806,11 @@ function createSmartProspectClient(config) {
826
806
  async function getLocations(type, options = {}) {
827
807
  const { search: searchQuery, limit = 20, offset = 0 } = options;
828
808
  const params = new URLSearchParams();
829
- params.set('limit', String(limit));
809
+ params.set("limit", String(limit));
830
810
  if (offset > 0)
831
- params.set('offset', String(offset));
811
+ params.set("offset", String(offset));
832
812
  if (searchQuery)
833
- params.set('search', searchQuery);
813
+ params.set("search", searchQuery);
834
814
  const makeRequest = async (token) => {
835
815
  return requestWithRetry(`${apiUrl}/${type}?${params.toString()}`, {
836
816
  method: "GET",
@@ -870,19 +850,19 @@ function createSmartProspectClient(config) {
870
850
  * Lookup countries (for typeahead)
871
851
  */
872
852
  async function getCountries(options) {
873
- return getLocations('countries', options);
853
+ return getLocations("countries", options);
874
854
  }
875
855
  /**
876
856
  * Lookup states (for typeahead)
877
857
  */
878
858
  async function getStates(options) {
879
- return getLocations('states', options);
859
+ return getLocations("states", options);
880
860
  }
881
861
  /**
882
862
  * Lookup cities (for typeahead)
883
863
  */
884
864
  async function getCities(options) {
885
- return getLocations('cities', options);
865
+ return getLocations("cities", options);
886
866
  }
887
867
  return {
888
868
  search,
@@ -12,7 +12,7 @@
12
12
  *
13
13
  * @see https://snov.io/api
14
14
  */
15
- import type { EnrichmentCandidate, ProviderResult, ProviderMultiResult, SnovioConfig, SnovioGetEmailsResponse, SnovioVerificationStatus } from '../types';
15
+ import type { EnrichmentCandidate, ProviderResult, ProviderMultiResult, SnovioConfig, SnovioGetEmailsResponse, SnovioVerificationStatus } from "../types";
16
16
  /**
17
17
  * Create the Snov.io email finder provider
18
18
  *
@@ -18,7 +18,8 @@ exports.createSnovioProvider = createSnovioProvider;
18
18
  exports.findEmailsWithSnovio = findEmailsWithSnovio;
19
19
  exports.verifyEmailWithSnovio = verifyEmailWithSnovio;
20
20
  exports.clearSnovioTokenCache = clearSnovioTokenCache;
21
- const DEFAULT_API_URL = 'https://api.snov.io';
21
+ const noop_provider_1 = require("../utils/noop-provider");
22
+ const DEFAULT_API_URL = "https://api.snov.io";
22
23
  const DEFAULT_TIMEOUT_MS = 30000;
23
24
  // Token cache
24
25
  let cachedAccessToken = null;
@@ -28,15 +29,15 @@ let tokenExpiresAt = 0;
28
29
  */
29
30
  function statusToConfidence(status) {
30
31
  switch (status) {
31
- case 'valid':
32
+ case "valid":
32
33
  return 95; // High confidence - verified email
33
- case 'catch_all':
34
+ case "catch_all":
34
35
  return 60; // Medium confidence - catch-all domain
35
- case 'unverifiable':
36
+ case "unverifiable":
36
37
  return 40; // Low confidence - could not verify
37
- case 'not_valid':
38
+ case "not_valid":
38
39
  return 0; // Invalid email
39
- case 'unknown':
40
+ case "unknown":
40
41
  default:
41
42
  return 30;
42
43
  }
@@ -48,17 +49,17 @@ function extractNameAndDomain(candidate) {
48
49
  const firstName = candidate.firstName ||
49
50
  candidate.first_name ||
50
51
  candidate.first ||
51
- candidate.name?.split(' ')?.[0] ||
52
- '';
52
+ candidate.name?.split(" ")?.[0] ||
53
+ "";
53
54
  const lastName = candidate.lastName ||
54
55
  candidate.last_name ||
55
56
  candidate.last ||
56
- candidate.name?.split(' ')?.slice(1).join(' ') ||
57
- '';
57
+ candidate.name?.split(" ")?.slice(1).join(" ") ||
58
+ "";
58
59
  const domain = candidate.domain ||
59
60
  candidate.companyDomain ||
60
61
  candidate.company_domain ||
61
- '';
62
+ "";
62
63
  if (!firstName || !domain) {
63
64
  return null;
64
65
  }
@@ -76,12 +77,12 @@ async function getAccessToken(userId, apiSecret, apiUrl, timeoutMs) {
76
77
  const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
77
78
  try {
78
79
  const response = await fetch(`${apiUrl}/v1/oauth/access_token`, {
79
- method: 'POST',
80
+ method: "POST",
80
81
  headers: {
81
- 'Content-Type': 'application/json',
82
+ "Content-Type": "application/json",
82
83
  },
83
84
  body: JSON.stringify({
84
- grant_type: 'client_credentials',
85
+ grant_type: "client_credentials",
85
86
  client_id: userId,
86
87
  client_secret: apiSecret,
87
88
  }),
@@ -90,7 +91,7 @@ async function getAccessToken(userId, apiSecret, apiUrl, timeoutMs) {
90
91
  if (!response.ok) {
91
92
  return null;
92
93
  }
93
- const data = await response.json();
94
+ const data = (await response.json());
94
95
  // Cache token with 5 minute buffer
95
96
  cachedAccessToken = data.access_token;
96
97
  tokenExpiresAt = Date.now() + (data.expires_in - 300) * 1000;
@@ -117,24 +118,24 @@ async function findEmailsByName(firstName, lastName, domain, accessToken, apiUrl
117
118
  domain,
118
119
  });
119
120
  const response = await fetch(`${apiUrl}/v1/get-emails-from-names?${params}`, {
120
- method: 'GET',
121
+ method: "GET",
121
122
  headers: {
122
- 'Accept': 'application/json',
123
+ Accept: "application/json",
123
124
  },
124
125
  signal: controller.signal,
125
126
  });
126
127
  if (!response.ok) {
127
128
  // Handle rate limiting
128
129
  if (response.status === 429) {
129
- throw new Error('Rate limited by Snov.io API');
130
+ throw new Error("Rate limited by Snov.io API");
130
131
  }
131
132
  return null;
132
133
  }
133
- const data = await response.json();
134
+ const data = (await response.json());
134
135
  return data;
135
136
  }
136
137
  catch (error) {
137
- if (error instanceof Error && error.name === 'AbortError') {
138
+ if (error instanceof Error && error.name === "AbortError") {
138
139
  return null; // Timeout
139
140
  }
140
141
  throw error;
@@ -152,10 +153,7 @@ async function findEmailsByName(firstName, lastName, domain, accessToken, apiUrl
152
153
  function createSnovioProvider(config) {
153
154
  const { userId, apiSecret, apiUrl = DEFAULT_API_URL, timeoutMs = DEFAULT_TIMEOUT_MS, } = config;
154
155
  if (!userId || !apiSecret) {
155
- // Return no-op provider if not configured
156
- const noop = async () => null;
157
- noop.__name = 'snovio';
158
- return noop;
156
+ return (0, noop_provider_1.createNoOpProvider)("snovio");
159
157
  }
160
158
  async function findEmails(candidate) {
161
159
  const nameAndDomain = extractNameAndDomain(candidate);
@@ -175,12 +173,12 @@ function createSnovioProvider(config) {
175
173
  }
176
174
  // Convert to provider multi-result format
177
175
  const emails = result.data.emails
178
- .filter((e) => e.email && e.emailStatus !== 'not_valid')
176
+ .filter((e) => e.email && e.emailStatus !== "not_valid")
179
177
  .map((e) => ({
180
178
  email: e.email,
181
- verified: e.emailStatus === 'valid',
179
+ verified: e.emailStatus === "valid",
182
180
  confidence: statusToConfidence(e.emailStatus),
183
- isCatchAll: e.emailStatus === 'catch_all',
181
+ isCatchAll: e.emailStatus === "catch_all",
184
182
  metadata: {
185
183
  snovioStatus: e.emailStatus,
186
184
  firstName: e.firstName,
@@ -199,7 +197,7 @@ function createSnovioProvider(config) {
199
197
  return { emails };
200
198
  }
201
199
  // Mark provider name for orchestrator
202
- findEmails.__name = 'snovio';
200
+ findEmails.__name = "snovio";
203
201
  return findEmails;
204
202
  }
205
203
  /**
@@ -251,16 +249,16 @@ async function verifyEmailWithSnovio(email, config) {
251
249
  email,
252
250
  });
253
251
  const response = await fetch(`${apiUrl}/v1/email-verifier?${params}`, {
254
- method: 'GET',
252
+ method: "GET",
255
253
  headers: {
256
- 'Accept': 'application/json',
254
+ Accept: "application/json",
257
255
  },
258
256
  signal: controller.signal,
259
257
  });
260
258
  if (!response.ok) {
261
259
  return null;
262
260
  }
263
- const data = await response.json();
261
+ const data = (await response.json());
264
262
  if (!data.success || !data.data?.emails?.length) {
265
263
  return null;
266
264
  }
@@ -0,0 +1,63 @@
1
+ /**
2
+ * TryKitt.ai Email Finder Provider
3
+ *
4
+ * AI-powered email finding with enterprise identity server verification.
5
+ * FREE for individuals with unlimited searches.
6
+ *
7
+ * Features:
8
+ * - Email finding by name + domain
9
+ * - Enterprise identity server catch-all verification (more accurate than SMTP)
10
+ * - <0.1% bounce rate claimed
11
+ * - 2-5x faster than traditional SMTP verification
12
+ *
13
+ * API Endpoints:
14
+ * - POST /job/find-email - Find email by name + domain
15
+ * - POST /job/verify-email - Verify existing email
16
+ *
17
+ * Rate Limits (Free tier):
18
+ * - 2 requests/second
19
+ * - 120 requests/minute
20
+ * - Unlimited monthly quota
21
+ *
22
+ * @see https://trykitt.ai
23
+ */
24
+ import type { EnrichmentCandidate, ProviderResult, TryKittConfig, TryKittFindEmailResponse } from "../types";
25
+ /**
26
+ * Create the TryKitt.ai email finder provider
27
+ *
28
+ * This provider uses TryKitt's AI-powered email finding with enterprise
29
+ * identity server verification for catch-all domains.
30
+ */
31
+ export declare function createTryKittProvider(config: TryKittConfig): (candidate: EnrichmentCandidate) => Promise<ProviderResult | null>;
32
+ /**
33
+ * Standalone function to find email via TryKitt.ai
34
+ *
35
+ * @example
36
+ * ```typescript
37
+ * const result = await findEmailWithTryKitt('John Doe', 'example.com', {
38
+ * apiKey: process.env.TRYKITT_API_KEY,
39
+ * });
40
+ * console.log(result?.email); // john.doe@example.com
41
+ * ```
42
+ */
43
+ export declare function findEmailWithTryKitt(fullName: string, domain: string, config: TryKittConfig, linkedinUrl?: string): Promise<TryKittFindEmailResponse | null>;
44
+ /**
45
+ * Verify an email address via TryKitt.ai
46
+ *
47
+ * @example
48
+ * ```typescript
49
+ * const result = await verifyEmailWithTryKitt('john@example.com', {
50
+ * apiKey: process.env.TRYKITT_API_KEY,
51
+ * });
52
+ * console.log(result?.result?.valid); // true
53
+ * console.log(result?.result?.is_catchall); // false
54
+ * ```
55
+ */
56
+ export declare function verifyEmailWithTryKitt(email: string, config: TryKittConfig): Promise<{
57
+ valid: boolean;
58
+ deliverable: boolean;
59
+ confidence: number;
60
+ isCatchAll: boolean;
61
+ isDisposable: boolean;
62
+ isRoleAccount: boolean;
63
+ } | null>;