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,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple LRU (Least Recently Used) Cache
|
|
3
|
+
*
|
|
4
|
+
* A memory-bounded cache that evicts the least recently used entries
|
|
5
|
+
* when the maximum size is exceeded. Prevents unbounded memory growth.
|
|
6
|
+
*
|
|
7
|
+
* Features:
|
|
8
|
+
* - O(1) get and set operations
|
|
9
|
+
* - Automatic eviction of stale entries
|
|
10
|
+
* - Optional TTL support
|
|
11
|
+
* - Size limit enforcement
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Cache entry with data and timestamp
|
|
15
|
+
*/
|
|
16
|
+
export interface CacheEntry<T> {
|
|
17
|
+
data: T;
|
|
18
|
+
ts: number;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* LRU Cache implementation using Map's insertion order
|
|
22
|
+
*
|
|
23
|
+
* JavaScript Map maintains insertion order, so we can implement LRU
|
|
24
|
+
* by deleting and re-inserting on access (moving to end).
|
|
25
|
+
*/
|
|
26
|
+
export declare class LRUCache<T> {
|
|
27
|
+
private cache;
|
|
28
|
+
private readonly maxSize;
|
|
29
|
+
private readonly ttlMs;
|
|
30
|
+
/**
|
|
31
|
+
* Create a new LRU cache
|
|
32
|
+
*
|
|
33
|
+
* @param maxSize - Maximum number of entries to store (default: 1000)
|
|
34
|
+
* @param ttlMs - Optional TTL in milliseconds (default: null = no expiry)
|
|
35
|
+
*/
|
|
36
|
+
constructor(maxSize?: number, ttlMs?: number | null);
|
|
37
|
+
/**
|
|
38
|
+
* Get a value from the cache
|
|
39
|
+
*
|
|
40
|
+
* @param key - Cache key
|
|
41
|
+
* @param ttlOverride - Optional TTL override in milliseconds (for dynamic TTL from config)
|
|
42
|
+
* @returns The cached value or null if not found/expired
|
|
43
|
+
*/
|
|
44
|
+
get(key: string, ttlOverride?: number): T | null;
|
|
45
|
+
/**
|
|
46
|
+
* Set a value in the cache
|
|
47
|
+
*
|
|
48
|
+
* @param key - Cache key
|
|
49
|
+
* @param data - Value to cache
|
|
50
|
+
*/
|
|
51
|
+
set(key: string, data: T): void;
|
|
52
|
+
/**
|
|
53
|
+
* Delete a key from the cache
|
|
54
|
+
*
|
|
55
|
+
* @param key - Cache key to delete
|
|
56
|
+
* @returns true if the key existed
|
|
57
|
+
*/
|
|
58
|
+
delete(key: string): boolean;
|
|
59
|
+
/**
|
|
60
|
+
* Check if a key exists in the cache (without updating access time)
|
|
61
|
+
*
|
|
62
|
+
* @param key - Cache key
|
|
63
|
+
* @returns true if key exists and is not expired
|
|
64
|
+
*/
|
|
65
|
+
has(key: string): boolean;
|
|
66
|
+
/**
|
|
67
|
+
* Clear all entries from the cache
|
|
68
|
+
*/
|
|
69
|
+
clear(): void;
|
|
70
|
+
/**
|
|
71
|
+
* Get the current number of entries in the cache
|
|
72
|
+
*/
|
|
73
|
+
get size(): number;
|
|
74
|
+
/**
|
|
75
|
+
* Get all keys in the cache (for debugging/testing)
|
|
76
|
+
*/
|
|
77
|
+
keys(): IterableIterator<string>;
|
|
78
|
+
/**
|
|
79
|
+
* Prune expired entries (useful for periodic cleanup)
|
|
80
|
+
*
|
|
81
|
+
* @returns Number of entries removed
|
|
82
|
+
*/
|
|
83
|
+
prune(): number;
|
|
84
|
+
/**
|
|
85
|
+
* Get cache statistics
|
|
86
|
+
*/
|
|
87
|
+
stats(): {
|
|
88
|
+
size: number;
|
|
89
|
+
maxSize: number;
|
|
90
|
+
ttlMs: number | null;
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Create a new LRU cache with the specified options
|
|
95
|
+
*
|
|
96
|
+
* @example
|
|
97
|
+
* ```typescript
|
|
98
|
+
* // Cache up to 500 profiles for 15 minutes
|
|
99
|
+
* const profileCache = createLRUCache<LinkedInProfile>(500, 15 * 60 * 1000);
|
|
100
|
+
*
|
|
101
|
+
* profileCache.set('johndoe', profile);
|
|
102
|
+
* const cached = profileCache.get('johndoe');
|
|
103
|
+
* ```
|
|
104
|
+
*/
|
|
105
|
+
export declare function createLRUCache<T>(maxSize?: number, ttlMs?: number | null): LRUCache<T>;
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Simple LRU (Least Recently Used) Cache
|
|
4
|
+
*
|
|
5
|
+
* A memory-bounded cache that evicts the least recently used entries
|
|
6
|
+
* when the maximum size is exceeded. Prevents unbounded memory growth.
|
|
7
|
+
*
|
|
8
|
+
* Features:
|
|
9
|
+
* - O(1) get and set operations
|
|
10
|
+
* - Automatic eviction of stale entries
|
|
11
|
+
* - Optional TTL support
|
|
12
|
+
* - Size limit enforcement
|
|
13
|
+
*/
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.LRUCache = void 0;
|
|
16
|
+
exports.createLRUCache = createLRUCache;
|
|
17
|
+
/**
|
|
18
|
+
* LRU Cache implementation using Map's insertion order
|
|
19
|
+
*
|
|
20
|
+
* JavaScript Map maintains insertion order, so we can implement LRU
|
|
21
|
+
* by deleting and re-inserting on access (moving to end).
|
|
22
|
+
*/
|
|
23
|
+
class LRUCache {
|
|
24
|
+
cache;
|
|
25
|
+
maxSize;
|
|
26
|
+
ttlMs;
|
|
27
|
+
/**
|
|
28
|
+
* Create a new LRU cache
|
|
29
|
+
*
|
|
30
|
+
* @param maxSize - Maximum number of entries to store (default: 1000)
|
|
31
|
+
* @param ttlMs - Optional TTL in milliseconds (default: null = no expiry)
|
|
32
|
+
*/
|
|
33
|
+
constructor(maxSize = 1000, ttlMs = null) {
|
|
34
|
+
this.cache = new Map();
|
|
35
|
+
this.maxSize = maxSize;
|
|
36
|
+
this.ttlMs = ttlMs;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Get a value from the cache
|
|
40
|
+
*
|
|
41
|
+
* @param key - Cache key
|
|
42
|
+
* @param ttlOverride - Optional TTL override in milliseconds (for dynamic TTL from config)
|
|
43
|
+
* @returns The cached value or null if not found/expired
|
|
44
|
+
*/
|
|
45
|
+
get(key, ttlOverride) {
|
|
46
|
+
const entry = this.cache.get(key);
|
|
47
|
+
if (!entry) {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
// Check TTL expiration (use override if provided, otherwise instance TTL)
|
|
51
|
+
const effectiveTtl = ttlOverride ?? this.ttlMs;
|
|
52
|
+
if (effectiveTtl !== null && Date.now() - entry.ts > effectiveTtl) {
|
|
53
|
+
this.cache.delete(key);
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
// Move to end (most recently used) by delete + set
|
|
57
|
+
this.cache.delete(key);
|
|
58
|
+
this.cache.set(key, entry);
|
|
59
|
+
return entry.data;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Set a value in the cache
|
|
63
|
+
*
|
|
64
|
+
* @param key - Cache key
|
|
65
|
+
* @param data - Value to cache
|
|
66
|
+
*/
|
|
67
|
+
set(key, data) {
|
|
68
|
+
// If key exists, delete it first (will be re-added at end)
|
|
69
|
+
if (this.cache.has(key)) {
|
|
70
|
+
this.cache.delete(key);
|
|
71
|
+
}
|
|
72
|
+
// Evict oldest entries if at capacity
|
|
73
|
+
while (this.cache.size >= this.maxSize) {
|
|
74
|
+
const oldestKey = this.cache.keys().next().value;
|
|
75
|
+
if (oldestKey !== undefined) {
|
|
76
|
+
this.cache.delete(oldestKey);
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
// Add new entry
|
|
83
|
+
this.cache.set(key, { data, ts: Date.now() });
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Delete a key from the cache
|
|
87
|
+
*
|
|
88
|
+
* @param key - Cache key to delete
|
|
89
|
+
* @returns true if the key existed
|
|
90
|
+
*/
|
|
91
|
+
delete(key) {
|
|
92
|
+
return this.cache.delete(key);
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Check if a key exists in the cache (without updating access time)
|
|
96
|
+
*
|
|
97
|
+
* @param key - Cache key
|
|
98
|
+
* @returns true if key exists and is not expired
|
|
99
|
+
*/
|
|
100
|
+
has(key) {
|
|
101
|
+
const entry = this.cache.get(key);
|
|
102
|
+
if (!entry) {
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
// Check TTL expiration
|
|
106
|
+
if (this.ttlMs !== null && Date.now() - entry.ts > this.ttlMs) {
|
|
107
|
+
this.cache.delete(key);
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
return true;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Clear all entries from the cache
|
|
114
|
+
*/
|
|
115
|
+
clear() {
|
|
116
|
+
this.cache.clear();
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Get the current number of entries in the cache
|
|
120
|
+
*/
|
|
121
|
+
get size() {
|
|
122
|
+
return this.cache.size;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Get all keys in the cache (for debugging/testing)
|
|
126
|
+
*/
|
|
127
|
+
keys() {
|
|
128
|
+
return this.cache.keys();
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Prune expired entries (useful for periodic cleanup)
|
|
132
|
+
*
|
|
133
|
+
* @returns Number of entries removed
|
|
134
|
+
*/
|
|
135
|
+
prune() {
|
|
136
|
+
if (this.ttlMs === null) {
|
|
137
|
+
return 0;
|
|
138
|
+
}
|
|
139
|
+
const now = Date.now();
|
|
140
|
+
let removed = 0;
|
|
141
|
+
for (const [key, entry] of this.cache) {
|
|
142
|
+
if (now - entry.ts > this.ttlMs) {
|
|
143
|
+
this.cache.delete(key);
|
|
144
|
+
removed++;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return removed;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Get cache statistics
|
|
151
|
+
*/
|
|
152
|
+
stats() {
|
|
153
|
+
return {
|
|
154
|
+
size: this.cache.size,
|
|
155
|
+
maxSize: this.maxSize,
|
|
156
|
+
ttlMs: this.ttlMs,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
exports.LRUCache = LRUCache;
|
|
161
|
+
/**
|
|
162
|
+
* Create a new LRU cache with the specified options
|
|
163
|
+
*
|
|
164
|
+
* @example
|
|
165
|
+
* ```typescript
|
|
166
|
+
* // Cache up to 500 profiles for 15 minutes
|
|
167
|
+
* const profileCache = createLRUCache<LinkedInProfile>(500, 15 * 60 * 1000);
|
|
168
|
+
*
|
|
169
|
+
* profileCache.set('johndoe', profile);
|
|
170
|
+
* const cached = profileCache.get('johndoe');
|
|
171
|
+
* ```
|
|
172
|
+
*/
|
|
173
|
+
function createLRUCache(maxSize = 1000, ttlMs = null) {
|
|
174
|
+
return new LRUCache(maxSize, ttlMs);
|
|
175
|
+
}
|
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
# Cosiall Profile Emails API
|
|
2
|
+
|
|
3
|
+
This document describes how to use the Cosiall Profile Emails API to retrieve email addresses associated with LinkedIn profiles.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The Profile Emails endpoint retrieves all email addresses associated with a LinkedIn profile from the Cosiall database. It provides flexible lookup options using ObjectURN, LinkedIn URL, or vanity name.
|
|
8
|
+
|
|
9
|
+
## API Endpoint
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
GET /api/flexiq/profile-emails
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
### Authentication
|
|
16
|
+
|
|
17
|
+
| Header | Value | Required |
|
|
18
|
+
|--------|-------|----------|
|
|
19
|
+
| `X-API-Key` | Your FlexIQ API key | Yes |
|
|
20
|
+
|
|
21
|
+
### Query Parameters
|
|
22
|
+
|
|
23
|
+
At least one of the following parameters must be provided:
|
|
24
|
+
|
|
25
|
+
| Parameter | Type | Description | Example |
|
|
26
|
+
|-----------|------|-------------|---------|
|
|
27
|
+
| `objectUrn` | string | LinkedIn ObjectURN identifier | `urn:li:member:129147375` |
|
|
28
|
+
| `linkedInUrl` | string | Full LinkedIn profile URL | `https://www.linkedin.com/in/john-doe/` |
|
|
29
|
+
| `vanity` | string | LinkedIn username/vanity name | `john-doe` |
|
|
30
|
+
|
|
31
|
+
**Priority**: If multiple parameters are provided, the lookup follows this order:
|
|
32
|
+
1. `objectUrn` (most precise)
|
|
33
|
+
2. `linkedInUrl`
|
|
34
|
+
3. `vanity`
|
|
35
|
+
|
|
36
|
+
### Response
|
|
37
|
+
|
|
38
|
+
#### Success (200 OK)
|
|
39
|
+
|
|
40
|
+
```json
|
|
41
|
+
{
|
|
42
|
+
"profileId": 12345,
|
|
43
|
+
"objectUrn": "urn:li:member:129147375",
|
|
44
|
+
"linkedInUrl": "https://www.linkedin.com/in/john-doe/",
|
|
45
|
+
"emails": [
|
|
46
|
+
"john.doe@company.com",
|
|
47
|
+
"johndoe@gmail.com"
|
|
48
|
+
]
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
| Field | Type | Description |
|
|
53
|
+
|-------|------|-------------|
|
|
54
|
+
| `profileId` | integer | Cosiall internal profile ID |
|
|
55
|
+
| `objectUrn` | string | LinkedIn ObjectURN |
|
|
56
|
+
| `linkedInUrl` | string | LinkedIn profile URL |
|
|
57
|
+
| `emails` | array | List of email addresses (may be empty) |
|
|
58
|
+
|
|
59
|
+
#### Error Responses
|
|
60
|
+
|
|
61
|
+
| Status | Description | Response Body |
|
|
62
|
+
|--------|-------------|---------------|
|
|
63
|
+
| 400 Bad Request | No search parameters provided | `{ "error": "At least one of 'objectUrn', 'linkedInUrl', or 'vanity' must be provided." }` |
|
|
64
|
+
| 401 Unauthorized | Invalid or missing API key | - |
|
|
65
|
+
| 404 Not Found | Profile not found in database | `{ "error": "No LinkedIn profile found matching the provided parameters." }` |
|
|
66
|
+
| 500 Internal Server Error | Server error | `{ "error": "An internal server error occurred while retrieving profile emails." }` |
|
|
67
|
+
|
|
68
|
+
## Usage Examples
|
|
69
|
+
|
|
70
|
+
### cURL
|
|
71
|
+
|
|
72
|
+
**Using ObjectURN:**
|
|
73
|
+
```bash
|
|
74
|
+
curl -X GET "https://api.cosiall.com/api/flexiq/profile-emails?objectUrn=urn:li:member:129147375" \
|
|
75
|
+
-H "X-API-Key: your-api-key"
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
**Using LinkedIn URL:**
|
|
79
|
+
```bash
|
|
80
|
+
curl -X GET "https://api.cosiall.com/api/flexiq/profile-emails?linkedInUrl=https://www.linkedin.com/in/john-doe/" \
|
|
81
|
+
-H "X-API-Key: your-api-key"
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
**Using Vanity/Username:**
|
|
85
|
+
```bash
|
|
86
|
+
curl -X GET "https://api.cosiall.com/api/flexiq/profile-emails?vanity=john-doe" \
|
|
87
|
+
-H "X-API-Key: your-api-key"
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Python
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
import requests
|
|
94
|
+
|
|
95
|
+
API_KEY = "your-api-key"
|
|
96
|
+
BASE_URL = "https://api.cosiall.com/api/flexiq"
|
|
97
|
+
|
|
98
|
+
def get_profile_emails(object_urn=None, linkedin_url=None, vanity=None):
|
|
99
|
+
headers = {"X-API-Key": API_KEY}
|
|
100
|
+
params = {}
|
|
101
|
+
|
|
102
|
+
if object_urn:
|
|
103
|
+
params["objectUrn"] = object_urn
|
|
104
|
+
if linkedin_url:
|
|
105
|
+
params["linkedInUrl"] = linkedin_url
|
|
106
|
+
if vanity:
|
|
107
|
+
params["vanity"] = vanity
|
|
108
|
+
|
|
109
|
+
response = requests.get(f"{BASE_URL}/profile-emails", headers=headers, params=params)
|
|
110
|
+
|
|
111
|
+
if response.status_code == 200:
|
|
112
|
+
return response.json()
|
|
113
|
+
elif response.status_code == 404:
|
|
114
|
+
return None # Profile not found
|
|
115
|
+
else:
|
|
116
|
+
response.raise_for_status()
|
|
117
|
+
|
|
118
|
+
# Usage examples
|
|
119
|
+
emails_data = get_profile_emails(vanity="john-doe")
|
|
120
|
+
if emails_data:
|
|
121
|
+
print(f"Found {len(emails_data['emails'])} email(s): {emails_data['emails']}")
|
|
122
|
+
|
|
123
|
+
# Or by ObjectURN (most precise)
|
|
124
|
+
emails_data = get_profile_emails(object_urn="urn:li:member:129147375")
|
|
125
|
+
|
|
126
|
+
# Or by LinkedIn URL
|
|
127
|
+
emails_data = get_profile_emails(linkedin_url="https://www.linkedin.com/in/john-doe/")
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### JavaScript/Node.js
|
|
131
|
+
|
|
132
|
+
```javascript
|
|
133
|
+
const axios = require('axios');
|
|
134
|
+
|
|
135
|
+
const API_KEY = 'your-api-key';
|
|
136
|
+
const BASE_URL = 'https://api.cosiall.com/api/flexiq';
|
|
137
|
+
|
|
138
|
+
async function getProfileEmails({ objectUrn, linkedInUrl, vanity }) {
|
|
139
|
+
try {
|
|
140
|
+
const response = await axios.get(`${BASE_URL}/profile-emails`, {
|
|
141
|
+
headers: { 'X-API-Key': API_KEY },
|
|
142
|
+
params: { objectUrn, linkedInUrl, vanity }
|
|
143
|
+
});
|
|
144
|
+
return response.data;
|
|
145
|
+
} catch (error) {
|
|
146
|
+
if (error.response?.status === 404) {
|
|
147
|
+
return null; // Profile not found
|
|
148
|
+
}
|
|
149
|
+
throw error;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Usage examples
|
|
154
|
+
const data = await getProfileEmails({ vanity: 'john-doe' });
|
|
155
|
+
if (data) {
|
|
156
|
+
console.log(`Found ${data.emails.length} email(s):`, data.emails);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Or by ObjectURN
|
|
160
|
+
const dataByUrn = await getProfileEmails({ objectUrn: 'urn:li:member:129147375' });
|
|
161
|
+
|
|
162
|
+
// Or by LinkedIn URL
|
|
163
|
+
const dataByUrl = await getProfileEmails({ linkedInUrl: 'https://www.linkedin.com/in/john-doe/' });
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### C#
|
|
167
|
+
|
|
168
|
+
```csharp
|
|
169
|
+
using System.Net.Http;
|
|
170
|
+
using System.Text.Json;
|
|
171
|
+
|
|
172
|
+
public class FlexIQClient
|
|
173
|
+
{
|
|
174
|
+
private readonly HttpClient _client;
|
|
175
|
+
private const string BaseUrl = "https://api.cosiall.com/api/flexiq";
|
|
176
|
+
|
|
177
|
+
public FlexIQClient(string apiKey)
|
|
178
|
+
{
|
|
179
|
+
_client = new HttpClient();
|
|
180
|
+
_client.DefaultRequestHeaders.Add("X-API-Key", apiKey);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
public async Task<ProfileEmailsResponse?> GetProfileEmailsAsync(
|
|
184
|
+
string? objectUrn = null,
|
|
185
|
+
string? linkedInUrl = null,
|
|
186
|
+
string? vanity = null)
|
|
187
|
+
{
|
|
188
|
+
var queryParams = new List<string>();
|
|
189
|
+
if (!string.IsNullOrEmpty(objectUrn))
|
|
190
|
+
queryParams.Add($"objectUrn={Uri.EscapeDataString(objectUrn)}");
|
|
191
|
+
if (!string.IsNullOrEmpty(linkedInUrl))
|
|
192
|
+
queryParams.Add($"linkedInUrl={Uri.EscapeDataString(linkedInUrl)}");
|
|
193
|
+
if (!string.IsNullOrEmpty(vanity))
|
|
194
|
+
queryParams.Add($"vanity={Uri.EscapeDataString(vanity)}");
|
|
195
|
+
|
|
196
|
+
var url = $"{BaseUrl}/profile-emails?{string.Join("&", queryParams)}";
|
|
197
|
+
var response = await _client.GetAsync(url);
|
|
198
|
+
|
|
199
|
+
if (response.StatusCode == System.Net.HttpStatusCode.NotFound)
|
|
200
|
+
return null;
|
|
201
|
+
|
|
202
|
+
response.EnsureSuccessStatusCode();
|
|
203
|
+
var json = await response.Content.ReadAsStringAsync();
|
|
204
|
+
return JsonSerializer.Deserialize<ProfileEmailsResponse>(json);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
public class ProfileEmailsResponse
|
|
209
|
+
{
|
|
210
|
+
public int ProfileId { get; set; }
|
|
211
|
+
public string? ObjectUrn { get; set; }
|
|
212
|
+
public string? LinkedInUrl { get; set; }
|
|
213
|
+
public List<string> Emails { get; set; } = new();
|
|
214
|
+
}
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
## Using the LinkedIn Secret Sauce Library
|
|
218
|
+
|
|
219
|
+
The library provides a convenient wrapper function for this endpoint.
|
|
220
|
+
|
|
221
|
+
### Installation
|
|
222
|
+
|
|
223
|
+
```bash
|
|
224
|
+
npm install linkedin-secret-sauce
|
|
225
|
+
# or
|
|
226
|
+
pnpm add linkedin-secret-sauce
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
### Usage
|
|
230
|
+
|
|
231
|
+
```typescript
|
|
232
|
+
import {
|
|
233
|
+
initializeLinkedInClient,
|
|
234
|
+
fetchProfileEmailsFromCosiall
|
|
235
|
+
} from 'linkedin-secret-sauce';
|
|
236
|
+
|
|
237
|
+
// Initialize the client (required once at startup)
|
|
238
|
+
initializeLinkedInClient({
|
|
239
|
+
cosiallApiUrl: process.env.COSIALL_API_URL,
|
|
240
|
+
cosiallApiKey: process.env.COSIALL_API_KEY,
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
// Lookup by ObjectURN (most precise)
|
|
244
|
+
const result = await fetchProfileEmailsFromCosiall({
|
|
245
|
+
objectUrn: 'urn:li:member:129147375'
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
// Lookup by LinkedIn URL
|
|
249
|
+
const result = await fetchProfileEmailsFromCosiall({
|
|
250
|
+
linkedInUrl: 'https://www.linkedin.com/in/john-doe/'
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
// Lookup by vanity name
|
|
254
|
+
const result = await fetchProfileEmailsFromCosiall({
|
|
255
|
+
vanity: 'john-doe'
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
console.log(`Profile ID: ${result.profileId}`);
|
|
259
|
+
console.log(`Found ${result.emails.length} emails:`, result.emails);
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
### Error Handling
|
|
263
|
+
|
|
264
|
+
```typescript
|
|
265
|
+
import {
|
|
266
|
+
fetchProfileEmailsFromCosiall,
|
|
267
|
+
LinkedInClientError
|
|
268
|
+
} from 'linkedin-secret-sauce';
|
|
269
|
+
|
|
270
|
+
try {
|
|
271
|
+
const result = await fetchProfileEmailsFromCosiall({ vanity: 'john-doe' });
|
|
272
|
+
console.log(result.emails);
|
|
273
|
+
} catch (error) {
|
|
274
|
+
if (error instanceof LinkedInClientError) {
|
|
275
|
+
switch (error.code) {
|
|
276
|
+
case 'NOT_FOUND':
|
|
277
|
+
console.log('Profile not found in database');
|
|
278
|
+
break;
|
|
279
|
+
case 'AUTH_ERROR':
|
|
280
|
+
console.log('Invalid API key');
|
|
281
|
+
break;
|
|
282
|
+
case 'INVALID_REQUEST':
|
|
283
|
+
console.log('Missing required parameters');
|
|
284
|
+
break;
|
|
285
|
+
default:
|
|
286
|
+
console.log('API error:', error.message);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
### TypeScript Types
|
|
293
|
+
|
|
294
|
+
```typescript
|
|
295
|
+
import type {
|
|
296
|
+
CosiallProfileEmailsResponse,
|
|
297
|
+
ProfileEmailsLookupOptions
|
|
298
|
+
} from 'linkedin-secret-sauce';
|
|
299
|
+
|
|
300
|
+
// Response type
|
|
301
|
+
interface CosiallProfileEmailsResponse {
|
|
302
|
+
profileId: number;
|
|
303
|
+
objectUrn: string;
|
|
304
|
+
linkedInUrl: string;
|
|
305
|
+
emails: string[];
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// Options type
|
|
309
|
+
interface ProfileEmailsLookupOptions {
|
|
310
|
+
objectUrn?: string;
|
|
311
|
+
linkedInUrl?: string;
|
|
312
|
+
vanity?: string;
|
|
313
|
+
}
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
## Notes
|
|
317
|
+
|
|
318
|
+
- **Empty Emails Array**: A 200 OK response with an empty `emails` array means the profile exists but no emails are stored for it.
|
|
319
|
+
|
|
320
|
+
- **URL Normalization**: The endpoint handles various URL formats:
|
|
321
|
+
- With or without trailing slash: `linkedin.com/in/john-doe` or `linkedin.com/in/john-doe/`
|
|
322
|
+
- With or without `https://` prefix
|
|
323
|
+
|
|
324
|
+
- **Data Freshness**: Emails are retrieved from Cosiall's database and reflect the data collected during profile enrichment.
|
|
325
|
+
|
|
326
|
+
- **Deduplication**: Email addresses are automatically deduplicated in the response.
|
|
327
|
+
|
|
328
|
+
## Related Endpoints
|
|
329
|
+
|
|
330
|
+
| Endpoint | Description |
|
|
331
|
+
|----------|-------------|
|
|
332
|
+
| `GET /api/flexiq/linkedin-cookies/all` | Retrieve Sales Navigator session cookies |
|
|
333
|
+
|
|
334
|
+
## Playground Testing
|
|
335
|
+
|
|
336
|
+
The LinkedIn Secret Sauce Playground includes a dedicated "Cosiall" tab for testing the Profile Emails API. Start the playground with:
|
|
337
|
+
|
|
338
|
+
```bash
|
|
339
|
+
pnpm dev:playground
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
Then navigate to the "Cosiall" tab to interactively test lookups by vanity, URL, or ObjectURN.
|