linkedin-secret-sauce 0.1.2 → 0.3.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/config.js +0 -1
- package/dist/cookie-pool.js +2 -1
- package/dist/cosiall-client.js +20 -5
- package/dist/http-client.d.ts +1 -1
- package/dist/http-client.js +10 -6
- package/dist/linkedin-api.d.ts +75 -0
- package/dist/linkedin-api.js +86 -9
- package/dist/parsers/company-parser.d.ts +1 -1
- package/dist/parsers/company-parser.js +13 -8
- package/dist/parsers/image-parser.d.ts +10 -1
- package/dist/parsers/image-parser.js +4 -3
- package/dist/parsers/profile-parser.d.ts +1 -1
- package/dist/parsers/profile-parser.js +28 -24
- package/dist/parsers/search-parser.d.ts +1 -1
- package/dist/parsers/search-parser.js +2 -1
- package/dist/utils/logger.d.ts +1 -1
- package/dist/utils/metrics.d.ts +4 -1
- package/dist/utils/metrics.js +0 -1
- package/dist/utils/search-encoder.d.ts +1 -1
- package/dist/utils/search-encoder.js +3 -2
- package/package.json +11 -7
package/dist/config.js
CHANGED
package/dist/cookie-pool.js
CHANGED
|
@@ -89,7 +89,8 @@ async function ensureInitialized() {
|
|
|
89
89
|
(0, logger_1.log)('info', 'cookiePool.refreshed', { count: refreshed.length });
|
|
90
90
|
}
|
|
91
91
|
catch (e) {
|
|
92
|
-
|
|
92
|
+
const err = e;
|
|
93
|
+
(0, logger_1.log)('warn', 'cookiePool.refreshFailed', { error: err?.message });
|
|
93
94
|
}
|
|
94
95
|
}, interval);
|
|
95
96
|
}
|
package/dist/cosiall-client.js
CHANGED
|
@@ -29,9 +29,24 @@ async function fetchCookiesFromCosiall() {
|
|
|
29
29
|
}
|
|
30
30
|
(0, logger_1.log)('info', 'cosiall.fetch.success', { count: data.length });
|
|
31
31
|
(0, metrics_1.incrementMetric)('cosiallSuccess');
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
32
|
+
function isCookie(obj) {
|
|
33
|
+
return !!obj && typeof obj === 'object' && 'name' in obj && 'value' in obj;
|
|
34
|
+
}
|
|
35
|
+
function isItem(obj) {
|
|
36
|
+
if (!obj || typeof obj !== 'object')
|
|
37
|
+
return false;
|
|
38
|
+
const rec = obj;
|
|
39
|
+
if (typeof rec.accountId !== 'string')
|
|
40
|
+
return false;
|
|
41
|
+
if (!Array.isArray(rec.cookies))
|
|
42
|
+
return false;
|
|
43
|
+
if (!rec.cookies.every(isCookie))
|
|
44
|
+
return false;
|
|
45
|
+
if (rec.expiresAt !== undefined && typeof rec.expiresAt !== 'number')
|
|
46
|
+
return false;
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
return data
|
|
50
|
+
.filter(isItem)
|
|
51
|
+
.map((item) => ({ accountId: item.accountId, cookies: item.cookies, expiresAt: item.expiresAt }));
|
|
37
52
|
}
|
package/dist/http-client.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export interface LinkedInRequestOptions {
|
|
2
2
|
url: string;
|
|
3
3
|
method?: 'GET' | 'POST';
|
|
4
|
-
body?:
|
|
4
|
+
body?: unknown;
|
|
5
5
|
headers?: Record<string, string>;
|
|
6
6
|
}
|
|
7
7
|
export declare function executeLinkedInRequest<T>(options: LinkedInRequestOptions, _operationName: string): Promise<T>;
|
package/dist/http-client.js
CHANGED
|
@@ -71,18 +71,22 @@ async function executeLinkedInRequest(options, _operationName) {
|
|
|
71
71
|
(0, logger_1.log)('debug', 'http.attempt', { accountId, attempt: attempt + 1, method: options.method ?? 'GET', url: options.url });
|
|
72
72
|
let res;
|
|
73
73
|
try {
|
|
74
|
-
|
|
74
|
+
const init = {
|
|
75
75
|
method: options.method ?? 'GET',
|
|
76
76
|
headers,
|
|
77
|
-
|
|
78
|
-
|
|
77
|
+
};
|
|
78
|
+
if (options.body !== undefined) {
|
|
79
|
+
init.body = typeof options.body === 'string' ? options.body : JSON.stringify(options.body);
|
|
80
|
+
}
|
|
81
|
+
res = (await fetch(options.url, init));
|
|
79
82
|
}
|
|
80
83
|
catch (err) {
|
|
81
|
-
const
|
|
82
|
-
|
|
84
|
+
const e = err;
|
|
85
|
+
const code = e?.code || e?.cause?.code || 'FETCH_FAILED';
|
|
86
|
+
(0, logger_1.log)('error', 'http.networkError', { accountId, url: options.url, code, message: String(e?.message || err) });
|
|
83
87
|
(0, metrics_1.incrementMetric)('httpFailures');
|
|
84
88
|
try {
|
|
85
|
-
(0, request_history_1.recordRequest)({ operation: op, selector: options.url, status: 0, durationMs: Date.now() - started, accountId, errorMessage: String(
|
|
89
|
+
(0, request_history_1.recordRequest)({ operation: op, selector: options.url, status: 0, durationMs: Date.now() - started, accountId, errorMessage: String(e?.message || err) });
|
|
86
90
|
}
|
|
87
91
|
catch { }
|
|
88
92
|
lastError = new errors_1.LinkedInClientError('LinkedIn fetch failed', 'REQUEST_FAILED', 0, accountId);
|
package/dist/linkedin-api.d.ts
CHANGED
|
@@ -21,4 +21,79 @@ export declare function typeahead(options: {
|
|
|
21
21
|
start?: number;
|
|
22
22
|
count?: number;
|
|
23
23
|
}): Promise<TypeaheadResult>;
|
|
24
|
+
export declare const YEARS_AT_COMPANY_OPTIONS: readonly [{
|
|
25
|
+
readonly id: 1;
|
|
26
|
+
readonly text: "Less than 1 year";
|
|
27
|
+
readonly displayValue: "< 1 year";
|
|
28
|
+
}, {
|
|
29
|
+
readonly id: 2;
|
|
30
|
+
readonly text: "1 to 2 years";
|
|
31
|
+
readonly displayValue: "1-2 years";
|
|
32
|
+
}, {
|
|
33
|
+
readonly id: 3;
|
|
34
|
+
readonly text: "3 to 5 years";
|
|
35
|
+
readonly displayValue: "3-5 years";
|
|
36
|
+
}, {
|
|
37
|
+
readonly id: 4;
|
|
38
|
+
readonly text: "6 to 10 years";
|
|
39
|
+
readonly displayValue: "6-10 years";
|
|
40
|
+
}, {
|
|
41
|
+
readonly id: 5;
|
|
42
|
+
readonly text: "More than 10 years";
|
|
43
|
+
readonly displayValue: "10+ years";
|
|
44
|
+
}];
|
|
45
|
+
export declare const YEARS_IN_POSITION_OPTIONS: readonly [{
|
|
46
|
+
readonly id: 1;
|
|
47
|
+
readonly text: "Less than 1 year";
|
|
48
|
+
readonly displayValue: "< 1 year";
|
|
49
|
+
}, {
|
|
50
|
+
readonly id: 2;
|
|
51
|
+
readonly text: "1 to 2 years";
|
|
52
|
+
readonly displayValue: "1-2 years";
|
|
53
|
+
}, {
|
|
54
|
+
readonly id: 3;
|
|
55
|
+
readonly text: "3 to 5 years";
|
|
56
|
+
readonly displayValue: "3-5 years";
|
|
57
|
+
}, {
|
|
58
|
+
readonly id: 4;
|
|
59
|
+
readonly text: "6 to 10 years";
|
|
60
|
+
readonly displayValue: "6-10 years";
|
|
61
|
+
}, {
|
|
62
|
+
readonly id: 5;
|
|
63
|
+
readonly text: "More than 10 years";
|
|
64
|
+
readonly displayValue: "10+ years";
|
|
65
|
+
}];
|
|
66
|
+
export declare const YEARS_OF_EXPERIENCE_OPTIONS: readonly [{
|
|
67
|
+
readonly id: 1;
|
|
68
|
+
readonly text: "Less than 1 year";
|
|
69
|
+
readonly displayValue: "< 1 year";
|
|
70
|
+
}, {
|
|
71
|
+
readonly id: 2;
|
|
72
|
+
readonly text: "1 to 2 years";
|
|
73
|
+
readonly displayValue: "1-2 years";
|
|
74
|
+
}, {
|
|
75
|
+
readonly id: 3;
|
|
76
|
+
readonly text: "3 to 5 years";
|
|
77
|
+
readonly displayValue: "3-5 years";
|
|
78
|
+
}, {
|
|
79
|
+
readonly id: 4;
|
|
80
|
+
readonly text: "6 to 10 years";
|
|
81
|
+
readonly displayValue: "6-10 years";
|
|
82
|
+
}, {
|
|
83
|
+
readonly id: 5;
|
|
84
|
+
readonly text: "More than 10 years";
|
|
85
|
+
readonly displayValue: "10+ years";
|
|
86
|
+
}];
|
|
87
|
+
/**
|
|
88
|
+
* Returns static years at company options (no API call needed)
|
|
89
|
+
*/
|
|
90
|
+
export declare function getYearsAtCompanyOptions(): Promise<TypeaheadResult>;
|
|
91
|
+
/**
|
|
92
|
+
* Returns static years in position options (no API call needed)
|
|
93
|
+
*/
|
|
94
|
+
export declare function getYearsInPositionOptions(): Promise<TypeaheadResult>;
|
|
95
|
+
/**
|
|
96
|
+
* Returns static years of experience options (no API call needed)
|
|
97
|
+
*/
|
|
98
|
+
export declare function getYearsOfExperienceOptions(): Promise<TypeaheadResult>;
|
|
24
99
|
export declare function getSalesNavigatorProfileDetails(profileUrnOrId: string): Promise<SalesNavigatorProfile>;
|
package/dist/linkedin-api.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.YEARS_OF_EXPERIENCE_OPTIONS = exports.YEARS_IN_POSITION_OPTIONS = exports.YEARS_AT_COMPANY_OPTIONS = void 0;
|
|
3
4
|
exports.getProfileByVanity = getProfileByVanity;
|
|
4
5
|
exports.getProfileByUrn = getProfileByUrn;
|
|
5
6
|
exports.searchSalesLeads = searchSalesLeads;
|
|
@@ -8,6 +9,9 @@ exports.resolveCompanyUniversalName = resolveCompanyUniversalName;
|
|
|
8
9
|
exports.getCompanyById = getCompanyById;
|
|
9
10
|
exports.getCompanyByUrl = getCompanyByUrl;
|
|
10
11
|
exports.typeahead = typeahead;
|
|
12
|
+
exports.getYearsAtCompanyOptions = getYearsAtCompanyOptions;
|
|
13
|
+
exports.getYearsInPositionOptions = getYearsInPositionOptions;
|
|
14
|
+
exports.getYearsOfExperienceOptions = getYearsOfExperienceOptions;
|
|
11
15
|
exports.getSalesNavigatorProfileDetails = getSalesNavigatorProfileDetails;
|
|
12
16
|
const config_1 = require("./config");
|
|
13
17
|
const http_client_1 = require("./http-client");
|
|
@@ -210,7 +214,10 @@ async function searchSalesLeads(keywords, options) {
|
|
|
210
214
|
}
|
|
211
215
|
}
|
|
212
216
|
const items = (0, search_parser_1.parseSalesSearchResults)(raw);
|
|
213
|
-
const
|
|
217
|
+
const rrec = (raw && typeof raw === 'object') ? raw : undefined;
|
|
218
|
+
const pagingVal = rrec && 'paging' in rrec ? rrec.paging : undefined;
|
|
219
|
+
const p = (pagingVal && typeof pagingVal === 'object') ? pagingVal : undefined;
|
|
220
|
+
const paging = p ?? { start, count };
|
|
214
221
|
const result = options
|
|
215
222
|
? { items, page: { start: Number(paging.start ?? start), count: Number(paging.count ?? count), total: paging?.total } }
|
|
216
223
|
: items; // backward-compat: old tests expect an array when no options passed
|
|
@@ -223,11 +230,8 @@ async function getProfilesBatch(vanities, concurrency = 4) {
|
|
|
223
230
|
const results = Array.from({ length: vanities.length }, () => null);
|
|
224
231
|
let idx = 0;
|
|
225
232
|
async function worker() {
|
|
226
|
-
|
|
227
|
-
while (true) {
|
|
233
|
+
while (idx < vanities.length) {
|
|
228
234
|
const myIdx = idx++;
|
|
229
|
-
if (myIdx >= vanities.length)
|
|
230
|
-
break;
|
|
231
235
|
const vanity = vanities[myIdx];
|
|
232
236
|
try {
|
|
233
237
|
const prof = await getProfileByVanity(vanity);
|
|
@@ -253,7 +257,10 @@ async function resolveCompanyUniversalName(universalName) {
|
|
|
253
257
|
}
|
|
254
258
|
catch { }
|
|
255
259
|
const raw = await (0, http_client_1.executeLinkedInRequest)({ url }, 'resolveCompanyUniversalName');
|
|
256
|
-
const
|
|
260
|
+
const rec = (raw && typeof raw === 'object') ? raw : undefined;
|
|
261
|
+
const elementsVal = rec && 'elements' in rec ? rec.elements : undefined;
|
|
262
|
+
const first = Array.isArray(elementsVal) ? elementsVal[0] : undefined;
|
|
263
|
+
const id = first?.id ?? first?.entityUrn ?? undefined;
|
|
257
264
|
try {
|
|
258
265
|
(0, logger_1.log)('info', 'api.ok', { operation: 'resolveCompanyUniversalName', selector: universalName, id });
|
|
259
266
|
}
|
|
@@ -372,11 +379,18 @@ async function typeahead(options) {
|
|
|
372
379
|
const items = Array.isArray(raw?.elements)
|
|
373
380
|
? raw.elements.map((it) => ({
|
|
374
381
|
id: String(it?.id ?? it?.backendId ?? ''),
|
|
375
|
-
text: it
|
|
382
|
+
text: it.displayValue ??
|
|
383
|
+
it.headline?.text ??
|
|
384
|
+
it.text ??
|
|
385
|
+
it.name ??
|
|
386
|
+
it.label ??
|
|
387
|
+
''
|
|
376
388
|
}))
|
|
377
389
|
: [];
|
|
378
|
-
const
|
|
379
|
-
const
|
|
390
|
+
const rtype = (raw && typeof raw === 'object') ? raw : undefined;
|
|
391
|
+
const pagingVal2 = rtype && 'paging' in rtype ? rtype.paging : undefined;
|
|
392
|
+
const paging2 = (pagingVal2 && typeof pagingVal2 === 'object') ? pagingVal2 : undefined;
|
|
393
|
+
const result = { items, page: { start: Number(paging2?.start ?? start), count: Number(paging2?.count ?? count), total: paging2?.total } };
|
|
380
394
|
typeaheadCache.set(cacheKey, { data: result, ts: Date.now() });
|
|
381
395
|
try {
|
|
382
396
|
(0, logger_1.log)('info', 'api.ok', { operation: 'typeahead', selector: { type, query }, count: items.length });
|
|
@@ -385,6 +399,69 @@ async function typeahead(options) {
|
|
|
385
399
|
return result;
|
|
386
400
|
}
|
|
387
401
|
// ---------------------------------------------------------------------------
|
|
402
|
+
// Static Filters (Years) - LinkedIn has fixed ranges, no need for API calls
|
|
403
|
+
// ---------------------------------------------------------------------------
|
|
404
|
+
exports.YEARS_AT_COMPANY_OPTIONS = [
|
|
405
|
+
{ id: 1, text: "Less than 1 year", displayValue: "< 1 year" },
|
|
406
|
+
{ id: 2, text: "1 to 2 years", displayValue: "1-2 years" },
|
|
407
|
+
{ id: 3, text: "3 to 5 years", displayValue: "3-5 years" },
|
|
408
|
+
{ id: 4, text: "6 to 10 years", displayValue: "6-10 years" },
|
|
409
|
+
{ id: 5, text: "More than 10 years", displayValue: "10+ years" },
|
|
410
|
+
];
|
|
411
|
+
exports.YEARS_IN_POSITION_OPTIONS = [
|
|
412
|
+
{ id: 1, text: "Less than 1 year", displayValue: "< 1 year" },
|
|
413
|
+
{ id: 2, text: "1 to 2 years", displayValue: "1-2 years" },
|
|
414
|
+
{ id: 3, text: "3 to 5 years", displayValue: "3-5 years" },
|
|
415
|
+
{ id: 4, text: "6 to 10 years", displayValue: "6-10 years" },
|
|
416
|
+
{ id: 5, text: "More than 10 years", displayValue: "10+ years" },
|
|
417
|
+
];
|
|
418
|
+
exports.YEARS_OF_EXPERIENCE_OPTIONS = [
|
|
419
|
+
{ id: 1, text: "Less than 1 year", displayValue: "< 1 year" },
|
|
420
|
+
{ id: 2, text: "1 to 2 years", displayValue: "1-2 years" },
|
|
421
|
+
{ id: 3, text: "3 to 5 years", displayValue: "3-5 years" },
|
|
422
|
+
{ id: 4, text: "6 to 10 years", displayValue: "6-10 years" },
|
|
423
|
+
{ id: 5, text: "More than 10 years", displayValue: "10+ years" },
|
|
424
|
+
];
|
|
425
|
+
/**
|
|
426
|
+
* Returns static years at company options (no API call needed)
|
|
427
|
+
*/
|
|
428
|
+
async function getYearsAtCompanyOptions() {
|
|
429
|
+
return {
|
|
430
|
+
items: exports.YEARS_AT_COMPANY_OPTIONS.map(opt => ({ id: String(opt.id), text: opt.displayValue })),
|
|
431
|
+
page: {
|
|
432
|
+
start: 0,
|
|
433
|
+
count: exports.YEARS_AT_COMPANY_OPTIONS.length,
|
|
434
|
+
total: exports.YEARS_AT_COMPANY_OPTIONS.length
|
|
435
|
+
}
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
/**
|
|
439
|
+
* Returns static years in position options (no API call needed)
|
|
440
|
+
*/
|
|
441
|
+
async function getYearsInPositionOptions() {
|
|
442
|
+
return {
|
|
443
|
+
items: exports.YEARS_IN_POSITION_OPTIONS.map(opt => ({ id: String(opt.id), text: opt.displayValue })),
|
|
444
|
+
page: {
|
|
445
|
+
start: 0,
|
|
446
|
+
count: exports.YEARS_IN_POSITION_OPTIONS.length,
|
|
447
|
+
total: exports.YEARS_IN_POSITION_OPTIONS.length
|
|
448
|
+
}
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
/**
|
|
452
|
+
* Returns static years of experience options (no API call needed)
|
|
453
|
+
*/
|
|
454
|
+
async function getYearsOfExperienceOptions() {
|
|
455
|
+
return {
|
|
456
|
+
items: exports.YEARS_OF_EXPERIENCE_OPTIONS.map(opt => ({ id: String(opt.id), text: opt.displayValue })),
|
|
457
|
+
page: {
|
|
458
|
+
start: 0,
|
|
459
|
+
count: exports.YEARS_OF_EXPERIENCE_OPTIONS.length,
|
|
460
|
+
total: exports.YEARS_OF_EXPERIENCE_OPTIONS.length
|
|
461
|
+
}
|
|
462
|
+
};
|
|
463
|
+
}
|
|
464
|
+
// ---------------------------------------------------------------------------
|
|
388
465
|
// Sales Navigator profile details
|
|
389
466
|
// ---------------------------------------------------------------------------
|
|
390
467
|
async function getSalesNavigatorProfileDetails(profileUrnOrId) {
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import type { Company } from '../types';
|
|
2
|
-
export declare function parseCompany(raw:
|
|
2
|
+
export declare function parseCompany(raw: unknown): Company;
|
|
@@ -3,18 +3,23 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.parseCompany = parseCompany;
|
|
4
4
|
const image_parser_1 = require("./image-parser");
|
|
5
5
|
function parseCompany(raw) {
|
|
6
|
+
const r = raw;
|
|
6
7
|
const company = {
|
|
7
|
-
companyId: String(
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
8
|
+
companyId: String(r?.id ?? '')
|
|
9
|
+
.replace(/^urn:li:(?:fsd_)?company:/i, '') ||
|
|
10
|
+
String(r?.entityUrn ?? '')
|
|
11
|
+
.replace(/^urn:li:(?:fsd_)?company:/i, ''),
|
|
12
|
+
universalName: r?.universalName,
|
|
13
|
+
name: r?.name,
|
|
14
|
+
websiteUrl: r?.websiteUrl,
|
|
15
|
+
sizeLabel: r?.employeeCountRange?.localizedName,
|
|
16
|
+
headquarters: r?.headquarterLocation?.defaultLocalizedName ||
|
|
17
|
+
r?.headquarter?.defaultLocalizedName,
|
|
13
18
|
logoUrl: undefined,
|
|
14
19
|
coverUrl: undefined,
|
|
15
20
|
};
|
|
16
|
-
const logoVector =
|
|
17
|
-
const coverVector =
|
|
21
|
+
const logoVector = r?.logo?.vectorImage || r?.logoV2?.vectorImage;
|
|
22
|
+
const coverVector = r?.coverPhoto?.vectorImage || r?.backgroundImage?.vectorImage;
|
|
18
23
|
const logoUrl = (0, image_parser_1.selectBestImageUrl)(logoVector);
|
|
19
24
|
const coverUrl = (0, image_parser_1.selectBestImageUrl)(coverVector);
|
|
20
25
|
if (logoUrl)
|
|
@@ -1 +1,10 @@
|
|
|
1
|
-
export
|
|
1
|
+
export type VectorArtifact = {
|
|
2
|
+
width?: number;
|
|
3
|
+
fileIdentifyingUrlPathSegment?: string;
|
|
4
|
+
url?: string;
|
|
5
|
+
};
|
|
6
|
+
export type VectorImage = {
|
|
7
|
+
rootUrl?: string;
|
|
8
|
+
artifacts?: VectorArtifact[];
|
|
9
|
+
};
|
|
10
|
+
export declare function selectBestImageUrl(vector: unknown): string | undefined;
|
|
@@ -2,14 +2,15 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.selectBestImageUrl = selectBestImageUrl;
|
|
4
4
|
function selectBestImageUrl(vector) {
|
|
5
|
-
|
|
5
|
+
const v = vector;
|
|
6
|
+
if (!v || !Array.isArray(v.artifacts) || v.artifacts.length === 0)
|
|
6
7
|
return undefined;
|
|
7
|
-
const best =
|
|
8
|
+
const best = v.artifacts.reduce((prev, curr) => ((curr?.width || 0) > (prev?.width || 0) ? curr : prev), {});
|
|
8
9
|
const seg = best?.fileIdentifyingUrlPathSegment || best?.url;
|
|
9
10
|
if (!seg)
|
|
10
11
|
return undefined;
|
|
11
12
|
if (/^https?:\/\//i.test(seg))
|
|
12
13
|
return seg;
|
|
13
|
-
const root = String(
|
|
14
|
+
const root = String(v.rootUrl || '');
|
|
14
15
|
return root + seg;
|
|
15
16
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import type { LinkedInProfile } from '../types';
|
|
2
|
-
export declare function parseFullProfile(rawResponse:
|
|
2
|
+
export declare function parseFullProfile(rawResponse: unknown, vanity: string): LinkedInProfile;
|
|
@@ -3,7 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.parseFullProfile = parseFullProfile;
|
|
4
4
|
const image_parser_1 = require("./image-parser");
|
|
5
5
|
function parseFullProfile(rawResponse, vanity) {
|
|
6
|
-
const
|
|
6
|
+
const rr = rawResponse;
|
|
7
|
+
const included = Array.isArray(rr?.included) ? rr.included : [];
|
|
7
8
|
const identity = included.find((it) => String(it?.$type || '').includes('identity.profile.Profile')) || {};
|
|
8
9
|
const profile = {
|
|
9
10
|
vanity,
|
|
@@ -33,8 +34,9 @@ function parseFullProfile(rawResponse, vanity) {
|
|
|
33
34
|
return obj;
|
|
34
35
|
if (typeof obj !== 'object')
|
|
35
36
|
return undefined;
|
|
36
|
-
|
|
37
|
-
|
|
37
|
+
const rec = obj;
|
|
38
|
+
for (const k of Object.keys(rec)) {
|
|
39
|
+
const v = rec[k];
|
|
38
40
|
if (typeof v === 'string' && /urn:li:(?:fsd_)?company:/i.test(v))
|
|
39
41
|
return v;
|
|
40
42
|
if (v && typeof v === 'object') {
|
|
@@ -46,25 +48,26 @@ function parseFullProfile(rawResponse, vanity) {
|
|
|
46
48
|
return undefined;
|
|
47
49
|
}
|
|
48
50
|
for (const item of included) {
|
|
49
|
-
const
|
|
51
|
+
const rec = item;
|
|
52
|
+
const urn = rec?.entityUrn || '';
|
|
50
53
|
if (!urn.includes('fsd_profilePosition'))
|
|
51
54
|
continue;
|
|
52
55
|
const pos = {
|
|
53
|
-
title:
|
|
54
|
-
companyName:
|
|
55
|
-
description:
|
|
56
|
-
isCurrent: !
|
|
57
|
-
startYear:
|
|
58
|
-
startMonth:
|
|
59
|
-
endYear:
|
|
60
|
-
endMonth:
|
|
56
|
+
title: rec?.title,
|
|
57
|
+
companyName: rec?.companyName,
|
|
58
|
+
description: rec?.description,
|
|
59
|
+
isCurrent: !rec?.timePeriod?.endDate,
|
|
60
|
+
startYear: rec?.timePeriod?.startDate?.year,
|
|
61
|
+
startMonth: rec?.timePeriod?.startDate?.month,
|
|
62
|
+
endYear: rec?.timePeriod?.endDate?.year,
|
|
63
|
+
endMonth: rec?.timePeriod?.endDate?.month,
|
|
61
64
|
};
|
|
62
65
|
// Try to extract company URN and numeric id robustly
|
|
63
66
|
const candUrns = [
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
67
|
+
rec?.companyUrn,
|
|
68
|
+
rec?.company?.entityUrn,
|
|
69
|
+
rec?.company?.companyUrn,
|
|
70
|
+
rec?.companyUrnV2,
|
|
68
71
|
].filter(Boolean);
|
|
69
72
|
const foundUrn = candUrns.find(u => /urn:li:(?:fsd_)?company:/i.test(u)) || deepFindCompanyUrn(item);
|
|
70
73
|
if (foundUrn) {
|
|
@@ -77,17 +80,18 @@ function parseFullProfile(rawResponse, vanity) {
|
|
|
77
80
|
}
|
|
78
81
|
// Educations
|
|
79
82
|
for (const item of included) {
|
|
80
|
-
const
|
|
83
|
+
const rec = item;
|
|
84
|
+
const urn = rec?.entityUrn || '';
|
|
81
85
|
if (!urn.includes('fsd_profileEducation'))
|
|
82
86
|
continue;
|
|
83
87
|
const edu = {
|
|
84
|
-
schoolName:
|
|
85
|
-
degree:
|
|
86
|
-
fieldOfStudy:
|
|
87
|
-
startYear:
|
|
88
|
-
startMonth:
|
|
89
|
-
endYear:
|
|
90
|
-
endMonth:
|
|
88
|
+
schoolName: rec?.schoolName,
|
|
89
|
+
degree: rec?.degreeName,
|
|
90
|
+
fieldOfStudy: rec?.fieldOfStudy,
|
|
91
|
+
startYear: rec?.timePeriod?.startDate?.year,
|
|
92
|
+
startMonth: rec?.timePeriod?.startDate?.month,
|
|
93
|
+
endYear: rec?.timePeriod?.endDate?.year,
|
|
94
|
+
endMonth: rec?.timePeriod?.endDate?.month,
|
|
91
95
|
};
|
|
92
96
|
profile.educations.push(edu);
|
|
93
97
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import type { SalesLeadSearchResult } from '../types';
|
|
2
|
-
export declare function parseSalesSearchResults(rawResponse:
|
|
2
|
+
export declare function parseSalesSearchResults(rawResponse: unknown): SalesLeadSearchResult[];
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.parseSalesSearchResults = parseSalesSearchResults;
|
|
4
4
|
function parseSalesSearchResults(rawResponse) {
|
|
5
|
-
const
|
|
5
|
+
const rr = rawResponse;
|
|
6
|
+
const elements = Array.isArray(rr?.elements) ? rr?.elements : [];
|
|
6
7
|
const results = [];
|
|
7
8
|
for (const el of elements) {
|
|
8
9
|
const name = [el?.firstName, el?.lastName].filter(Boolean).join(' ').trim() || undefined;
|
package/dist/utils/logger.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export type LogLevel = 'debug' | 'info' | 'warn' | 'error';
|
|
2
|
-
export declare function log(level: LogLevel, message: string, data?:
|
|
2
|
+
export declare function log(level: LogLevel, message: string, data?: unknown): void;
|
package/dist/utils/metrics.d.ts
CHANGED
|
@@ -19,4 +19,7 @@ export interface Metrics {
|
|
|
19
19
|
typeaheadRequests: number;
|
|
20
20
|
}
|
|
21
21
|
export declare function incrementMetric(key: keyof Metrics, by?: number): void;
|
|
22
|
-
export
|
|
22
|
+
export interface MetricsSnapshot extends Metrics {
|
|
23
|
+
requestHistorySize: number;
|
|
24
|
+
}
|
|
25
|
+
export declare function getSnapshot(): MetricsSnapshot;
|
package/dist/utils/metrics.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import type { SalesSearchFilters } from '../types';
|
|
2
|
-
export declare function stableStringify(obj:
|
|
2
|
+
export declare function stableStringify(obj: unknown): string;
|
|
3
3
|
export declare function buildFilterSignature(filters?: SalesSearchFilters, rawQuery?: string): string | undefined;
|
|
4
4
|
export declare function buildLeadSearchQuery(keywords: string, filters?: SalesSearchFilters): string;
|
|
@@ -9,8 +9,9 @@ function stableStringify(obj) {
|
|
|
9
9
|
return JSON.stringify(obj);
|
|
10
10
|
if (Array.isArray(obj))
|
|
11
11
|
return '[' + obj.map((v) => stableStringify(v)).join(',') + ']';
|
|
12
|
-
const
|
|
13
|
-
|
|
12
|
+
const rec = obj;
|
|
13
|
+
const keys = Object.keys(rec).sort();
|
|
14
|
+
return '{' + keys.map((k) => JSON.stringify(k) + ':' + stableStringify(rec[k])).join(',') + '}';
|
|
14
15
|
}
|
|
15
16
|
function buildFilterSignature(filters, rawQuery) {
|
|
16
17
|
if (rawQuery)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "linkedin-secret-sauce",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Private LinkedIn Sales Navigator client with automatic cookie management",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -15,9 +15,11 @@
|
|
|
15
15
|
"search": "node scripts/rg-fast.mjs",
|
|
16
16
|
"rg:fast": "node scripts/rg-fast.mjs",
|
|
17
17
|
"build": "tsc -p tsconfig.json",
|
|
18
|
+
"lint": "eslint \"src/**/*.ts\" --max-warnings=0",
|
|
19
|
+
"lint:fix": "eslint \"src/**/*.ts\" --fix",
|
|
18
20
|
"dev": "tsc -w -p tsconfig.json",
|
|
19
21
|
"test": "vitest run",
|
|
20
|
-
"
|
|
22
|
+
"_prepare": "husky",
|
|
21
23
|
"prepublishOnly": "npm run build",
|
|
22
24
|
"release:patch": "npm version patch && git push --follow-tags",
|
|
23
25
|
"release:minor": "npm version minor && git push --follow-tags",
|
|
@@ -43,12 +45,14 @@
|
|
|
43
45
|
},
|
|
44
46
|
"homepage": "https://github.com/enerage/LinkedInSecretSauce#readme",
|
|
45
47
|
"devDependencies": {
|
|
46
|
-
"@types/node": "^20.11.0",
|
|
47
|
-
"typescript": "^5.3.3",
|
|
48
|
-
"vitest": "^1.6.0",
|
|
49
48
|
"@commitlint/cli": "^19.4.0",
|
|
50
49
|
"@commitlint/config-conventional": "^19.4.0",
|
|
51
|
-
"
|
|
50
|
+
"@types/node": "^20.11.0",
|
|
51
|
+
"@typescript-eslint/eslint-plugin": "^8.9.0",
|
|
52
|
+
"@typescript-eslint/parser": "^8.9.0",
|
|
53
|
+
"eslint": "^9.12.0",
|
|
54
|
+
"husky": "^9.0.11",
|
|
55
|
+
"typescript": "^5.9.3",
|
|
56
|
+
"vitest": "^1.6.0"
|
|
52
57
|
}
|
|
53
58
|
}
|
|
54
|
-
|