linkedin-secret-sauce 0.3.29 → 0.5.0

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 (55) hide show
  1. package/dist/cookie-pool.d.ts +1 -1
  2. package/dist/cookie-pool.js +67 -35
  3. package/dist/cosiall-client.d.ts +20 -1
  4. package/dist/cosiall-client.js +48 -25
  5. package/dist/enrichment/index.d.ts +43 -0
  6. package/dist/enrichment/index.js +231 -0
  7. package/dist/enrichment/orchestrator.d.ts +31 -0
  8. package/dist/enrichment/orchestrator.js +218 -0
  9. package/dist/enrichment/providers/apollo.d.ts +11 -0
  10. package/dist/enrichment/providers/apollo.js +136 -0
  11. package/dist/enrichment/providers/construct.d.ts +11 -0
  12. package/dist/enrichment/providers/construct.js +107 -0
  13. package/dist/enrichment/providers/dropcontact.d.ts +16 -0
  14. package/dist/enrichment/providers/dropcontact.js +37 -0
  15. package/dist/enrichment/providers/hunter.d.ts +11 -0
  16. package/dist/enrichment/providers/hunter.js +162 -0
  17. package/dist/enrichment/providers/index.d.ts +9 -0
  18. package/dist/enrichment/providers/index.js +18 -0
  19. package/dist/enrichment/providers/ldd.d.ts +11 -0
  20. package/dist/enrichment/providers/ldd.js +110 -0
  21. package/dist/enrichment/providers/smartprospect.d.ts +11 -0
  22. package/dist/enrichment/providers/smartprospect.js +249 -0
  23. package/dist/enrichment/types.d.ts +329 -0
  24. package/dist/enrichment/types.js +31 -0
  25. package/dist/enrichment/utils/disposable-domains.d.ts +24 -0
  26. package/dist/enrichment/utils/disposable-domains.js +1011 -0
  27. package/dist/enrichment/utils/index.d.ts +6 -0
  28. package/dist/enrichment/utils/index.js +22 -0
  29. package/dist/enrichment/utils/personal-domains.d.ts +31 -0
  30. package/dist/enrichment/utils/personal-domains.js +95 -0
  31. package/dist/enrichment/utils/validation.d.ts +42 -0
  32. package/dist/enrichment/utils/validation.js +130 -0
  33. package/dist/enrichment/verification/index.d.ts +4 -0
  34. package/dist/enrichment/verification/index.js +8 -0
  35. package/dist/enrichment/verification/mx.d.ts +16 -0
  36. package/dist/enrichment/verification/mx.js +168 -0
  37. package/dist/http-client.d.ts +1 -1
  38. package/dist/http-client.js +146 -63
  39. package/dist/index.d.ts +17 -14
  40. package/dist/index.js +20 -1
  41. package/dist/linkedin-api.d.ts +97 -4
  42. package/dist/linkedin-api.js +416 -134
  43. package/dist/parsers/company-parser.d.ts +15 -1
  44. package/dist/parsers/company-parser.js +45 -17
  45. package/dist/parsers/profile-parser.d.ts +19 -1
  46. package/dist/parsers/profile-parser.js +131 -81
  47. package/dist/parsers/search-parser.d.ts +1 -1
  48. package/dist/parsers/search-parser.js +24 -11
  49. package/dist/utils/logger.d.ts +1 -1
  50. package/dist/utils/logger.js +28 -18
  51. package/dist/utils/search-encoder.d.ts +32 -1
  52. package/dist/utils/search-encoder.js +102 -58
  53. package/dist/utils/sentry.d.ts +1 -1
  54. package/dist/utils/sentry.js +56 -8
  55. package/package.json +1 -1
