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 CHANGED
@@ -11,7 +11,6 @@ function initializeLinkedInClient(config) {
11
11
  }
12
12
  // Validate URL format
13
13
  try {
14
- // eslint-disable-next-line no-new
15
14
  new URL(config.cosiallApiUrl);
16
15
  }
17
16
  catch {
@@ -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
- (0, logger_1.log)('warn', 'cookiePool.refreshFailed', { error: e?.message });
92
+ const err = e;
93
+ (0, logger_1.log)('warn', 'cookiePool.refreshFailed', { error: err?.message });
93
94
  }
94
95
  }, interval);
95
96
  }
@@ -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
- return data.map((item) => ({
33
- accountId: item.accountId,
34
- cookies: Array.isArray(item.cookies) ? item.cookies : [],
35
- expiresAt: item.expiresAt,
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
  }
@@ -1,7 +1,7 @@
1
1
  export interface LinkedInRequestOptions {
2
2
  url: string;
3
3
  method?: 'GET' | 'POST';
4
- body?: any;
4
+ body?: unknown;
5
5
  headers?: Record<string, string>;
6
6
  }
7
7
  export declare function executeLinkedInRequest<T>(options: LinkedInRequestOptions, _operationName: string): Promise<T>;
@@ -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
- res = await fetch(options.url, {
74
+ const init = {
75
75
  method: options.method ?? 'GET',
76
76
  headers,
77
- body: options.body ? JSON.stringify(options.body) : undefined,
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 code = err?.code || err?.cause?.code || 'FETCH_FAILED';
82
- (0, logger_1.log)('error', 'http.networkError', { accountId, url: options.url, code, message: String(err?.message || err) });
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(err?.message || err) });
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);
@@ -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>;
@@ -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 paging = raw?.paging ?? { start, count };
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
- // eslint-disable-next-line no-constant-condition
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 id = raw?.elements?.[0]?.id ?? raw?.elements?.[0]?.entityUrn ?? undefined;
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?.displayValue ?? it?.headline?.text ?? it?.text ?? it?.name ?? it?.label ?? ''
382
+ text: it.displayValue ??
383
+ it.headline?.text ??
384
+ it.text ??
385
+ it.name ??
386
+ it.label ??
387
+ ''
376
388
  }))
377
389
  : [];
378
- const paging = raw?.paging ?? {};
379
- const result = { items, page: { start: Number(paging?.start ?? start), count: Number(paging?.count ?? count), total: paging?.total } };
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: any): Company;
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(raw?.id ?? '').replace(/^urn:li:(?:fsd_)?company:/i, '') || String(raw?.entityUrn ?? '').replace(/^urn:li:(?:fsd_)?company:/i, ''),
8
- universalName: raw?.universalName,
9
- name: raw?.name,
10
- websiteUrl: raw?.websiteUrl,
11
- sizeLabel: raw?.employeeCountRange?.localizedName,
12
- headquarters: raw?.headquarterLocation?.defaultLocalizedName || raw?.headquarter?.defaultLocalizedName,
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 = raw?.logo?.vectorImage || raw?.logoV2?.vectorImage;
17
- const coverVector = raw?.coverPhoto?.vectorImage || raw?.backgroundImage?.vectorImage;
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 declare function selectBestImageUrl(vector: any): string | undefined;
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
- if (!vector || !Array.isArray(vector.artifacts) || vector.artifacts.length === 0)
5
+ const v = vector;
6
+ if (!v || !Array.isArray(v.artifacts) || v.artifacts.length === 0)
6
7
  return undefined;
7
- const best = vector.artifacts.reduce((prev, curr) => ((curr?.width || 0) > (prev?.width || 0) ? curr : prev), {});
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(vector.rootUrl || '');
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: any, vanity: string): LinkedInProfile;
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 included = Array.isArray(rawResponse?.included) ? rawResponse.included : [];
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
- for (const k of Object.keys(obj)) {
37
- const v = obj[k];
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 urn = item?.entityUrn || '';
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: item?.title,
54
- companyName: item?.companyName,
55
- description: item?.description,
56
- isCurrent: !item?.timePeriod?.endDate,
57
- startYear: item?.timePeriod?.startDate?.year,
58
- startMonth: item?.timePeriod?.startDate?.month,
59
- endYear: item?.timePeriod?.endDate?.year,
60
- endMonth: item?.timePeriod?.endDate?.month,
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
- item?.companyUrn,
65
- item?.company?.entityUrn,
66
- item?.company?.companyUrn,
67
- item?.companyUrnV2,
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 urn = item?.entityUrn || '';
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: item?.schoolName,
85
- degree: item?.degreeName,
86
- fieldOfStudy: item?.fieldOfStudy,
87
- startYear: item?.timePeriod?.startDate?.year,
88
- startMonth: item?.timePeriod?.startDate?.month,
89
- endYear: item?.timePeriod?.endDate?.year,
90
- endMonth: item?.timePeriod?.endDate?.month,
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: any): SalesLeadSearchResult[];
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 elements = Array.isArray(rawResponse?.elements) ? rawResponse.elements : [];
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;
@@ -1,2 +1,2 @@
1
1
  export type LogLevel = 'debug' | 'info' | 'warn' | 'error';
2
- export declare function log(level: LogLevel, message: string, data?: any): void;
2
+ export declare function log(level: LogLevel, message: string, data?: unknown): void;
@@ -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 declare function getSnapshot(): Metrics;
22
+ export interface MetricsSnapshot extends Metrics {
23
+ requestHistorySize: number;
24
+ }
25
+ export declare function getSnapshot(): MetricsSnapshot;
@@ -24,7 +24,6 @@ const metrics = {
24
24
  typeaheadRequests: 0,
25
25
  };
26
26
  function incrementMetric(key, by = 1) {
27
- // @ts-ignore - index signature not declared, but keys are enforced by type
28
27
  metrics[key] = (metrics[key] || 0) + by;
29
28
  }
30
29
  function getSnapshot() {
@@ -1,4 +1,4 @@
1
1
  import type { SalesSearchFilters } from '../types';
2
- export declare function stableStringify(obj: any): string;
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 keys = Object.keys(obj).sort();
13
- return '{' + keys.map((k) => JSON.stringify(k) + ':' + stableStringify(obj[k])).join(',') + '}';
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.1.2",
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
- "prepare": "husky",
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
- "husky": "^9.0.11"
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
-