linkedin-secret-sauce 0.3.29 → 0.4.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.
@@ -45,11 +45,30 @@ function sleep(ms) {
45
45
  return new Promise((resolve) => setTimeout(resolve, ms));
46
46
  }
47
47
  function isRetryableStatus(status) {
48
- return status === 429 || status === 500 || status === 502 || status === 503 || status === 504;
48
+ return (status === 429 ||
49
+ status === 500 ||
50
+ status === 502 ||
51
+ status === 503 ||
52
+ status === 504);
53
+ }
54
+ /**
55
+ * Redact sensitive parts of URL for safe logging.
56
+ * Preserves hostname and path but removes query parameters which may contain session info.
57
+ */
58
+ function redactUrlForLogging(url) {
59
+ try {
60
+ const parsed = new URL(url);
61
+ // Keep only host and pathname, remove query params
62
+ return `${parsed.host}${parsed.pathname}`;
63
+ }
64
+ catch {
65
+ // If URL parsing fails, just return a safe placeholder
66
+ return "[invalid-url]";
67
+ }
49
68
  }
50
69
  async function executeLinkedInRequest(options, _operationName) {
51
70
  const config = (0, config_1.getConfig)();
52
- const op = _operationName || 'request';
71
+ const op = _operationName || "request";
53
72
  const perAccountAttempts = (config.maxRetries ?? 0) + 1;
54
73
  const delay = config.retryDelayMs ?? 700;
55
74
  // Setup proxy env if configured
@@ -62,7 +81,7 @@ async function executeLinkedInRequest(options, _operationName) {
62
81
  process.env.HTTP_PROXY = proxyUrl;
63
82
  process.env.HTTPS_PROXY = proxyUrl;
64
83
  proxySet = true;
65
- (0, logger_1.log)('info', 'proxy.set', { host: masked });
84
+ (0, logger_1.log)("info", "proxy.set", { host: masked });
66
85
  }
67
86
  // Phase 1.4: Try ALL available accounts (not just 3)
68
87
  const health = (0, cookie_pool_1.getCookiePoolHealth)();
@@ -77,8 +96,10 @@ async function executeLinkedInRequest(options, _operationName) {
77
96
  catch (err) {
78
97
  const e = err;
79
98
  // Phase 1.3: If NO_VALID_ACCOUNTS, try force refresh once
80
- if (e?.code === 'NO_VALID_ACCOUNTS' && accountsTriedCount > 0) {
81
- (0, logger_1.log)('warn', 'http.noValidAccounts.forceRefresh', { accountsTriedCount });
99
+ if (e?.code === "NO_VALID_ACCOUNTS" && accountsTriedCount > 0) {
100
+ (0, logger_1.log)("warn", "http.noValidAccounts.forceRefresh", {
101
+ accountsTriedCount,
102
+ });
82
103
  try {
83
104
  await (0, cookie_pool_1.forceRefreshCookies)();
84
105
  // Retry selection after refresh
@@ -86,31 +107,43 @@ async function executeLinkedInRequest(options, _operationName) {
86
107
  }
87
108
  catch (refreshErr) {
88
109
  const re = refreshErr;
89
- (0, logger_1.log)('error', 'http.forceRefreshFailed', { error: re?.message });
110
+ (0, logger_1.log)("error", "http.forceRefreshFailed", { error: re?.message });
90
111
  // Report to Sentry
91
112
  try {
92
- const { reportCriticalError } = await Promise.resolve().then(() => __importStar(require('./utils/sentry')));
93
- reportCriticalError('All accounts exhausted, force refresh failed', {
113
+ const { reportCriticalError } = await Promise.resolve().then(() => __importStar(require("./utils/sentry")));
114
+ reportCriticalError("All accounts exhausted, force refresh failed", {
94
115
  error: re?.message,
95
116
  accountsTriedCount,
96
- tags: { component: 'http-client', severity: 'critical' },
117
+ tags: { component: "http-client", severity: "critical" },
97
118
  });
98
119
  }
99
120
  catch { }
100
121
  }
101
122
  }
102
- if (!selection && process.env.NODE_ENV !== 'test')
123
+ if (!selection && process.env.NODE_ENV !== "test")
103
124
  throw err;
104
125
  }
105
126
  if (!selection || !selection.accountId) {
106
- if (process.env.NODE_ENV === 'test') {
127
+ if (process.env.NODE_ENV === "test") {
107
128
  const fallback = rotation === 0
108
- ? { accountId: 'acc1', cookies: [{ name: 'li_at', value: 'A' }, { name: 'JSESSIONID', value: '"csrf1"' }] }
109
- : { accountId: 'acc2', cookies: [{ name: 'li_at', value: 'B' }, { name: 'JSESSIONID', value: '"csrf2"' }] };
129
+ ? {
130
+ accountId: "acc1",
131
+ cookies: [
132
+ { name: "li_at", value: "A" },
133
+ { name: "JSESSIONID", value: '"csrf1"' },
134
+ ],
135
+ }
136
+ : {
137
+ accountId: "acc2",
138
+ cookies: [
139
+ { name: "li_at", value: "B" },
140
+ { name: "JSESSIONID", value: '"csrf2"' },
141
+ ],
142
+ };
110
143
  selection = fallback;
111
144
  }
112
145
  else {
113
- throw new errors_1.LinkedInClientError('No LinkedIn accounts available', 'NO_ACCOUNTS', 503);
146
+ throw new errors_1.LinkedInClientError("No LinkedIn accounts available", "NO_ACCOUNTS", 503);
114
147
  }
115
148
  }
116
149
  const { accountId, cookies } = selection;
@@ -118,51 +151,79 @@ async function executeLinkedInRequest(options, _operationName) {
118
151
  const csrf = (0, cookie_pool_1.extractCsrfToken)(cookies);
119
152
  const cookieHeader = (0, cookie_pool_1.buildCookieHeader)(cookies);
120
153
  // Choose header profile based on Sales vs Voyager context
121
- const isSales = /\/sales\//i.test(String(options.headers?.Referer || ''))
122
- || /https:\/\/www\.linkedin\.com\/sales-api/i.test(options.url);
154
+ const isSales = /\/sales\//i.test(String(options.headers?.Referer || "")) ||
155
+ /https:\/\/www\.linkedin\.com\/sales-api/i.test(options.url);
123
156
  const baseHeaders = isSales
124
- ? (0, linkedin_config_1.buildSalesHeaders)({ cookiesHeader: cookieHeader, csrf, referer: options.headers?.Referer })
125
- : (0, linkedin_config_1.buildVoyagerHeaders)({ cookiesHeader: cookieHeader, csrf, referer: options.headers?.Referer });
157
+ ? (0, linkedin_config_1.buildSalesHeaders)({
158
+ cookiesHeader: cookieHeader,
159
+ csrf,
160
+ referer: options.headers?.Referer,
161
+ })
162
+ : (0, linkedin_config_1.buildVoyagerHeaders)({
163
+ cookiesHeader: cookieHeader,
164
+ csrf,
165
+ referer: options.headers?.Referer,
166
+ });
126
167
  const headers = {
127
168
  ...baseHeaders,
128
169
  ...(options.headers || {}),
129
170
  };
130
171
  for (let attempt = 0; attempt < perAccountAttempts; attempt++) {
131
172
  const started = Date.now();
132
- (0, logger_1.log)('debug', 'http.attempt', { accountId, attempt: attempt + 1, method: options.method ?? 'GET', url: options.url });
173
+ (0, logger_1.log)("debug", "http.attempt", {
174
+ accountId,
175
+ attempt: attempt + 1,
176
+ method: options.method ?? "GET",
177
+ url: redactUrlForLogging(options.url),
178
+ });
133
179
  let res;
134
180
  try {
135
181
  const init = {
136
- method: options.method ?? 'GET',
182
+ method: options.method ?? "GET",
137
183
  headers,
138
184
  };
139
185
  if (options.body !== undefined) {
140
- init.body = typeof options.body === 'string' ? options.body : JSON.stringify(options.body);
186
+ init.body =
187
+ typeof options.body === "string"
188
+ ? options.body
189
+ : JSON.stringify(options.body);
141
190
  }
142
191
  res = (await fetch(options.url, init));
143
192
  }
144
193
  catch (err) {
145
194
  const e = err;
146
- const code = e?.code || e?.cause?.code || 'FETCH_FAILED';
147
- (0, logger_1.log)('error', 'http.networkError', { accountId, url: options.url, code, message: String(e?.message || err) });
148
- (0, metrics_1.incrementMetric)('httpFailures');
195
+ const code = e?.code || e?.cause?.code || "FETCH_FAILED";
196
+ (0, logger_1.log)("error", "http.networkError", {
197
+ accountId,
198
+ url: redactUrlForLogging(options.url),
199
+ code,
200
+ message: String(e?.message || err),
201
+ });
202
+ (0, metrics_1.incrementMetric)("httpFailures");
149
203
  try {
150
- (0, request_history_1.recordRequest)({ operation: op, selector: options.url, status: 0, durationMs: Date.now() - started, accountId, errorMessage: String(e?.message || err) });
204
+ (0, request_history_1.recordRequest)({
205
+ operation: op,
206
+ selector: options.url,
207
+ status: 0,
208
+ durationMs: Date.now() - started,
209
+ accountId,
210
+ errorMessage: String(e?.message || err),
211
+ });
151
212
  }
152
213
  catch { }
153
214
  // Report network errors to Sentry
154
215
  try {
155
- const { reportWarningToSentry } = await Promise.resolve().then(() => __importStar(require('./utils/sentry')));
156
- reportWarningToSentry('Network error during LinkedIn request', {
216
+ const { reportWarningToSentry } = await Promise.resolve().then(() => __importStar(require("./utils/sentry")));
217
+ reportWarningToSentry("Network error during LinkedIn request", {
157
218
  code,
158
- url: options.url,
219
+ url: redactUrlForLogging(options.url),
159
220
  accountId,
160
221
  error: String(e?.message || err),
161
- tags: { component: 'http-client', severity: 'high' },
222
+ tags: { component: "http-client", severity: "high" },
162
223
  });
163
224
  }
164
225
  catch { }
165
- lastError = new errors_1.LinkedInClientError('LinkedIn fetch failed', 'REQUEST_FAILED', 0, accountId);
226
+ lastError = new errors_1.LinkedInClientError("LinkedIn fetch failed", "REQUEST_FAILED", 0, accountId);
166
227
  break; // rotate to next account
167
228
  }
168
229
  if (res.ok) {
@@ -171,8 +232,11 @@ async function executeLinkedInRequest(options, _operationName) {
171
232
  (0, cookie_pool_1.reportAccountSuccess)(accountId);
172
233
  }
173
234
  catch { }
174
- (0, logger_1.log)('info', 'http.success', { accountId, url: options.url });
175
- (0, metrics_1.incrementMetric)('httpSuccess');
235
+ (0, logger_1.log)("info", "http.success", {
236
+ accountId,
237
+ url: redactUrlForLogging(options.url),
238
+ });
239
+ (0, metrics_1.incrementMetric)("httpSuccess");
176
240
  try {
177
241
  (0, request_history_1.recordRequest)({
178
242
  operation: op,
@@ -192,10 +256,10 @@ async function executeLinkedInRequest(options, _operationName) {
192
256
  (0, cookie_pool_1.reportAccountFailure)(accountId, true);
193
257
  }
194
258
  catch { }
195
- (0, logger_1.log)('warn', 'http.rotateOnAuth', { accountId, status });
196
- (0, metrics_1.incrementMetric)('authErrors');
197
- (0, metrics_1.incrementMetric)('httpFailures');
198
- lastError = new errors_1.LinkedInClientError(`LinkedIn request failed: ${status}`, 'REQUEST_FAILED', status, accountId);
259
+ (0, logger_1.log)("warn", "http.rotateOnAuth", { accountId, status });
260
+ (0, metrics_1.incrementMetric)("authErrors");
261
+ (0, metrics_1.incrementMetric)("httpFailures");
262
+ lastError = new errors_1.LinkedInClientError(`LinkedIn request failed: ${status}`, "REQUEST_FAILED", status, accountId);
199
263
  try {
200
264
  (0, request_history_1.recordRequest)({
201
265
  operation: op,
@@ -203,7 +267,7 @@ async function executeLinkedInRequest(options, _operationName) {
203
267
  status,
204
268
  durationMs: Date.now() - started,
205
269
  accountId,
206
- errorMessage: 'rotate',
270
+ errorMessage: "rotate",
207
271
  });
208
272
  }
209
273
  catch { }
@@ -212,38 +276,54 @@ async function executeLinkedInRequest(options, _operationName) {
212
276
  // Phase 1.5: Proxy fallback on 502/503/504 if enabled
213
277
  const isProxyError = status === 502 || status === 503 || status === 504;
214
278
  const fallbackEnabled = config.fallbackWithoutProxyOnError ?? false;
215
- if (isProxyError && fallbackEnabled && proxySet && attempt < perAccountAttempts - 1) {
216
- (0, logger_1.log)('warn', 'http.proxyFallback', { accountId, status, attempt: attempt + 1 });
279
+ if (isProxyError &&
280
+ fallbackEnabled &&
281
+ proxySet &&
282
+ attempt < perAccountAttempts - 1) {
283
+ (0, logger_1.log)("warn", "http.proxyFallback", {
284
+ accountId,
285
+ status,
286
+ attempt: attempt + 1,
287
+ });
217
288
  // Temporarily disable proxy for this retry
218
289
  delete process.env.HTTP_PROXY;
219
290
  delete process.env.HTTPS_PROXY;
220
291
  proxySet = false;
221
292
  // Report to Sentry
222
293
  try {
223
- const { reportWarningToSentry } = await Promise.resolve().then(() => __importStar(require('./utils/sentry')));
224
- reportWarningToSentry('Proxy error, retrying without proxy', {
294
+ const { reportWarningToSentry } = await Promise.resolve().then(() => __importStar(require("./utils/sentry")));
295
+ reportWarningToSentry("Proxy error, retrying without proxy", {
225
296
  status,
226
- url: options.url,
297
+ url: redactUrlForLogging(options.url),
227
298
  accountId,
228
- tags: { component: 'http-client', severity: 'medium' },
299
+ tags: { component: "http-client", severity: "medium" },
229
300
  });
230
301
  }
231
302
  catch { }
232
- (0, metrics_1.incrementMetric)('httpRetries');
303
+ (0, metrics_1.incrementMetric)("httpRetries");
233
304
  await sleep(delay);
234
305
  continue;
235
306
  }
236
307
  // Retryable 5xx on same account
237
- if ((status >= 500 && status < 600) && attempt < perAccountAttempts - 1) {
238
- (0, logger_1.log)('debug', 'http.retry', { accountId, status, attempt: attempt + 1, nextDelayMs: delay });
239
- (0, metrics_1.incrementMetric)('httpRetries');
308
+ if (status >= 500 && status < 600 && attempt < perAccountAttempts - 1) {
309
+ (0, logger_1.log)("debug", "http.retry", {
310
+ accountId,
311
+ status,
312
+ attempt: attempt + 1,
313
+ nextDelayMs: delay,
314
+ });
315
+ (0, metrics_1.incrementMetric)("httpRetries");
240
316
  await sleep(delay);
241
317
  continue;
242
318
  }
243
319
  // Non-retryable: throw
244
- const err = new errors_1.LinkedInClientError(`LinkedIn request failed: ${status}`, 'REQUEST_FAILED', status, accountId);
245
- (0, metrics_1.incrementMetric)('httpFailures');
246
- (0, logger_1.log)(status >= 500 ? 'warn' : 'error', 'http.fail', { accountId, status, url: options.url });
320
+ const err = new errors_1.LinkedInClientError(`LinkedIn request failed: ${status}`, "REQUEST_FAILED", status, accountId);
321
+ (0, metrics_1.incrementMetric)("httpFailures");
322
+ (0, logger_1.log)(status >= 500 ? "warn" : "error", "http.fail", {
323
+ accountId,
324
+ status,
325
+ url: redactUrlForLogging(options.url),
326
+ });
247
327
  try {
248
328
  (0, request_history_1.recordRequest)({
249
329
  operation: op,
@@ -260,21 +340,24 @@ async function executeLinkedInRequest(options, _operationName) {
260
340
  }
261
341
  // Exhausted all accounts
262
342
  const errMsg = `All ${accountsTriedCount} LinkedIn accounts exhausted`;
263
- (0, logger_1.log)('error', 'http.allAccountsExhausted', { accountsTriedCount, maxRotations });
343
+ (0, logger_1.log)("error", "http.allAccountsExhausted", {
344
+ accountsTriedCount,
345
+ maxRotations,
346
+ });
264
347
  // Report critical failure to Sentry
265
348
  try {
266
- const { reportCriticalError } = await Promise.resolve().then(() => __importStar(require('./utils/sentry')));
349
+ const { reportCriticalError } = await Promise.resolve().then(() => __importStar(require("./utils/sentry")));
267
350
  reportCriticalError(errMsg, {
268
351
  accountsTriedCount,
269
352
  maxRotations,
270
353
  lastError: lastError?.message,
271
- tags: { component: 'http-client', severity: 'critical' },
354
+ tags: { component: "http-client", severity: "critical" },
272
355
  });
273
356
  }
274
357
  catch { }
275
358
  if (lastError)
276
359
  throw lastError;
277
- throw new errors_1.LinkedInClientError(errMsg, 'ALL_ACCOUNTS_FAILED', 503);
360
+ throw new errors_1.LinkedInClientError(errMsg, "ALL_ACCOUNTS_FAILED", 503);
278
361
  }
279
362
  finally {
280
363
  // Restore proxy env
@@ -287,7 +370,7 @@ async function executeLinkedInRequest(options, _operationName) {
287
370
  delete process.env.HTTPS_PROXY;
288
371
  else
289
372
  process.env.HTTPS_PROXY = prevHttps;
290
- (0, logger_1.log)('info', 'proxy.restore', {});
373
+ (0, logger_1.log)("info", "proxy.restore", {});
291
374
  }
292
375
  }
293
376
  }
@@ -300,13 +383,13 @@ function parseProxyString(input) {
300
383
  if (/^\w+:\/\//.test(s)) {
301
384
  try {
302
385
  const u = new URL(s);
303
- if (u.protocol !== 'http:' && u.protocol !== 'https:') {
304
- throw new errors_1.LinkedInClientError(`Unsupported proxy protocol: ${u.protocol}`, 'INVALID_CONFIG', 400);
386
+ if (u.protocol !== "http:" && u.protocol !== "https:") {
387
+ throw new errors_1.LinkedInClientError(`Unsupported proxy protocol: ${u.protocol}`, "INVALID_CONFIG", 400);
305
388
  }
306
- const user = u.username || '';
307
- const pass = u.password || '';
389
+ const user = u.username || "";
390
+ const pass = u.password || "";
308
391
  const hostport = u.host; // includes brackets for IPv6
309
- const url = `${u.protocol}//${user ? `${user}:${pass}@` : ''}${hostport}`;
392
+ const url = `${u.protocol}//${user ? `${user}:${pass}@` : ""}${hostport}`;
310
393
  const masked = user ? `${hostport}:${user}:***` : hostport;
311
394
  return { url, masked };
312
395
  }
@@ -323,15 +406,15 @@ function parseProxyString(input) {
323
406
  const user = m[3];
324
407
  const pass = m[4];
325
408
  // Ensure brackets for IPv6 in URL
326
- const needsBrackets = host.includes(':') && !/^\[.*\]$/.test(host);
409
+ const needsBrackets = host.includes(":") && !/^\[.*\]$/.test(host);
327
410
  if (needsBrackets)
328
411
  host = `[${host}]`;
329
- const auth = user && pass ? `${user}:${pass}@` : '';
412
+ const auth = user && pass ? `${user}:${pass}@` : "";
330
413
  const url = `http://${auth}${host}:${port}`;
331
414
  const hostport = `${host}:${port}`;
332
415
  const masked = user && pass ? `${hostport}:${user}:***` : hostport;
333
416
  return { url, masked };
334
417
  }
335
418
  // Fallback: conservative
336
- return { url: s.includes('://') ? s : `http://${s}`, masked: '***' };
419
+ return { url: s.includes("://") ? s : `http://${s}`, masked: "***" };
337
420
  }
@@ -1,7 +1,79 @@
1
- import type { SalesSearchFilters } from './types';
2
- import type { LinkedInProfile, SalesLeadSearchResult, SearchSalesResult, TypeaheadResult, SalesNavigatorProfile, Company } from './types';
1
+ import type { SalesSearchFilters } from "./types";
2
+ import type { LinkedInProfile, SearchSalesResult, TypeaheadResult, SalesNavigatorProfile, Company } from "./types";
3
+ /**
4
+ * Fetches a LinkedIn profile by vanity URL (public identifier).
5
+ * Results are cached for the configured TTL (default: 15 minutes).
6
+ *
7
+ * @param vanity - The public identifier from LinkedIn URL (e.g., "johndoe" from linkedin.com/in/johndoe)
8
+ * @returns Parsed LinkedInProfile with positions, education, skills, and metadata
9
+ * @throws LinkedInClientError with code NOT_FOUND if profile doesn't exist
10
+ * @throws LinkedInClientError with code PARSE_ERROR if response cannot be parsed
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * const profile = await getProfileByVanity('johndoe');
15
+ * console.log(profile.firstName, profile.headline);
16
+ * ```
17
+ */
3
18
  export declare function getProfileByVanity(vanity: string): Promise<LinkedInProfile>;
19
+ /**
20
+ * Fetches a LinkedIn profile by FSD key or URN.
21
+ * Accepts multiple input formats and normalizes them.
22
+ * Results are cached for the configured TTL (default: 15 minutes).
23
+ *
24
+ * @param fsdKey - Profile identifier in any of these formats:
25
+ * - Bare key: "ABC123xyz"
26
+ * - FSD profile URN: "urn:li:fsd_profile:ABC123xyz"
27
+ * - Sales profile URN: "urn:li:fs_salesProfile:(ABC123xyz,NAME_SEARCH,abc)"
28
+ * @returns Parsed LinkedInProfile with positions, education, skills, and metadata
29
+ * @throws LinkedInClientError with code INVALID_INPUT if URN format is invalid
30
+ * @throws LinkedInClientError with code NOT_FOUND if profile doesn't exist
31
+ * @throws LinkedInClientError with code PARSE_ERROR if response cannot be parsed
32
+ *
33
+ * @example
34
+ * ```typescript
35
+ * // By bare key
36
+ * const profile = await getProfileByUrn('ABC123xyz');
37
+ *
38
+ * // By URN from search results
39
+ * const profile = await getProfileByUrn(searchResult.salesProfileUrn);
40
+ * ```
41
+ */
4
42
  export declare function getProfileByUrn(fsdKey: string): Promise<LinkedInProfile>;
43
+ /**
44
+ * Searches Sales Navigator for leads matching the given criteria.
45
+ * Supports pagination, advanced filters, and session-based account stickiness.
46
+ *
47
+ * @param keywords - Search keywords (max 2000 characters)
48
+ * @param options - Search configuration options
49
+ * @param options.start - Pagination offset (default: 0)
50
+ * @param options.count - Results per page (default: 25)
51
+ * @param options.decorationId - LinkedIn decoration ID for response format
52
+ * @param options.filters - Advanced search filters (title, company, geography, etc.)
53
+ * @param options.rawQuery - Raw query string (bypasses filter encoding)
54
+ * @param options.sessionId - Session ID for consistent pagination (same account across pages)
55
+ * @returns SearchSalesResult with items array, pagination info, and session metadata
56
+ * @throws LinkedInClientError with code INVALID_INPUT if keywords exceed max length
57
+ *
58
+ * @example
59
+ * ```typescript
60
+ * // Simple search
61
+ * const results = await searchSalesLeads('CEO');
62
+ *
63
+ * // Paginated search with session persistence
64
+ * const page1 = await searchSalesLeads('CEO', { start: 0, count: 25 });
65
+ * const sessionId = page1._meta?.sessionId;
66
+ * const page2 = await searchSalesLeads('CEO', { start: 25, count: 25, sessionId });
67
+ *
68
+ * // With filters
69
+ * const filtered = await searchSalesLeads('', {
70
+ * filters: {
71
+ * role: { seniority_ids: [8, 9, 10] }, // VP, CXO, Partner
72
+ * company: { headcount: { include: ['501-1000', '1001-5000'] } }
73
+ * }
74
+ * });
75
+ * ```
76
+ */
5
77
  export declare function searchSalesLeads(keywords: string, options?: {
6
78
  start?: number;
7
79
  count?: number;
@@ -9,20 +81,41 @@ export declare function searchSalesLeads(keywords: string, options?: {
9
81
  filters?: SalesSearchFilters;
10
82
  rawQuery?: string;
11
83
  sessionId?: string;
12
- }): Promise<SearchSalesResult | SalesLeadSearchResult[]>;
84
+ }): Promise<SearchSalesResult>;
13
85
  export declare function getProfilesBatch(vanities: string[], concurrency?: number): Promise<(LinkedInProfile | null)[]>;
14
86
  export declare function resolveCompanyUniversalName(universalName: string): Promise<{
15
87
  companyId?: string;
16
88
  }>;
89
+ /**
90
+ * Fetches a company by its numeric LinkedIn ID.
91
+ * Results are cached for the configured TTL (default: 10 minutes).
92
+ *
93
+ * @param companyId - Numeric company ID (e.g., "1234567")
94
+ * @returns Company object with name, description, size, headquarters, etc.
95
+ * @throws LinkedInClientError with code NOT_FOUND if company doesn't exist
96
+ *
97
+ * @example
98
+ * ```typescript
99
+ * const company = await getCompanyById('1234567');
100
+ * console.log(company.name, company.sizeLabel);
101
+ * ```
102
+ */
17
103
  export declare function getCompanyById(companyId: string): Promise<Company>;
18
104
  export declare function getCompanyByUrl(companyUrl: string): Promise<Company>;
105
+ /**
106
+ * Fetch multiple companies in parallel with controlled concurrency.
107
+ * @param companyIds - Array of company IDs (numeric strings)
108
+ * @param concurrency - Maximum parallel requests (default: 4, max: 16)
109
+ * @returns Array of Company objects (null for failed fetches, preserves order)
110
+ */
111
+ export declare function getCompaniesBatch(companyIds: string[], concurrency?: number): Promise<(Company | null)[]>;
19
112
  export declare function typeahead(options: {
20
113
  type: string;
21
114
  query?: string;
22
115
  start?: number;
23
116
  count?: number;
24
117
  }): Promise<TypeaheadResult>;
25
- export { YEARS_OPTIONS as YEARS_AT_COMPANY_OPTIONS, YEARS_OPTIONS as YEARS_IN_POSITION_OPTIONS, YEARS_OPTIONS as YEARS_OF_EXPERIENCE_OPTIONS, SENIORITY_OPTIONS, FUNCTION_OPTIONS, REGION_OPTIONS, LANGUAGE_OPTIONS, INDUSTRY_OPTIONS, COMPANY_SIZE_OPTIONS, } from './constants';
118
+ export { YEARS_OPTIONS as YEARS_AT_COMPANY_OPTIONS, YEARS_OPTIONS as YEARS_IN_POSITION_OPTIONS, YEARS_OPTIONS as YEARS_OF_EXPERIENCE_OPTIONS, SENIORITY_OPTIONS, FUNCTION_OPTIONS, REGION_OPTIONS, LANGUAGE_OPTIONS, INDUSTRY_OPTIONS, COMPANY_SIZE_OPTIONS, } from "./constants";
26
119
  /**
27
120
  * Returns static years at company options (no API call needed)
28
121
  */