linkedin-secret-sauce 0.12.1 → 0.12.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +339 -31
- package/dist/cosiall-client.d.ts +1 -1
- package/dist/cosiall-client.js +1 -1
- package/dist/enrichment/index.d.ts +23 -2
- package/dist/enrichment/index.js +38 -22
- package/dist/enrichment/matching.d.ts +16 -2
- package/dist/enrichment/matching.js +387 -65
- package/dist/enrichment/providers/bounceban.d.ts +82 -0
- package/dist/enrichment/providers/bounceban.js +447 -0
- package/dist/enrichment/providers/bouncer.d.ts +1 -1
- package/dist/enrichment/providers/bouncer.js +19 -21
- package/dist/enrichment/providers/construct.d.ts +1 -1
- package/dist/enrichment/providers/construct.js +22 -38
- package/dist/enrichment/providers/cosiall.d.ts +1 -1
- package/dist/enrichment/providers/cosiall.js +3 -4
- package/dist/enrichment/providers/dropcontact.d.ts +15 -9
- package/dist/enrichment/providers/dropcontact.js +188 -19
- package/dist/enrichment/providers/hunter.d.ts +8 -1
- package/dist/enrichment/providers/hunter.js +52 -28
- package/dist/enrichment/providers/index.d.ts +2 -0
- package/dist/enrichment/providers/index.js +10 -1
- package/dist/enrichment/providers/ldd.d.ts +1 -10
- package/dist/enrichment/providers/ldd.js +20 -97
- package/dist/enrichment/providers/smartprospect.js +28 -48
- package/dist/enrichment/providers/snovio.d.ts +1 -1
- package/dist/enrichment/providers/snovio.js +29 -31
- package/dist/enrichment/providers/trykitt.d.ts +63 -0
- package/dist/enrichment/providers/trykitt.js +210 -0
- package/dist/enrichment/types.d.ts +220 -7
- package/dist/enrichment/types.js +16 -8
- package/dist/enrichment/utils/candidate-parser.d.ts +107 -0
- package/dist/enrichment/utils/candidate-parser.js +173 -0
- package/dist/enrichment/utils/noop-provider.d.ts +39 -0
- package/dist/enrichment/utils/noop-provider.js +37 -0
- package/dist/enrichment/utils/rate-limiter.d.ts +103 -0
- package/dist/enrichment/utils/rate-limiter.js +204 -0
- package/dist/enrichment/utils/validation.d.ts +75 -3
- package/dist/enrichment/utils/validation.js +164 -11
- package/dist/linkedin-api.d.ts +40 -1
- package/dist/linkedin-api.js +160 -27
- package/dist/types.d.ts +50 -1
- package/dist/utils/lru-cache.d.ts +105 -0
- package/dist/utils/lru-cache.js +175 -0
- package/docs/COSIALL_PROFILE_EMAILS.md +342 -0
- package/docs/ENRICHMENT.md +622 -0
- package/docs/INTEGRATION.md +405 -0
- package/docs/PLAYGROUND.md +558 -0
- package/docs/SALES_SEARCH.md +171 -0
- package/docs/api/.nojekyll +1 -0
- package/docs/api/assets/hierarchy.js +1 -0
- package/docs/api/assets/highlight.css +92 -0
- package/docs/api/assets/icons.js +18 -0
- package/docs/api/assets/icons.svg +1 -0
- package/docs/api/assets/main.js +60 -0
- package/docs/api/assets/navigation.js +1 -0
- package/docs/api/assets/search.js +1 -0
- package/docs/api/assets/style.css +1633 -0
- package/docs/api/classes/LinkedInClientError.html +37 -0
- package/docs/api/functions/_testGetAccountCookies.html +4 -0
- package/docs/api/functions/_testGetAccountEntry.html +4 -0
- package/docs/api/functions/_testGetAllAccountIds.html +3 -0
- package/docs/api/functions/_testGetPoolState.html +3 -0
- package/docs/api/functions/adminResetAccount.html +1 -0
- package/docs/api/functions/adminSetCooldown.html +1 -0
- package/docs/api/functions/buildCookieHeader.html +1 -0
- package/docs/api/functions/clearAllSmartLeadTokens.html +2 -0
- package/docs/api/functions/clearRequestHistory.html +1 -0
- package/docs/api/functions/clearSessionAccount.html +1 -0
- package/docs/api/functions/clearSmartLeadToken.html +2 -0
- package/docs/api/functions/createEnrichmentClient.html +8 -0
- package/docs/api/functions/extractCsrfToken.html +1 -0
- package/docs/api/functions/extractLinkedInHandle.html +7 -0
- package/docs/api/functions/fetchCookiesFromCosiall.html +14 -0
- package/docs/api/functions/fetchProfileEmailsFromCosiall.html +18 -0
- package/docs/api/functions/forceRefreshCookies.html +1 -0
- package/docs/api/functions/getAccountForSession.html +1 -0
- package/docs/api/functions/getAccountsSummary.html +1 -0
- package/docs/api/functions/getCompaniesBatch.html +5 -0
- package/docs/api/functions/getCompanyById.html +9 -0
- package/docs/api/functions/getCompanyByUrl.html +1 -0
- package/docs/api/functions/getConfig.html +1 -0
- package/docs/api/functions/getCookiePoolHealth.html +1 -0
- package/docs/api/functions/getProfileByUrn.html +17 -0
- package/docs/api/functions/getProfileByVanity.html +10 -0
- package/docs/api/functions/getProfilesBatch.html +1 -0
- package/docs/api/functions/getRequestHistory.html +1 -0
- package/docs/api/functions/getSalesNavigatorProfileDetails.html +1 -0
- package/docs/api/functions/getSalesNavigatorProfileFull.html +16 -0
- package/docs/api/functions/getSmartLeadToken.html +1 -0
- package/docs/api/functions/getSmartLeadTokenCacheStats.html +2 -0
- package/docs/api/functions/getSmartLeadUser.html +2 -0
- package/docs/api/functions/getSnapshot.html +1 -0
- package/docs/api/functions/getYearsAtCompanyOptions.html +2 -0
- package/docs/api/functions/getYearsInPositionOptions.html +2 -0
- package/docs/api/functions/getYearsOfExperienceOptions.html +2 -0
- package/docs/api/functions/incrementMetric.html +1 -0
- package/docs/api/functions/initializeCookiePool.html +1 -0
- package/docs/api/functions/initializeLinkedInClient.html +1 -0
- package/docs/api/functions/isBusinessEmail.html +4 -0
- package/docs/api/functions/isDisposableDomain.html +4 -0
- package/docs/api/functions/isDisposableEmail.html +4 -0
- package/docs/api/functions/isPersonalDomain.html +4 -0
- package/docs/api/functions/isPersonalEmail.html +4 -0
- package/docs/api/functions/isRoleAccount.html +4 -0
- package/docs/api/functions/isValidEmailSyntax.html +4 -0
- package/docs/api/functions/parseFullProfile.html +15 -0
- package/docs/api/functions/parseSalesSearchResults.html +1 -0
- package/docs/api/functions/reportAccountFailure.html +1 -0
- package/docs/api/functions/reportAccountSuccess.html +1 -0
- package/docs/api/functions/resolveCompanyUniversalName.html +1 -0
- package/docs/api/functions/searchSalesLeads.html +16 -0
- package/docs/api/functions/selectAccountForRequest.html +1 -0
- package/docs/api/functions/setAccountForSession.html +1 -0
- package/docs/api/functions/typeahead.html +1 -0
- package/docs/api/functions/verifyEmailMx.html +1 -0
- package/docs/api/hierarchy.html +1 -0
- package/docs/api/index.html +12 -0
- package/docs/api/interfaces/AccountCookies.html +4 -0
- package/docs/api/interfaces/BatchEnrichmentOptions.html +14 -0
- package/docs/api/interfaces/CacheAdapter.html +6 -0
- package/docs/api/interfaces/CanonicalEmail.html +14 -0
- package/docs/api/interfaces/Company.html +17 -0
- package/docs/api/interfaces/ConstructConfig.html +8 -0
- package/docs/api/interfaces/CosiallProfileEmailsResponse.html +11 -0
- package/docs/api/interfaces/DropcontactConfig.html +3 -0
- package/docs/api/interfaces/EnrichmentCandidate.html +34 -0
- package/docs/api/interfaces/EnrichmentClient.html +10 -0
- package/docs/api/interfaces/EnrichmentClientConfig.html +12 -0
- package/docs/api/interfaces/EnrichmentLogger.html +6 -0
- package/docs/api/interfaces/EnrichmentOptions.html +10 -0
- package/docs/api/interfaces/HunterConfig.html +3 -0
- package/docs/api/interfaces/LddConfig.html +4 -0
- package/docs/api/interfaces/LddProfileData.html +6 -0
- package/docs/api/interfaces/LinkedInClientConfig.html +20 -0
- package/docs/api/interfaces/LinkedInCookie.html +9 -0
- package/docs/api/interfaces/LinkedInPosition.html +14 -0
- package/docs/api/interfaces/LinkedInProfile.html +21 -0
- package/docs/api/interfaces/LinkedInSpotlightBadge.html +5 -0
- package/docs/api/interfaces/LinkedInTenure.html +3 -0
- package/docs/api/interfaces/Metrics.html +22 -0
- package/docs/api/interfaces/MetricsSnapshot.html +23 -0
- package/docs/api/interfaces/ProfileEducation.html +8 -0
- package/docs/api/interfaces/ProfileEmailsLookupOptions.html +9 -0
- package/docs/api/interfaces/ProfilePosition.html +12 -0
- package/docs/api/interfaces/ProfileSkill.html +3 -0
- package/docs/api/interfaces/ProviderResult.html +11 -0
- package/docs/api/interfaces/ProvidersConfig.html +17 -0
- package/docs/api/interfaces/RequestHistoryEntry.html +8 -0
- package/docs/api/interfaces/SalesLeadSearchResult.html +31 -0
- package/docs/api/interfaces/SalesNavigatorContactInfo.html +5 -0
- package/docs/api/interfaces/SalesNavigatorPosition.html +11 -0
- package/docs/api/interfaces/SalesNavigatorProfile.html +9 -0
- package/docs/api/interfaces/SalesNavigatorProfileFull.html +24 -0
- package/docs/api/interfaces/SearchSalesResult.html +5 -0
- package/docs/api/interfaces/SmartLeadAuthConfig.html +6 -0
- package/docs/api/interfaces/SmartLeadCredentials.html +3 -0
- package/docs/api/interfaces/SmartLeadLoginResponse.html +3 -0
- package/docs/api/interfaces/SmartLeadUser.html +8 -0
- package/docs/api/interfaces/SmartProspectConfig.html +19 -0
- package/docs/api/interfaces/SmartProspectContact.html +24 -0
- package/docs/api/interfaces/SmartProspectSearchFilters.html +42 -0
- package/docs/api/interfaces/TypeaheadItem.html +3 -0
- package/docs/api/interfaces/TypeaheadResult.html +3 -0
- package/docs/api/interfaces/VerificationResult.html +16 -0
- package/docs/api/types/CostCallback.html +2 -0
- package/docs/api/types/Geo.html +4 -0
- package/docs/api/types/LddApiResponse.html +1 -0
- package/docs/api/types/ProviderFunc.html +2 -0
- package/docs/api/types/ProviderName.html +2 -0
- package/docs/api/types/SalesSearchFilters.html +7 -0
- package/docs/api/types/TypeaheadType.html +1 -0
- package/docs/api/variables/COMPANY_SIZE_OPTIONS.html +1 -0
- package/docs/api/variables/DEFAULT_PROVIDER_ORDER.html +20 -0
- package/docs/api/variables/DISPOSABLE_DOMAINS.html +2 -0
- package/docs/api/variables/FUNCTION_OPTIONS.html +1 -0
- package/docs/api/variables/INDUSTRY_OPTIONS.html +1 -0
- package/docs/api/variables/LANGUAGE_OPTIONS.html +1 -0
- package/docs/api/variables/PERSONAL_DOMAINS.html +2 -0
- package/docs/api/variables/PROVIDER_COSTS.html +15 -0
- package/docs/api/variables/REGION_OPTIONS.html +1 -0
- package/docs/api/variables/SENIORITY_OPTIONS.html +3 -0
- package/docs/api/variables/YEARS_OPTIONS.html +1 -0
- package/docs/index.html +98 -0
- package/package.json +16 -5
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* TryKitt.ai Email Finder Provider
|
|
4
|
+
*
|
|
5
|
+
* AI-powered email finding with enterprise identity server verification.
|
|
6
|
+
* FREE for individuals with unlimited searches.
|
|
7
|
+
*
|
|
8
|
+
* Features:
|
|
9
|
+
* - Email finding by name + domain
|
|
10
|
+
* - Enterprise identity server catch-all verification (more accurate than SMTP)
|
|
11
|
+
* - <0.1% bounce rate claimed
|
|
12
|
+
* - 2-5x faster than traditional SMTP verification
|
|
13
|
+
*
|
|
14
|
+
* API Endpoints:
|
|
15
|
+
* - POST /job/find-email - Find email by name + domain
|
|
16
|
+
* - POST /job/verify-email - Verify existing email
|
|
17
|
+
*
|
|
18
|
+
* Rate Limits (Free tier):
|
|
19
|
+
* - 2 requests/second
|
|
20
|
+
* - 120 requests/minute
|
|
21
|
+
* - Unlimited monthly quota
|
|
22
|
+
*
|
|
23
|
+
* @see https://trykitt.ai
|
|
24
|
+
*/
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.createTryKittProvider = createTryKittProvider;
|
|
27
|
+
exports.findEmailWithTryKitt = findEmailWithTryKitt;
|
|
28
|
+
exports.verifyEmailWithTryKitt = verifyEmailWithTryKitt;
|
|
29
|
+
const http_retry_1 = require("../utils/http-retry");
|
|
30
|
+
const candidate_parser_1 = require("../utils/candidate-parser");
|
|
31
|
+
const noop_provider_1 = require("../utils/noop-provider");
|
|
32
|
+
const DEFAULT_API_URL = "https://api.trykitt.ai";
|
|
33
|
+
const DEFAULT_TIMEOUT_MS = 30000;
|
|
34
|
+
/**
|
|
35
|
+
* Extract TryKitt-specific inputs from candidate using shared parser
|
|
36
|
+
*/
|
|
37
|
+
function extractTryKittInputs(candidate) {
|
|
38
|
+
const name = (0, candidate_parser_1.extractName)(candidate);
|
|
39
|
+
const { domain } = (0, candidate_parser_1.extractCompany)(candidate);
|
|
40
|
+
const linkedin = (0, candidate_parser_1.extractLinkedIn)(candidate);
|
|
41
|
+
// Build LinkedIn URL from username if URL not available
|
|
42
|
+
const linkedinUrl = linkedin.url ||
|
|
43
|
+
(linkedin.username ? (0, candidate_parser_1.buildLinkedInUrl)(linkedin.username) : null);
|
|
44
|
+
return {
|
|
45
|
+
fullName: name.fullName || null,
|
|
46
|
+
firstName: name.firstName || null,
|
|
47
|
+
lastName: name.lastName || null,
|
|
48
|
+
domain,
|
|
49
|
+
linkedinUrl,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Map TryKitt confidence to our 0-100 scale
|
|
54
|
+
* TryKitt returns confidence as 0-1 decimal OR 0-100 percentage
|
|
55
|
+
*/
|
|
56
|
+
function normalizeConfidence(confidence) {
|
|
57
|
+
// If confidence is <= 1, treat as decimal and convert to percentage
|
|
58
|
+
if (confidence <= 1) {
|
|
59
|
+
return Math.round(confidence * 100);
|
|
60
|
+
}
|
|
61
|
+
// Already a percentage
|
|
62
|
+
return Math.round(confidence);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Create the TryKitt.ai email finder provider
|
|
66
|
+
*
|
|
67
|
+
* This provider uses TryKitt's AI-powered email finding with enterprise
|
|
68
|
+
* identity server verification for catch-all domains.
|
|
69
|
+
*/
|
|
70
|
+
function createTryKittProvider(config) {
|
|
71
|
+
const { apiKey, apiUrl = DEFAULT_API_URL, timeoutMs = DEFAULT_TIMEOUT_MS, } = config;
|
|
72
|
+
if (!apiKey) {
|
|
73
|
+
return (0, noop_provider_1.createNoOpProvider)("trykitt");
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Find email for a candidate
|
|
77
|
+
*/
|
|
78
|
+
async function findEmail(candidate) {
|
|
79
|
+
const { fullName, domain, linkedinUrl } = extractTryKittInputs(candidate);
|
|
80
|
+
// Need at least name and domain to search
|
|
81
|
+
if (!(0, http_retry_1.truthy)(fullName) || !(0, http_retry_1.truthy)(domain)) {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
try {
|
|
85
|
+
// Build request body
|
|
86
|
+
const body = {
|
|
87
|
+
full_name: fullName,
|
|
88
|
+
domain: domain,
|
|
89
|
+
};
|
|
90
|
+
// Add LinkedIn URL if available (improves accuracy)
|
|
91
|
+
if (linkedinUrl) {
|
|
92
|
+
body.linkedin_url = linkedinUrl;
|
|
93
|
+
}
|
|
94
|
+
const response = await (0, http_retry_1.postWithRetry)(`${apiUrl}/job/find-email`, body, {
|
|
95
|
+
Authorization: `Bearer ${apiKey}`,
|
|
96
|
+
}, {
|
|
97
|
+
retries: 2,
|
|
98
|
+
backoffMs: 500,
|
|
99
|
+
timeoutMs,
|
|
100
|
+
retryOnStatus: [429, 500, 502, 503, 504],
|
|
101
|
+
});
|
|
102
|
+
// Check if job completed successfully
|
|
103
|
+
if (response.status !== "completed") {
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
// Check if email was found
|
|
107
|
+
if (!response.result || !response.result.email) {
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
const result = response.result;
|
|
111
|
+
// Map confidence score
|
|
112
|
+
const confidence = normalizeConfidence(result.confidence_score ?? result.confidence ?? 0);
|
|
113
|
+
// Determine verification status
|
|
114
|
+
// TryKitt verifies emails, so if confidence is high, consider verified
|
|
115
|
+
const verified = confidence >= 80 &&
|
|
116
|
+
result.verification_type !== "unverified" &&
|
|
117
|
+
!result.is_catchall;
|
|
118
|
+
return {
|
|
119
|
+
email: result.email,
|
|
120
|
+
verified,
|
|
121
|
+
score: confidence,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
// Silently fail - let other providers try
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
// Mark provider name for orchestrator
|
|
130
|
+
findEmail.__name = "trykitt";
|
|
131
|
+
return findEmail;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Standalone function to find email via TryKitt.ai
|
|
135
|
+
*
|
|
136
|
+
* @example
|
|
137
|
+
* ```typescript
|
|
138
|
+
* const result = await findEmailWithTryKitt('John Doe', 'example.com', {
|
|
139
|
+
* apiKey: process.env.TRYKITT_API_KEY,
|
|
140
|
+
* });
|
|
141
|
+
* console.log(result?.email); // john.doe@example.com
|
|
142
|
+
* ```
|
|
143
|
+
*/
|
|
144
|
+
async function findEmailWithTryKitt(fullName, domain, config, linkedinUrl) {
|
|
145
|
+
const { apiKey, apiUrl = DEFAULT_API_URL, timeoutMs = DEFAULT_TIMEOUT_MS, } = config;
|
|
146
|
+
if (!apiKey) {
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
try {
|
|
150
|
+
const body = {
|
|
151
|
+
full_name: fullName,
|
|
152
|
+
domain: domain,
|
|
153
|
+
};
|
|
154
|
+
if (linkedinUrl) {
|
|
155
|
+
body.linkedin_url = linkedinUrl;
|
|
156
|
+
}
|
|
157
|
+
const response = await (0, http_retry_1.postWithRetry)(`${apiUrl}/job/find-email`, body, {
|
|
158
|
+
Authorization: `Bearer ${apiKey}`,
|
|
159
|
+
}, {
|
|
160
|
+
retries: 2,
|
|
161
|
+
backoffMs: 500,
|
|
162
|
+
timeoutMs,
|
|
163
|
+
});
|
|
164
|
+
return response;
|
|
165
|
+
}
|
|
166
|
+
catch {
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Verify an email address via TryKitt.ai
|
|
172
|
+
*
|
|
173
|
+
* @example
|
|
174
|
+
* ```typescript
|
|
175
|
+
* const result = await verifyEmailWithTryKitt('john@example.com', {
|
|
176
|
+
* apiKey: process.env.TRYKITT_API_KEY,
|
|
177
|
+
* });
|
|
178
|
+
* console.log(result?.result?.valid); // true
|
|
179
|
+
* console.log(result?.result?.is_catchall); // false
|
|
180
|
+
* ```
|
|
181
|
+
*/
|
|
182
|
+
async function verifyEmailWithTryKitt(email, config) {
|
|
183
|
+
const { apiKey, apiUrl = DEFAULT_API_URL, timeoutMs = DEFAULT_TIMEOUT_MS, } = config;
|
|
184
|
+
if (!apiKey) {
|
|
185
|
+
return null;
|
|
186
|
+
}
|
|
187
|
+
try {
|
|
188
|
+
const response = await (0, http_retry_1.postWithRetry)(`${apiUrl}/job/verify-email`, { email }, {
|
|
189
|
+
Authorization: `Bearer ${apiKey}`,
|
|
190
|
+
}, {
|
|
191
|
+
retries: 2,
|
|
192
|
+
backoffMs: 500,
|
|
193
|
+
timeoutMs,
|
|
194
|
+
});
|
|
195
|
+
if (response.status !== "completed" || !response.result) {
|
|
196
|
+
return null;
|
|
197
|
+
}
|
|
198
|
+
return {
|
|
199
|
+
valid: response.result.valid,
|
|
200
|
+
deliverable: response.result.deliverable,
|
|
201
|
+
confidence: normalizeConfidence(response.result.confidence),
|
|
202
|
+
isCatchAll: response.result.is_catchall ?? false,
|
|
203
|
+
isDisposable: response.result.is_disposable ?? false,
|
|
204
|
+
isRoleAccount: response.result.is_role_account ?? false,
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
catch {
|
|
208
|
+
return null;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
@@ -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
|
/**
|
|
@@ -112,6 +122,9 @@ export interface EnrichmentCandidate {
|
|
|
112
122
|
linkedin_url?: string;
|
|
113
123
|
linkedinId?: string;
|
|
114
124
|
linkedin_id?: string;
|
|
125
|
+
/** LinkedIn handle/vanity (e.g., "john-doe" from linkedin.com/in/john-doe) - best for Hunter.io */
|
|
126
|
+
linkedinHandle?: string;
|
|
127
|
+
linkedin_handle?: string;
|
|
115
128
|
numericLinkedInId?: string;
|
|
116
129
|
numeric_linkedin_id?: string;
|
|
117
130
|
objectUrn?: string;
|
|
@@ -160,6 +173,197 @@ export interface LddConfig {
|
|
|
160
173
|
export interface DropcontactConfig {
|
|
161
174
|
apiKey: string;
|
|
162
175
|
}
|
|
176
|
+
/**
|
|
177
|
+
* TryKitt.ai provider configuration
|
|
178
|
+
*
|
|
179
|
+
* TryKitt.ai provides AI-powered email finding with enterprise identity server
|
|
180
|
+
* verification for catch-all domains. FREE for individuals, unlimited searches.
|
|
181
|
+
*
|
|
182
|
+
* Features:
|
|
183
|
+
* - Email finding by name + domain
|
|
184
|
+
* - Enterprise identity server catch-all verification
|
|
185
|
+
* - <0.1% bounce rate claimed
|
|
186
|
+
* - 2-5x faster than traditional SMTP verification
|
|
187
|
+
*
|
|
188
|
+
* @see https://trykitt.ai
|
|
189
|
+
*/
|
|
190
|
+
export interface TryKittConfig {
|
|
191
|
+
/** TryKitt API key (Bearer token) */
|
|
192
|
+
apiKey: string;
|
|
193
|
+
/** API URL override (default: https://api.trykitt.ai) */
|
|
194
|
+
apiUrl?: string;
|
|
195
|
+
/** Timeout in ms (default: 30000) */
|
|
196
|
+
timeoutMs?: number;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* TryKitt.ai find email response result
|
|
200
|
+
*/
|
|
201
|
+
export interface TryKittFindEmailResult {
|
|
202
|
+
/** Found email address */
|
|
203
|
+
email: string;
|
|
204
|
+
/** First name */
|
|
205
|
+
first_name?: string;
|
|
206
|
+
/** Last name */
|
|
207
|
+
last_name?: string;
|
|
208
|
+
/** Domain searched */
|
|
209
|
+
domain: string;
|
|
210
|
+
/** Confidence score (0-1 decimal or 0-100 percentage) */
|
|
211
|
+
confidence: number;
|
|
212
|
+
/** Confidence as percentage (0-100) */
|
|
213
|
+
confidence_score?: number;
|
|
214
|
+
/** Sources used to find email */
|
|
215
|
+
sources?: string[];
|
|
216
|
+
/** Verification type: "smtp_verified", "enterprise_identity_server", etc. */
|
|
217
|
+
verification_type?: string;
|
|
218
|
+
/** Whether domain is catch-all */
|
|
219
|
+
is_catchall?: boolean;
|
|
220
|
+
/** Whether email is a role account (info@, support@) */
|
|
221
|
+
is_role_account?: boolean;
|
|
222
|
+
/** Company name if found */
|
|
223
|
+
company?: string;
|
|
224
|
+
/** Job title if found */
|
|
225
|
+
title?: string;
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* TryKitt.ai API response for find-email endpoint
|
|
229
|
+
*/
|
|
230
|
+
export interface TryKittFindEmailResponse {
|
|
231
|
+
/** Job ID */
|
|
232
|
+
id: string;
|
|
233
|
+
/** Status: "completed", "pending", "failed" */
|
|
234
|
+
status: string;
|
|
235
|
+
/** When job was created */
|
|
236
|
+
created_at: string;
|
|
237
|
+
/** When job completed */
|
|
238
|
+
completed_at?: string;
|
|
239
|
+
/** Result data (null if not found) */
|
|
240
|
+
result: TryKittFindEmailResult | null;
|
|
241
|
+
/** Error message if failed */
|
|
242
|
+
error?: string;
|
|
243
|
+
/** Error code */
|
|
244
|
+
error_code?: string;
|
|
245
|
+
/** Custom data passed in request */
|
|
246
|
+
custom_data?: Record<string, unknown>;
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* TryKitt.ai verify email response result
|
|
250
|
+
*/
|
|
251
|
+
export interface TryKittVerifyEmailResult {
|
|
252
|
+
/** Email verified */
|
|
253
|
+
email: string;
|
|
254
|
+
/** Whether email is valid */
|
|
255
|
+
valid: boolean;
|
|
256
|
+
/** Whether email is deliverable */
|
|
257
|
+
deliverable: boolean;
|
|
258
|
+
/** Confidence score (0-1) */
|
|
259
|
+
confidence: number;
|
|
260
|
+
/** Verification type used */
|
|
261
|
+
verification_type?: string;
|
|
262
|
+
/** Whether domain is catch-all */
|
|
263
|
+
is_catchall?: boolean;
|
|
264
|
+
/** Whether email is disposable */
|
|
265
|
+
is_disposable?: boolean;
|
|
266
|
+
/** Whether email is a role account */
|
|
267
|
+
is_role_account?: boolean;
|
|
268
|
+
/** Whether email is from free provider (Gmail, Yahoo) */
|
|
269
|
+
is_free_email?: boolean;
|
|
270
|
+
/** SMTP status */
|
|
271
|
+
smtp_status?: string;
|
|
272
|
+
/** Bounce type: "none", "soft", "hard" */
|
|
273
|
+
bounce_type?: string;
|
|
274
|
+
/** Suggested correction for typos */
|
|
275
|
+
suggestion?: string;
|
|
276
|
+
/** Whether domain has MX records */
|
|
277
|
+
domain_mx?: boolean;
|
|
278
|
+
/** Whether domain has SPF record */
|
|
279
|
+
domain_spf?: boolean;
|
|
280
|
+
/** Whether domain has DMARC record */
|
|
281
|
+
domain_dmarc?: boolean;
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* TryKitt.ai API response for verify-email endpoint
|
|
285
|
+
*/
|
|
286
|
+
export interface TryKittVerifyEmailResponse {
|
|
287
|
+
/** Job ID */
|
|
288
|
+
id: string;
|
|
289
|
+
/** Status: "completed", "pending", "failed" */
|
|
290
|
+
status: string;
|
|
291
|
+
/** When job was created */
|
|
292
|
+
created_at: string;
|
|
293
|
+
/** When job completed */
|
|
294
|
+
completed_at?: string;
|
|
295
|
+
/** Result data */
|
|
296
|
+
result: TryKittVerifyEmailResult | null;
|
|
297
|
+
/** Custom data passed in request */
|
|
298
|
+
custom_data?: Record<string, unknown>;
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* BounceBan provider configuration
|
|
302
|
+
*
|
|
303
|
+
* BounceBan specializes in catch-all email verification with 85-95% accuracy
|
|
304
|
+
* using proprietary algorithms WITHOUT sending actual emails.
|
|
305
|
+
*
|
|
306
|
+
* Features:
|
|
307
|
+
* - Catch-all domain verification (industry-leading)
|
|
308
|
+
* - DeepVerify mode for pattern-guessed emails
|
|
309
|
+
* - Waterfall endpoint with free 30-min retry window
|
|
310
|
+
* - Single email verification is FREE
|
|
311
|
+
*
|
|
312
|
+
* @see https://bounceban.com
|
|
313
|
+
*/
|
|
314
|
+
export interface BounceBanConfig {
|
|
315
|
+
/** BounceBan API key */
|
|
316
|
+
apiKey: string;
|
|
317
|
+
/** API URL override (default: https://api.bounceban.com) */
|
|
318
|
+
apiUrl?: string;
|
|
319
|
+
/** Waterfall API URL (default: https://api-waterfall.bounceban.com) */
|
|
320
|
+
waterfallApiUrl?: string;
|
|
321
|
+
/** Timeout in ms (default: 80000 for waterfall, 30000 for standard) */
|
|
322
|
+
timeoutMs?: number;
|
|
323
|
+
/** Use DeepVerify mode when company domain is confirmed (default: false) */
|
|
324
|
+
useDeepVerify?: boolean;
|
|
325
|
+
/** Use waterfall endpoint for better catch-all handling (default: true) */
|
|
326
|
+
useWaterfall?: boolean;
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* BounceBan verification result
|
|
330
|
+
*/
|
|
331
|
+
export type BounceBanResult = "deliverable" | "risky" | "undeliverable" | "unknown";
|
|
332
|
+
/**
|
|
333
|
+
* BounceBan API response for single verification
|
|
334
|
+
*/
|
|
335
|
+
export interface BounceBanVerifyResponse {
|
|
336
|
+
/** Unique verification ID */
|
|
337
|
+
id: string;
|
|
338
|
+
/** Status: "success", "pending", "error" */
|
|
339
|
+
status: string;
|
|
340
|
+
/** Email that was verified */
|
|
341
|
+
email: string;
|
|
342
|
+
/** Verification result */
|
|
343
|
+
result: BounceBanResult;
|
|
344
|
+
/** Deliverability score (0-100) */
|
|
345
|
+
score: number;
|
|
346
|
+
/** Whether email is from disposable domain */
|
|
347
|
+
is_disposable: boolean;
|
|
348
|
+
/** Whether domain is catch-all (accept-all) */
|
|
349
|
+
is_accept_all: boolean;
|
|
350
|
+
/** Whether email is a role account */
|
|
351
|
+
is_role: boolean;
|
|
352
|
+
/** Whether email is from free provider */
|
|
353
|
+
is_free: boolean;
|
|
354
|
+
/** MX records for the domain */
|
|
355
|
+
mx_records?: string[];
|
|
356
|
+
/** SMTP provider name (e.g., "Google", "Microsoft") */
|
|
357
|
+
smtp_provider?: string;
|
|
358
|
+
/** Verification mode used */
|
|
359
|
+
mode?: "regular" | "deepverify";
|
|
360
|
+
/** When verification completed */
|
|
361
|
+
verify_at?: string;
|
|
362
|
+
/** Credits consumed for this verification */
|
|
363
|
+
credits_consumed?: number;
|
|
364
|
+
/** Remaining credits */
|
|
365
|
+
credits_remaining?: number;
|
|
366
|
+
}
|
|
163
367
|
/**
|
|
164
368
|
* Bouncer.io provider configuration
|
|
165
369
|
*
|
|
@@ -293,10 +497,14 @@ export interface ProvidersConfig {
|
|
|
293
497
|
smartprospect?: SmartProspectConfig;
|
|
294
498
|
/** Cosiall Profile Emails (FREE - uses global Cosiall config) */
|
|
295
499
|
cosiall?: CosiallConfig;
|
|
500
|
+
/** TryKitt.ai AI email finder (FREE for individuals, unlimited) */
|
|
501
|
+
trykitt?: TryKittConfig;
|
|
296
502
|
hunter?: HunterConfig;
|
|
297
503
|
dropcontact?: DropcontactConfig;
|
|
298
504
|
/** Bouncer.io for SMTP email verification (99%+ accuracy) */
|
|
299
505
|
bouncer?: BouncerConfig;
|
|
506
|
+
/** BounceBan catch-all verification (FREE single, 85-95% accuracy) */
|
|
507
|
+
bounceban?: BounceBanConfig;
|
|
300
508
|
/** Snov.io for email finding (98% delivery rate) */
|
|
301
509
|
snovio?: SnovioConfig;
|
|
302
510
|
}
|
|
@@ -377,22 +585,25 @@ export interface EnrichmentClient {
|
|
|
377
585
|
/**
|
|
378
586
|
* Available provider names
|
|
379
587
|
*/
|
|
380
|
-
export type ProviderName = "construct" | "ldd" | "smartprospect" | "cosiall" | "hunter" | "dropcontact" | "bouncer" | "snovio";
|
|
588
|
+
export type ProviderName = "construct" | "ldd" | "smartprospect" | "cosiall" | "trykitt" | "hunter" | "dropcontact" | "bouncer" | "bounceban" | "snovio";
|
|
381
589
|
/**
|
|
382
|
-
* Default provider order -
|
|
590
|
+
* Default provider order - 3-Phase Strategy
|
|
383
591
|
*
|
|
384
592
|
* PHASE 1 - Free lookups (run in parallel):
|
|
385
593
|
* - ldd: LinkedIn Data Dump - real verified emails (FREE with subscription)
|
|
386
594
|
* - smartprospect: SmartLead API - real verified emails (FREE with subscription)
|
|
387
595
|
* - cosiall: Cosiall Profile Emails - emails from LinkedIn profiles (FREE)
|
|
596
|
+
* - trykitt: TryKitt.ai - AI email finder (FREE for individuals, unlimited)
|
|
597
|
+
*
|
|
598
|
+
* PHASE 2 - Pattern guessing + verification (if Phase 1 < 80% confidence):
|
|
388
599
|
* - construct: Pattern guessing + MX check (FREE)
|
|
600
|
+
* - bounceban: BounceBan catch-all verification (FREE single, $0.003/bulk)
|
|
389
601
|
*
|
|
390
|
-
* PHASE
|
|
391
|
-
* -
|
|
392
|
-
* - snovio:
|
|
393
|
-
* - hunter: Hunter.io fallback ($0.005/email)
|
|
602
|
+
* PHASE 3 - Paid finders (only if Phase 2 inconclusive):
|
|
603
|
+
* - hunter: Hunter.io email finder ($0.005/email)
|
|
604
|
+
* - snovio: Snov.io email finder ($0.02/email)
|
|
394
605
|
*
|
|
395
|
-
* Note: dropcontact available but not in default order
|
|
606
|
+
* Note: bouncer, dropcontact available but not in default order
|
|
396
607
|
*/
|
|
397
608
|
export declare const DEFAULT_PROVIDER_ORDER: ProviderName[];
|
|
398
609
|
/**
|
|
@@ -402,7 +613,9 @@ export declare const DEFAULT_PROVIDER_ORDER: ProviderName[];
|
|
|
402
613
|
* - ldd: FREE (subscription-based)
|
|
403
614
|
* - smartprospect: FREE (included in SmartLead subscription)
|
|
404
615
|
* - cosiall: FREE (uses global Cosiall config)
|
|
616
|
+
* - trykitt: FREE (unlimited for individuals, $0.005/email high volume)
|
|
405
617
|
* - construct: FREE (pattern guessing + MX check)
|
|
618
|
+
* - bounceban: FREE single / $0.003/email bulk (catch-all specialist)
|
|
406
619
|
* - bouncer: $0.006/email (SMTP verification, 99%+ accuracy)
|
|
407
620
|
* - snovio: $0.02/email (email finding + verification)
|
|
408
621
|
* - hunter: $0.005/email
|
package/dist/enrichment/types.js
CHANGED
|
@@ -8,29 +8,33 @@
|
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
9
|
exports.SMARTPROSPECT_SUB_INDUSTRIES = exports.PROVIDER_COSTS = exports.DEFAULT_PROVIDER_ORDER = void 0;
|
|
10
10
|
/**
|
|
11
|
-
* Default provider order -
|
|
11
|
+
* Default provider order - 3-Phase Strategy
|
|
12
12
|
*
|
|
13
13
|
* PHASE 1 - Free lookups (run in parallel):
|
|
14
14
|
* - ldd: LinkedIn Data Dump - real verified emails (FREE with subscription)
|
|
15
15
|
* - smartprospect: SmartLead API - real verified emails (FREE with subscription)
|
|
16
16
|
* - cosiall: Cosiall Profile Emails - emails from LinkedIn profiles (FREE)
|
|
17
|
+
* - trykitt: TryKitt.ai - AI email finder (FREE for individuals, unlimited)
|
|
18
|
+
*
|
|
19
|
+
* PHASE 2 - Pattern guessing + verification (if Phase 1 < 80% confidence):
|
|
17
20
|
* - construct: Pattern guessing + MX check (FREE)
|
|
21
|
+
* - bounceban: BounceBan catch-all verification (FREE single, $0.003/bulk)
|
|
18
22
|
*
|
|
19
|
-
* PHASE
|
|
20
|
-
* -
|
|
21
|
-
* - snovio:
|
|
22
|
-
* - hunter: Hunter.io fallback ($0.005/email)
|
|
23
|
+
* PHASE 3 - Paid finders (only if Phase 2 inconclusive):
|
|
24
|
+
* - hunter: Hunter.io email finder ($0.005/email)
|
|
25
|
+
* - snovio: Snov.io email finder ($0.02/email)
|
|
23
26
|
*
|
|
24
|
-
* Note: dropcontact available but not in default order
|
|
27
|
+
* Note: bouncer, dropcontact available but not in default order
|
|
25
28
|
*/
|
|
26
29
|
exports.DEFAULT_PROVIDER_ORDER = [
|
|
27
30
|
"ldd",
|
|
28
31
|
"smartprospect",
|
|
29
32
|
"cosiall",
|
|
33
|
+
"trykitt",
|
|
30
34
|
"construct",
|
|
31
|
-
"
|
|
32
|
-
"snovio",
|
|
35
|
+
"bounceban",
|
|
33
36
|
"hunter",
|
|
37
|
+
"snovio",
|
|
34
38
|
];
|
|
35
39
|
/**
|
|
36
40
|
* Provider costs in USD per lookup
|
|
@@ -39,7 +43,9 @@ exports.DEFAULT_PROVIDER_ORDER = [
|
|
|
39
43
|
* - ldd: FREE (subscription-based)
|
|
40
44
|
* - smartprospect: FREE (included in SmartLead subscription)
|
|
41
45
|
* - cosiall: FREE (uses global Cosiall config)
|
|
46
|
+
* - trykitt: FREE (unlimited for individuals, $0.005/email high volume)
|
|
42
47
|
* - construct: FREE (pattern guessing + MX check)
|
|
48
|
+
* - bounceban: FREE single / $0.003/email bulk (catch-all specialist)
|
|
43
49
|
* - bouncer: $0.006/email (SMTP verification, 99%+ accuracy)
|
|
44
50
|
* - snovio: $0.02/email (email finding + verification)
|
|
45
51
|
* - hunter: $0.005/email
|
|
@@ -50,6 +56,8 @@ exports.PROVIDER_COSTS = {
|
|
|
50
56
|
ldd: 0,
|
|
51
57
|
smartprospect: 0,
|
|
52
58
|
cosiall: 0,
|
|
59
|
+
trykitt: 0, // FREE for individuals
|
|
60
|
+
bounceban: 0.003, // FREE single, $0.003 bulk
|
|
53
61
|
hunter: 0.005,
|
|
54
62
|
dropcontact: 0.01,
|
|
55
63
|
bouncer: 0.006,
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared Candidate Parsing Utilities
|
|
3
|
+
*
|
|
4
|
+
* Extracts common fields from EnrichmentCandidate objects.
|
|
5
|
+
* Reduces code duplication across providers by centralizing field extraction logic.
|
|
6
|
+
*/
|
|
7
|
+
import type { EnrichmentCandidate } from "../types";
|
|
8
|
+
/**
|
|
9
|
+
* Parsed name components from a candidate
|
|
10
|
+
*/
|
|
11
|
+
export interface ParsedName {
|
|
12
|
+
firstName: string;
|
|
13
|
+
lastName: string;
|
|
14
|
+
fullName: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Parsed company/domain info from a candidate
|
|
18
|
+
*/
|
|
19
|
+
export interface ParsedCompany {
|
|
20
|
+
company: string | null;
|
|
21
|
+
domain: string | null;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Parsed LinkedIn identifiers from a candidate
|
|
25
|
+
*/
|
|
26
|
+
export interface ParsedLinkedIn {
|
|
27
|
+
/** LinkedIn username/handle (e.g., "john-doe") */
|
|
28
|
+
username: string | null;
|
|
29
|
+
/** LinkedIn profile URL */
|
|
30
|
+
url: string | null;
|
|
31
|
+
/** Numeric LinkedIn ID (stable, never changes) */
|
|
32
|
+
numericId: string | null;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Full parsed candidate with all extracted fields
|
|
36
|
+
*/
|
|
37
|
+
export interface ParsedCandidate {
|
|
38
|
+
name: ParsedName;
|
|
39
|
+
company: ParsedCompany;
|
|
40
|
+
linkedin: ParsedLinkedIn;
|
|
41
|
+
title: string | null;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Extract name components from a candidate
|
|
45
|
+
*
|
|
46
|
+
* Checks multiple field naming conventions:
|
|
47
|
+
* - firstName, first_name, first
|
|
48
|
+
* - lastName, last_name, last
|
|
49
|
+
* - name, fullName, full_name (split on space)
|
|
50
|
+
*/
|
|
51
|
+
export declare function extractName(candidate: EnrichmentCandidate): ParsedName;
|
|
52
|
+
/**
|
|
53
|
+
* Extract company and domain from a candidate
|
|
54
|
+
*
|
|
55
|
+
* Checks multiple field naming conventions:
|
|
56
|
+
* - company, currentCompany, organization, org
|
|
57
|
+
* - domain, companyDomain, company_domain
|
|
58
|
+
*/
|
|
59
|
+
export declare function extractCompany(candidate: EnrichmentCandidate): ParsedCompany;
|
|
60
|
+
/**
|
|
61
|
+
* Extract LinkedIn username from a URL
|
|
62
|
+
*
|
|
63
|
+
* Handles various URL formats:
|
|
64
|
+
* - https://linkedin.com/in/john-doe
|
|
65
|
+
* - https://www.linkedin.com/in/john-doe/
|
|
66
|
+
* - linkedin.com/in/john-doe?param=value
|
|
67
|
+
*/
|
|
68
|
+
export declare function extractLinkedInUsernameFromUrl(url: string | undefined | null): string | null;
|
|
69
|
+
/**
|
|
70
|
+
* Extract numeric LinkedIn ID from various formats
|
|
71
|
+
*
|
|
72
|
+
* Handles:
|
|
73
|
+
* - Direct numeric ID: "307567"
|
|
74
|
+
* - URN format: "urn:li:member:307567"
|
|
75
|
+
* - Sales profile URN: "urn:li:fs_salesProfile:(307567,...)"
|
|
76
|
+
*/
|
|
77
|
+
export declare function extractNumericLinkedInId(input: string | undefined | null): string | null;
|
|
78
|
+
/**
|
|
79
|
+
* Extract LinkedIn identifiers from a candidate
|
|
80
|
+
*
|
|
81
|
+
* Returns username, URL, and numeric ID (if available).
|
|
82
|
+
* Numeric ID is preferred for lookups as it never changes.
|
|
83
|
+
*/
|
|
84
|
+
export declare function extractLinkedIn(candidate: EnrichmentCandidate): ParsedLinkedIn;
|
|
85
|
+
/**
|
|
86
|
+
* Extract job title from a candidate
|
|
87
|
+
*/
|
|
88
|
+
export declare function extractTitle(candidate: EnrichmentCandidate): string | null;
|
|
89
|
+
/**
|
|
90
|
+
* Parse all common fields from a candidate
|
|
91
|
+
*
|
|
92
|
+
* Use this for a complete extraction of all fields at once.
|
|
93
|
+
* For partial extraction, use the individual functions.
|
|
94
|
+
*/
|
|
95
|
+
export declare function parseCandidate(candidate: EnrichmentCandidate): ParsedCandidate;
|
|
96
|
+
/**
|
|
97
|
+
* Build a LinkedIn URL from a username/handle
|
|
98
|
+
*/
|
|
99
|
+
export declare function buildLinkedInUrl(username: string): string;
|
|
100
|
+
/**
|
|
101
|
+
* Check if a candidate has minimum required fields for email lookup
|
|
102
|
+
*
|
|
103
|
+
* @param candidate - The candidate to check
|
|
104
|
+
* @param requireDomain - Whether domain is required (default: true)
|
|
105
|
+
* @returns true if candidate has at least name and optionally domain
|
|
106
|
+
*/
|
|
107
|
+
export declare function hasMinimumFields(candidate: EnrichmentCandidate, requireDomain?: boolean): boolean;
|