@@ -0,0 +1,107 @@
1
+ "use strict";
2
+ /**
3
+ * Email Construction Provider
4
+ *
5
+ * Generates email pattern candidates based on name + domain, then verifies via MX check.
6
+ * This is a FREE provider that doesn't require any API keys.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.createConstructProvider = createConstructProvider;
10
+ const mx_1 = require("../verification/mx");
11
+ const personal_domains_1 = require("../utils/personal-domains");
12
+ const validation_1 = require("../utils/validation");
13
+ /**
14
+ * Build all email pattern candidates for a person
15
+ */
16
+ function buildCandidates(input) {
17
+ const domain = String(input.domain || '').toLowerCase();
18
+ const first = (0, validation_1.cleanNamePart)(input.first || '');
19
+ const last = (0, validation_1.cleanNamePart)(input.last || '');
20
+ const fl = first ? first[0] : '';
21
+ const locals = [];
22
+ // Patterns with both first and last name
23
+ if (first && last) {
24
+ locals.push(`${first}.${last}`); // john.doe
25
+ locals.push(`${fl}.${last}`); // j.doe
26
+ locals.push(`${first}${last[0] ?? ''}`); // johnd
27
+ locals.push(`${fl}${last}`); // jdoe
28
+ locals.push(`${first}_${last}`); // john_doe
29
+ locals.push(`${last}.${first}`); // doe.john
30
+ }
31
+ // Single name patterns
32
+ if (first)
33
+ locals.push(first); // john
34
+ if (last)
35
+ locals.push(last); // doe
36
+ // Deduplicate while preserving order
37
+ const seen = new Set();
38
+ const emails = [];
39
+ for (const local of locals) {
40
+ if (!local)
41
+ continue;
42
+ const email = `${local}@${domain}`;
43
+ if (!seen.has(email)) {
44
+ seen.add(email);
45
+ emails.push(email);
46
+ }
47
+ }
48
+ return emails;
49
+ }
50
+ /**
51
+ * Extract name components from candidate
52
+ */
53
+ function extractNames(candidate) {
54
+ const firstName = candidate.firstName ||
55
+ candidate.first_name ||
56
+ candidate.first ||
57
+ candidate.name?.split(' ')?.[0] ||
58
+ '';
59
+ const lastName = candidate.lastName ||
60
+ candidate.last_name ||
61
+ candidate.last ||
62
+ candidate.name?.split(' ')?.slice(1).join(' ') ||
63
+ '';
64
+ return { first: firstName, last: lastName };
65
+ }
66
+ /**
67
+ * Extract domain from candidate
68
+ */
69
+ function extractDomain(candidate) {
70
+ return candidate.domain || candidate.companyDomain || candidate.company_domain || '';
71
+ }
72
+ /**
73
+ * Create the construct provider function
74
+ */
75
+ function createConstructProvider(config) {
76
+ const maxAttempts = config?.maxAttempts ?? 8;
77
+ const timeoutMs = config?.timeoutMs ?? 5000;
78
+ async function fetchEmail(candidate) {
79
+ const { first, last } = extractNames(candidate);
80
+ const domain = extractDomain(candidate);
81
+ // Skip if missing required fields
82
+ if (!first || !domain) {
83
+ return null;
84
+ }
85
+ // Skip personal domains
86
+ if ((0, personal_domains_1.isPersonalDomain)(domain)) {
87
+ return null;
88
+ }
89
+ const candidates = buildCandidates({ first, last, domain });
90
+ const max = Math.min(candidates.length, maxAttempts);
91
+ for (let i = 0; i < max; i++) {
92
+ const email = candidates[i];
93
+ const verification = await (0, mx_1.verifyEmailMx)(email, { timeoutMs });
94
+ if (verification.valid === true && verification.confidence >= 50) {
95
+ return {
96
+ email,
97
+ verified: true,
98
+ score: verification.confidence,
99
+ };
100
+ }
101
+ }
102
+ return null;
103
+ }
104
+ // Mark provider name for orchestrator
105
+ fetchEmail.__name = 'construct';
106
+ return fetchEmail;
107
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Dropcontact Provider
3
+ *
4
+ * Dropcontact API for email finding.
5
+ * This is a placeholder implementation - full implementation would call the Dropcontact API.
6
+ */
7
+ import type { EnrichmentCandidate, ProviderResult, DropcontactConfig } from '../types';
8
+ /**
9
+ * Create the Dropcontact provider function
10
+ *
11
+ * Note: This is a placeholder. Full implementation would:
12
+ * 1. POST to https://api.dropcontact.io/batch with contacts
13
+ * 2. Poll for results
14
+ * 3. Return enriched email with verification status
15
+ */
16
+ export declare function createDropcontactProvider(config: DropcontactConfig): (_candidate: EnrichmentCandidate) => Promise<ProviderResult | null>;
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ /**
3
+ * Dropcontact Provider
4
+ *
5
+ * Dropcontact API for email finding.
6
+ * This is a placeholder implementation - full implementation would call the Dropcontact API.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.createDropcontactProvider = createDropcontactProvider;
10
+ /**
11
+ * Create the Dropcontact provider function
12
+ *
13
+ * Note: This is a placeholder. Full implementation would:
14
+ * 1. POST to https://api.dropcontact.io/batch with contacts
15
+ * 2. Poll for results
16
+ * 3. Return enriched email with verification status
17
+ */
18
+ function createDropcontactProvider(config) {
19
+ const { apiKey } = config;
20
+ if (!apiKey) {
21
+ // Return a no-op provider if not configured
22
+ const noopProvider = async () => null;
23
+ noopProvider.__name = 'dropcontact';
24
+ return noopProvider;
25
+ }
26
+ async function fetchEmail(_candidate) {
27
+ // TODO: Implement Dropcontact API integration
28
+ // API: POST https://api.dropcontact.io/batch
29
+ // Headers: X-Access-Token: {apiKey}
30
+ // Body: { data: [{ first_name, last_name, company, website }], siren: false, language: "en" }
31
+ // For now, return null (skip this provider)
32
+ return null;
33
+ }
34
+ // Mark provider name for orchestrator
35
+ fetchEmail.__name = 'dropcontact';
36
+ return fetchEmail;
37
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Hunter.io Provider
3
+ *
4
+ * Hunter.io public API for email finding.
5
+ * Uses email-finder endpoint with name+domain, falls back to domain-search.
6
+ */
7
+ import type { EnrichmentCandidate, ProviderResult, HunterConfig } from "../types";
8
+ /**
9
+ * Create the Hunter provider function
10
+ */
11
+ export declare function createHunterProvider(config: HunterConfig): (candidate: EnrichmentCandidate) => Promise<ProviderResult | null>;
@@ -0,0 +1,162 @@
1
+ "use strict";
2
+ /**
3
+ * Hunter.io Provider
4
+ *
5
+ * Hunter.io public API for email finding.
6
+ * Uses email-finder endpoint with name+domain, falls back to domain-search.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.createHunterProvider = createHunterProvider;
10
+ const API_BASE = "https://api.hunter.io/v2";
11
+ /**
12
+ * Delay helper for retry logic
13
+ */
14
+ async function delay(ms) {
15
+ return new Promise((r) => setTimeout(r, ms));
16
+ }
17
+ /**
18
+ * Check if value is truthy
19
+ */
20
+ function truthy(v) {
21
+ return v !== undefined && v !== null && String(v).length > 0;
22
+ }
23
+ /**
24
+ * Map Hunter verification status to boolean
25
+ */
26
+ function mapVerified(status) {
27
+ if (!status)
28
+ return undefined;
29
+ const s = String(status).toLowerCase();
30
+ if (s === "valid" || s === "deliverable")
31
+ return true;
32
+ if (s === "invalid" || s === "undeliverable")
33
+ return false;
34
+ return undefined; // catch-all/unknown/webmail -> leave undefined
35
+ }
36
+ /**
37
+ * HTTP request with retry on 429 rate limit
38
+ */
39
+ async function requestWithRetry(url, retries = 1, backoffMs = 200) {
40
+ let lastErr;
41
+ for (let i = 0; i <= retries; i++) {
42
+ try {
43
+ const res = await fetch(url);
44
+ // Retry on rate limit
45
+ if (res?.status === 429 && i < retries) {
46
+ await delay(backoffMs * Math.pow(2, i));
47
+ continue;
48
+ }
49
+ if (!res || res.status >= 400) {
50
+ lastErr = new Error(`hunter_http_${res?.status ?? "error"}`);
51
+ break;
52
+ }
53
+ const json = (await res.json());
54
+ return json;
55
+ }
56
+ catch (err) {
57
+ lastErr = err;
58
+ if (i < retries) {
59
+ await delay(backoffMs * Math.pow(2, i));
60
+ continue;
61
+ }
62
+ }
63
+ }
64
+ throw lastErr ?? new Error("hunter_http_error");
65
+ }
66
+ /**
67
+ * Extract name and domain from candidate
68
+ */
69
+ function extractInputs(candidate) {
70
+ const name = candidate.name || candidate.fullName || candidate.full_name;
71
+ const first = candidate.first ||
72
+ candidate.first_name ||
73
+ candidate.firstName ||
74
+ name?.split?.(" ")?.[0];
75
+ const last = candidate.last ||
76
+ candidate.last_name ||
77
+ candidate.lastName ||
78
+ name?.split?.(" ")?.slice(1).join(" ") ||
79
+ undefined;
80
+ const domain = candidate.domain ||
81
+ candidate.companyDomain ||
82
+ candidate.company_domain ||
83
+ undefined;
84
+ return { first, last, domain };
85
+ }
86
+ /**
87
+ * Create the Hunter provider function
88
+ */
89
+ function createHunterProvider(config) {
90
+ const { apiKey } = config;
91
+ if (!apiKey) {
92
+ // Return a no-op provider if not configured
93
+ const noopProvider = async () => null;
94
+ noopProvider.__name = "hunter";
95
+ return noopProvider;
96
+ }
97
+ async function fetchEmail(candidate) {
98
+ const { first, last, domain } = extractInputs(candidate);
99
+ let url = null;
100
+ // Use email-finder if we have name components
101
+ if (truthy(first) && truthy(last) && truthy(domain)) {
102
+ const qs = new URLSearchParams({
103
+ api_key: String(apiKey),
104
+ domain: String(domain),
105
+ first_name: String(first),
106
+ last_name: String(last),
107
+ });
108
+ url = `${API_BASE}/email-finder?${qs.toString()}`;
109
+ }
110
+ // Fall back to domain-search if only domain available
111
+ else if (truthy(domain)) {
112
+ const qs = new URLSearchParams({
113
+ api_key: String(apiKey),
114
+ domain: String(domain),
115
+ limit: "1",
116
+ });
117
+ url = `${API_BASE}/domain-search?${qs.toString()}`;
118
+ }
119
+ else {
120
+ return null; // Can't search without domain
121
+ }
122
+ try {
123
+ const json = await requestWithRetry(url, 1, 100);
124
+ // Parse email-finder response shape
125
+ const ef = (json && (json.data || json.result));
126
+ if (ef && (ef.email || ef.score !== undefined)) {
127
+ const email = ef.email ?? null;
128
+ const score = typeof ef.score === "number"
129
+ ? ef.score
130
+ : Number(ef.score ?? 0) || undefined;
131
+ const verified = mapVerified(ef?.verification?.status ?? ef?.status);
132
+ if (!email)
133
+ return null;
134
+ return { email, verified, score };
135
+ }
136
+ // Parse domain-search response shape
137
+ const ds = (json &&
138
+ json.data &&
139
+ Array.isArray(json.data.emails)
140
+ ? json.data.emails
141
+ : null);
142
+ if (ds && ds.length > 0) {
143
+ const firstHit = ds[0];
144
+ const email = firstHit?.value || firstHit?.email || null;
145
+ const score = typeof firstHit?.confidence === "number"
146
+ ? firstHit.confidence
147
+ : Number(firstHit?.confidence ?? 0) || undefined;
148
+ const verified = mapVerified(firstHit?.verification?.status ?? firstHit?.status);
149
+ if (!email)
150
+ return null;
151
+ return { email, verified, score };
152
+ }
153
+ return null;
154
+ }
155
+ catch {
156
+ return null;
157
+ }
158
+ }
159
+ // Mark provider name for orchestrator
160
+ fetchEmail.__name = "hunter";
161
+ return fetchEmail;
162
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Email Enrichment Providers
3
+ */
4
+ export { createConstructProvider } from './construct';
5
+ export { createLddProvider } from './ldd';
6
+ export { createSmartProspectProvider } from './smartprospect';
7
+ export { createHunterProvider } from './hunter';
8
+ export { createApolloProvider } from './apollo';
9
+ export { createDropcontactProvider } from './dropcontact';
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ /**
3
+ * Email Enrichment Providers
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.createDropcontactProvider = exports.createApolloProvider = exports.createHunterProvider = exports.createSmartProspectProvider = exports.createLddProvider = exports.createConstructProvider = void 0;
7
+ var construct_1 = require("./construct");
8
+ Object.defineProperty(exports, "createConstructProvider", { enumerable: true, get: function () { return construct_1.createConstructProvider; } });
9
+ var ldd_1 = require("./ldd");
10
+ Object.defineProperty(exports, "createLddProvider", { enumerable: true, get: function () { return ldd_1.createLddProvider; } });
11
+ var smartprospect_1 = require("./smartprospect");
12
+ Object.defineProperty(exports, "createSmartProspectProvider", { enumerable: true, get: function () { return smartprospect_1.createSmartProspectProvider; } });
13
+ var hunter_1 = require("./hunter");
14
+ Object.defineProperty(exports, "createHunterProvider", { enumerable: true, get: function () { return hunter_1.createHunterProvider; } });
15
+ var apollo_1 = require("./apollo");
16
+ Object.defineProperty(exports, "createApolloProvider", { enumerable: true, get: function () { return apollo_1.createApolloProvider; } });
17
+ var dropcontact_1 = require("./dropcontact");
18
+ Object.defineProperty(exports, "createDropcontactProvider", { enumerable: true, get: function () { return dropcontact_1.createDropcontactProvider; } });
@@ -0,0 +1,11 @@
1
+ /**
2
+ * LinkedIn Data Dump (LDD) Provider
3
+ *
4
+ * YOUR private database of ~500M scraped LinkedIn profiles with emails.
5
+ * This is FREE and unlimited - it's your own service.
6
+ */
7
+ import type { EnrichmentCandidate, ProviderResult, LddConfig } from "../types";
8
+ /**
9
+ * Create the LDD provider function
10
+ */
11
+ export declare function createLddProvider(config: LddConfig): (candidate: EnrichmentCandidate) => Promise<ProviderResult | null>;
@@ -0,0 +1,110 @@
1
+ "use strict";
2
+ /**
3
+ * LinkedIn Data Dump (LDD) Provider
4
+ *
5
+ * YOUR private database of ~500M scraped LinkedIn profiles with emails.
6
+ * This is FREE and unlimited - it's your own service.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.createLddProvider = createLddProvider;
10
+ const validation_1 = require("../utils/validation");
11
+ /**
12
+ * Delay helper for retry logic
13
+ */
14
+ async function delay(ms) {
15
+ return new Promise((r) => setTimeout(r, ms));
16
+ }
17
+ /**
18
+ * HTTP request with retry on 429 rate limit
19
+ */
20
+ async function requestWithRetry(url, token, retries = 1, backoffMs = 200) {
21
+ let lastErr;
22
+ for (let i = 0; i <= retries; i++) {
23
+ try {
24
+ const res = await fetch(url, {
25
+ method: "GET",
26
+ headers: {
27
+ Authorization: `Bearer ${token}`,
28
+ "Content-Type": "application/json",
29
+ },
30
+ });
31
+ // Retry on rate limit
32
+ if (res?.status === 429 && i < retries) {
33
+ await delay(backoffMs * Math.pow(2, i));
34
+ continue;
35
+ }
36
+ return res;
37
+ }
38
+ catch (err) {
39
+ lastErr = err;
40
+ if (i < retries) {
41
+ await delay(backoffMs * Math.pow(2, i));
42
+ continue;
43
+ }
44
+ }
45
+ }
46
+ throw lastErr ?? new Error("ldd_http_error");
47
+ }
48
+ /**
49
+ * Extract LinkedIn username from candidate
50
+ */
51
+ function extractUsername(candidate) {
52
+ // Direct username
53
+ const directUsername = candidate.linkedinUsername || candidate.linkedin_username;
54
+ if (directUsername)
55
+ return directUsername;
56
+ // Extract from URL
57
+ const url = candidate.linkedinUrl || candidate.linkedin_url;
58
+ if (url) {
59
+ const extracted = (0, validation_1.extractLinkedInUsername)(url);
60
+ if (extracted)
61
+ return extracted;
62
+ }
63
+ return null;
64
+ }
65
+ /**
66
+ * Create the LDD provider function
67
+ */
68
+ function createLddProvider(config) {
69
+ const { apiUrl, apiToken } = config;
70
+ if (!apiUrl || !apiToken) {
71
+ // Return a no-op provider if not configured
72
+ const noopProvider = async () => null;
73
+ noopProvider.__name = "ldd";
74
+ return noopProvider;
75
+ }
76
+ async function fetchEmail(candidate) {
77
+ const username = extractUsername(candidate);
78
+ if (!username) {
79
+ return null;
80
+ }
81
+ try {
82
+ const endpoint = `${apiUrl}/api/v1/profiles/by-username/${encodeURIComponent(username)}`;
83
+ const response = await requestWithRetry(endpoint, apiToken, 1, 100);
84
+ if (!response.ok) {
85
+ return null;
86
+ }
87
+ const data = (await response.json());
88
+ if (!data.success || !data.data) {
89
+ return null;
90
+ }
91
+ // Find first valid email
92
+ const emails = data.data.emails || [];
93
+ const validEmail = emails.find((e) => e.email_address && e.email_address.includes("@"));
94
+ if (!validEmail) {
95
+ return null;
96
+ }
97
+ return {
98
+ email: validEmail.email_address,
99
+ verified: true, // LDD data is pre-verified
100
+ score: 90, // High confidence from your own database
101
+ };
102
+ }
103
+ catch {
104
+ return null;
105
+ }
106
+ }
107
+ // Mark provider name for orchestrator
108
+ fetchEmail.__name = "ldd";
109
+ return fetchEmail;
110
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * SmartProspect/Smartlead Provider
3
+ *
4
+ * Smartlead's prospect database - a REVERSE-ENGINEERED private API.
5
+ * Two-phase process: search (free) then fetch (costs credits).
6
+ */
7
+ import type { EnrichmentCandidate, ProviderResult, SmartProspectConfig } from '../types';
8
+ /**
9
+ * Create the SmartProspect provider function
10
+ */
11
+ export declare function createSmartProspectProvider(config: SmartProspectConfig): (candidate: EnrichmentCandidate) => Promise<ProviderResult | null>;