linkedin-secret-sauce 0.11.1 → 0.12.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.
- package/dist/cosiall-client.d.ts +40 -1
- package/dist/cosiall-client.js +142 -3
- package/dist/index.d.ts +1 -0
- package/dist/types.d.ts +29 -3
- package/dist/utils/metrics.d.ts +3 -0
- package/dist/utils/metrics.js +3 -0
- package/package.json +1 -1
package/dist/cosiall-client.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AccountCookies } from "./types";
|
|
1
|
+
import type { AccountCookies, CosiallProfileEmailsResponse, ProfileEmailsLookupOptions } from "./types";
|
|
2
2
|
/**
|
|
3
3
|
* Fetches LinkedIn cookies for all available accounts from the Cosiall API.
|
|
4
4
|
* These cookies are used for authenticating requests to LinkedIn's API.
|
|
@@ -19,3 +19,42 @@ import type { AccountCookies } from "./types";
|
|
|
19
19
|
* ```
|
|
20
20
|
*/
|
|
21
21
|
export declare function fetchCookiesFromCosiall(): Promise<AccountCookies[]>;
|
|
22
|
+
/**
|
|
23
|
+
* Fetches email addresses associated with a LinkedIn profile from the Cosiall API.
|
|
24
|
+
*
|
|
25
|
+
* This endpoint provides flexible lookup by ObjectURN, LinkedIn URL, or vanity name.
|
|
26
|
+
* At least one lookup parameter must be provided.
|
|
27
|
+
*
|
|
28
|
+
* @param options - Lookup options (objectUrn, linkedInUrl, or vanity)
|
|
29
|
+
* @returns Profile emails response with profileId, objectUrn, linkedInUrl, and emails array
|
|
30
|
+
* @throws LinkedInClientError with code INVALID_REQUEST if no lookup parameters provided
|
|
31
|
+
* @throws LinkedInClientError with code NOT_FOUND if profile not found
|
|
32
|
+
* @throws LinkedInClientError with code AUTH_ERROR if API key is invalid
|
|
33
|
+
* @throws LinkedInClientError with code REQUEST_FAILED for other API errors
|
|
34
|
+
*
|
|
35
|
+
* @remarks
|
|
36
|
+
* - Lookup priority: objectUrn > linkedInUrl > vanity
|
|
37
|
+
* - Returns empty emails array if profile exists but has no emails
|
|
38
|
+
* - URL normalization is handled server-side (trailing slashes, https prefix)
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```typescript
|
|
42
|
+
* // Lookup by ObjectURN (most precise)
|
|
43
|
+
* const result = await fetchProfileEmailsFromCosiall({
|
|
44
|
+
* objectUrn: 'urn:li:fsd_profile:ACoAABcdEfG'
|
|
45
|
+
* });
|
|
46
|
+
*
|
|
47
|
+
* // Lookup by LinkedIn URL
|
|
48
|
+
* const result = await fetchProfileEmailsFromCosiall({
|
|
49
|
+
* linkedInUrl: 'https://www.linkedin.com/in/john-doe/'
|
|
50
|
+
* });
|
|
51
|
+
*
|
|
52
|
+
* // Lookup by vanity name
|
|
53
|
+
* const result = await fetchProfileEmailsFromCosiall({
|
|
54
|
+
* vanity: 'john-doe'
|
|
55
|
+
* });
|
|
56
|
+
*
|
|
57
|
+
* console.log(`Found ${result.emails.length} emails:`, result.emails);
|
|
58
|
+
* ```
|
|
59
|
+
*/
|
|
60
|
+
export declare function fetchProfileEmailsFromCosiall(options: ProfileEmailsLookupOptions): Promise<CosiallProfileEmailsResponse>;
|
package/dist/cosiall-client.js
CHANGED
|
@@ -34,6 +34,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.fetchCookiesFromCosiall = fetchCookiesFromCosiall;
|
|
37
|
+
exports.fetchProfileEmailsFromCosiall = fetchProfileEmailsFromCosiall;
|
|
37
38
|
const config_1 = require("./config");
|
|
38
39
|
const errors_1 = require("./utils/errors");
|
|
39
40
|
const logger_1 = require("./utils/logger");
|
|
@@ -120,11 +121,149 @@ async function fetchCookiesFromCosiall() {
|
|
|
120
121
|
}
|
|
121
122
|
return true;
|
|
122
123
|
}
|
|
123
|
-
return data
|
|
124
|
-
.filter(isItem)
|
|
125
|
-
.map((item) => ({
|
|
124
|
+
return data.filter(isItem).map((item) => ({
|
|
126
125
|
accountId: item.accountId,
|
|
127
126
|
cookies: item.cookies,
|
|
128
127
|
expiresAt: item.expiresAt,
|
|
129
128
|
}));
|
|
130
129
|
}
|
|
130
|
+
/**
|
|
131
|
+
* Fetches email addresses associated with a LinkedIn profile from the Cosiall API.
|
|
132
|
+
*
|
|
133
|
+
* This endpoint provides flexible lookup by ObjectURN, LinkedIn URL, or vanity name.
|
|
134
|
+
* At least one lookup parameter must be provided.
|
|
135
|
+
*
|
|
136
|
+
* @param options - Lookup options (objectUrn, linkedInUrl, or vanity)
|
|
137
|
+
* @returns Profile emails response with profileId, objectUrn, linkedInUrl, and emails array
|
|
138
|
+
* @throws LinkedInClientError with code INVALID_REQUEST if no lookup parameters provided
|
|
139
|
+
* @throws LinkedInClientError with code NOT_FOUND if profile not found
|
|
140
|
+
* @throws LinkedInClientError with code AUTH_ERROR if API key is invalid
|
|
141
|
+
* @throws LinkedInClientError with code REQUEST_FAILED for other API errors
|
|
142
|
+
*
|
|
143
|
+
* @remarks
|
|
144
|
+
* - Lookup priority: objectUrn > linkedInUrl > vanity
|
|
145
|
+
* - Returns empty emails array if profile exists but has no emails
|
|
146
|
+
* - URL normalization is handled server-side (trailing slashes, https prefix)
|
|
147
|
+
*
|
|
148
|
+
* @example
|
|
149
|
+
* ```typescript
|
|
150
|
+
* // Lookup by ObjectURN (most precise)
|
|
151
|
+
* const result = await fetchProfileEmailsFromCosiall({
|
|
152
|
+
* objectUrn: 'urn:li:fsd_profile:ACoAABcdEfG'
|
|
153
|
+
* });
|
|
154
|
+
*
|
|
155
|
+
* // Lookup by LinkedIn URL
|
|
156
|
+
* const result = await fetchProfileEmailsFromCosiall({
|
|
157
|
+
* linkedInUrl: 'https://www.linkedin.com/in/john-doe/'
|
|
158
|
+
* });
|
|
159
|
+
*
|
|
160
|
+
* // Lookup by vanity name
|
|
161
|
+
* const result = await fetchProfileEmailsFromCosiall({
|
|
162
|
+
* vanity: 'john-doe'
|
|
163
|
+
* });
|
|
164
|
+
*
|
|
165
|
+
* console.log(`Found ${result.emails.length} emails:`, result.emails);
|
|
166
|
+
* ```
|
|
167
|
+
*/
|
|
168
|
+
async function fetchProfileEmailsFromCosiall(options) {
|
|
169
|
+
const { objectUrn, linkedInUrl, vanity } = options;
|
|
170
|
+
// Validate at least one parameter is provided
|
|
171
|
+
if (!objectUrn && !linkedInUrl && !vanity) {
|
|
172
|
+
throw new errors_1.LinkedInClientError("At least one of 'objectUrn', 'linkedInUrl', or 'vanity' must be provided", "INVALID_REQUEST", 400);
|
|
173
|
+
}
|
|
174
|
+
const { cosiallApiUrl, cosiallApiKey } = (0, config_1.getConfig)();
|
|
175
|
+
const base = cosiallApiUrl.replace(/\/+$/, "");
|
|
176
|
+
// Build query string
|
|
177
|
+
const params = new URLSearchParams();
|
|
178
|
+
if (objectUrn)
|
|
179
|
+
params.set("objectUrn", objectUrn);
|
|
180
|
+
if (linkedInUrl)
|
|
181
|
+
params.set("linkedInUrl", linkedInUrl);
|
|
182
|
+
if (vanity)
|
|
183
|
+
params.set("vanity", vanity);
|
|
184
|
+
const url = `${base}/api/flexiq/profile-emails?${params.toString()}`;
|
|
185
|
+
(0, logger_1.log)("info", "cosiall.profileEmails.start", {
|
|
186
|
+
objectUrn,
|
|
187
|
+
linkedInUrl,
|
|
188
|
+
vanity,
|
|
189
|
+
});
|
|
190
|
+
(0, metrics_1.incrementMetric)("cosiallProfileEmailsFetches");
|
|
191
|
+
const response = await fetch(url, {
|
|
192
|
+
method: "GET",
|
|
193
|
+
headers: {
|
|
194
|
+
"X-API-Key": cosiallApiKey,
|
|
195
|
+
Accept: "application/json",
|
|
196
|
+
},
|
|
197
|
+
});
|
|
198
|
+
// Handle error responses
|
|
199
|
+
if (!response.ok) {
|
|
200
|
+
const status = response.status;
|
|
201
|
+
let errorMessage = "Profile emails fetch failed";
|
|
202
|
+
let errorCode = "REQUEST_FAILED";
|
|
203
|
+
try {
|
|
204
|
+
const errorData = (await response.json());
|
|
205
|
+
errorMessage = errorData.error || errorMessage;
|
|
206
|
+
}
|
|
207
|
+
catch {
|
|
208
|
+
// Ignore JSON parse errors
|
|
209
|
+
}
|
|
210
|
+
if (status === 400) {
|
|
211
|
+
errorCode = "INVALID_REQUEST";
|
|
212
|
+
(0, logger_1.log)("warn", "cosiall.profileEmails.invalidRequest", {
|
|
213
|
+
status,
|
|
214
|
+
errorMessage,
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
else if (status === 401) {
|
|
218
|
+
errorCode = "AUTH_ERROR";
|
|
219
|
+
(0, logger_1.log)("warn", "cosiall.profileEmails.authError", { status });
|
|
220
|
+
}
|
|
221
|
+
else if (status === 404) {
|
|
222
|
+
errorCode = "NOT_FOUND";
|
|
223
|
+
(0, logger_1.log)("info", "cosiall.profileEmails.notFound", {
|
|
224
|
+
objectUrn,
|
|
225
|
+
linkedInUrl,
|
|
226
|
+
vanity,
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
(0, logger_1.log)("warn", "cosiall.profileEmails.error", { status, errorMessage });
|
|
231
|
+
(0, metrics_1.incrementMetric)("cosiallProfileEmailsFailures");
|
|
232
|
+
// Report unexpected errors to Sentry
|
|
233
|
+
try {
|
|
234
|
+
const { reportCriticalError } = await Promise.resolve().then(() => __importStar(require("./utils/sentry")));
|
|
235
|
+
reportCriticalError("Cosiall Profile Emails API failure", {
|
|
236
|
+
status,
|
|
237
|
+
errorMessage,
|
|
238
|
+
objectUrn,
|
|
239
|
+
linkedInUrl,
|
|
240
|
+
vanity,
|
|
241
|
+
tags: { component: "cosiall-client", endpoint: "profile-emails" },
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
catch { }
|
|
245
|
+
}
|
|
246
|
+
throw new errors_1.LinkedInClientError(errorMessage, errorCode, status);
|
|
247
|
+
}
|
|
248
|
+
const data = await response.json();
|
|
249
|
+
// Validate response structure
|
|
250
|
+
if (!data ||
|
|
251
|
+
typeof data !== "object" ||
|
|
252
|
+
!("profileId" in data) ||
|
|
253
|
+
!("objectUrn" in data) ||
|
|
254
|
+
!("linkedInUrl" in data) ||
|
|
255
|
+
!("emails" in data)) {
|
|
256
|
+
(0, logger_1.log)("error", "cosiall.profileEmails.invalidFormat", {
|
|
257
|
+
dataType: typeof data,
|
|
258
|
+
});
|
|
259
|
+
(0, metrics_1.incrementMetric)("cosiallProfileEmailsFailures");
|
|
260
|
+
throw new errors_1.LinkedInClientError("Invalid profile emails response format", "REQUEST_FAILED", 500);
|
|
261
|
+
}
|
|
262
|
+
const result = data;
|
|
263
|
+
(0, logger_1.log)("info", "cosiall.profileEmails.success", {
|
|
264
|
+
profileId: result.profileId,
|
|
265
|
+
emailCount: result.emails.length,
|
|
266
|
+
});
|
|
267
|
+
(0, metrics_1.incrementMetric)("cosiallProfileEmailsSuccess");
|
|
268
|
+
return result;
|
|
269
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -52,6 +52,7 @@ export type { LinkedInTenure, LinkedInPosition, LinkedInSpotlightBadge, SalesLea
|
|
|
52
52
|
export { LinkedInClientError } from "./utils/errors";
|
|
53
53
|
export * from "./cosiall-client";
|
|
54
54
|
export * from "./cookie-pool";
|
|
55
|
+
export type { CosiallProfileEmailsResponse, ProfileEmailsLookupOptions, } from "./types";
|
|
55
56
|
export { parseFullProfile } from "./parsers/profile-parser";
|
|
56
57
|
export { parseSalesSearchResults } from "./parsers/search-parser";
|
|
57
58
|
export * from "./utils/metrics";
|
package/dist/types.d.ts
CHANGED
|
@@ -1,3 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Response from the Cosiall Profile Emails API endpoint.
|
|
3
|
+
* Returns email addresses associated with a LinkedIn profile.
|
|
4
|
+
*/
|
|
5
|
+
export interface CosiallProfileEmailsResponse {
|
|
6
|
+
/** Cosiall internal profile ID */
|
|
7
|
+
profileId: number;
|
|
8
|
+
/** LinkedIn ObjectURN identifier (e.g., "urn:li:fsd_profile:ACoAABcdEfG") */
|
|
9
|
+
objectUrn: string;
|
|
10
|
+
/** LinkedIn profile URL */
|
|
11
|
+
linkedInUrl: string;
|
|
12
|
+
/** List of email addresses associated with the profile (may be empty) */
|
|
13
|
+
emails: string[];
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Options for looking up profile emails from Cosiall.
|
|
17
|
+
* At least one of objectUrn, linkedInUrl, or vanity must be provided.
|
|
18
|
+
*/
|
|
19
|
+
export interface ProfileEmailsLookupOptions {
|
|
20
|
+
/** LinkedIn ObjectURN identifier (most precise lookup method) */
|
|
21
|
+
objectUrn?: string;
|
|
22
|
+
/** Full LinkedIn profile URL (e.g., "https://www.linkedin.com/in/john-doe/") */
|
|
23
|
+
linkedInUrl?: string;
|
|
24
|
+
/** LinkedIn username/vanity name (e.g., "john-doe") */
|
|
25
|
+
vanity?: string;
|
|
26
|
+
}
|
|
1
27
|
export interface LinkedInCookie {
|
|
2
28
|
name: string;
|
|
3
29
|
value: string;
|
|
@@ -6,7 +32,7 @@ export interface LinkedInCookie {
|
|
|
6
32
|
expires?: number;
|
|
7
33
|
httpOnly?: boolean;
|
|
8
34
|
secure?: boolean;
|
|
9
|
-
sameSite?:
|
|
35
|
+
sameSite?: "Strict" | "Lax" | "None";
|
|
10
36
|
}
|
|
11
37
|
export interface AccountCookies {
|
|
12
38
|
accountId: string;
|
|
@@ -184,7 +210,7 @@ export interface Company {
|
|
|
184
210
|
specialties?: string[];
|
|
185
211
|
emailDomains?: string[];
|
|
186
212
|
}
|
|
187
|
-
export type TypeaheadType =
|
|
213
|
+
export type TypeaheadType = "BING_GEO" | "TITLE" | "INDUSTRY" | "SENIORITY_LEVEL" | "FUNCTION" | "COMPANY_SIZE" | "COMPANY" | "PROFILE_LANGUAGE";
|
|
188
214
|
export interface TypeaheadItem {
|
|
189
215
|
id: string;
|
|
190
216
|
text: string;
|
|
@@ -208,7 +234,7 @@ export interface SalesNavigatorProfile {
|
|
|
208
234
|
flagshipProfileUrl?: string;
|
|
209
235
|
}
|
|
210
236
|
export type Geo = {
|
|
211
|
-
type:
|
|
237
|
+
type: "region" | "country" | "state" | "city" | "postal";
|
|
212
238
|
value: string;
|
|
213
239
|
id?: string;
|
|
214
240
|
};
|
package/dist/utils/metrics.d.ts
CHANGED
|
@@ -15,6 +15,9 @@ export interface Metrics {
|
|
|
15
15
|
cosiallFetches: number;
|
|
16
16
|
cosiallSuccess: number;
|
|
17
17
|
cosiallFailures: number;
|
|
18
|
+
cosiallProfileEmailsFetches: number;
|
|
19
|
+
cosiallProfileEmailsSuccess: number;
|
|
20
|
+
cosiallProfileEmailsFailures: number;
|
|
18
21
|
companyFetches: number;
|
|
19
22
|
typeaheadRequests: number;
|
|
20
23
|
}
|
package/dist/utils/metrics.js
CHANGED