linkedin-secret-sauce 0.3.8 → 0.3.10

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 CHANGED
@@ -221,6 +221,55 @@ Notes:
221
221
  - For scoped packages that should be public, add `--access public` (not needed here because the package name is unscoped).
222
222
  - The “Publish your first package” button on GitHub targets GitHub Packages (`npm publish --registry=https://npm.pkg.github.com/`), not npmjs.com.
223
223
 
224
+ ## Changelog
225
+
226
+ See [CHANGELOG.md](CHANGELOG.md) for release notes and upgrade guides.
227
+
228
+ **Latest:** v0.3.10 - Fixed `fsdKey` extraction from Sales Navigator URNs
229
+
230
+ ## Known Issues & Solutions
231
+
232
+ ### Sales Navigator Search Results
233
+
234
+ **Issue:** `fsdKey` field not populated in search results (fixed in v0.3.10)
235
+
236
+ If you're on an older version, you may need to manually extract `fsdKey`:
237
+ ```typescript
238
+ const results = await searchSalesLeads('query');
239
+ // For older versions (<0.3.10), fsdKey might be missing
240
+ // Solution: Update to v0.3.10+
241
+ ```
242
+
243
+ **Upgrade:**
244
+ ```bash
245
+ npm update linkedin-secret-sauce
246
+ # or
247
+ pnpm update linkedin-secret-sauce
248
+ ```
249
+
250
+ After upgrading to v0.3.10+, `fsdKey` will be correctly populated from URNs like:
251
+ ```
252
+ urn:li:fs_salesProfile:(ACwAAAAM3xgBU8jQ8zPMpR_WswldHDbBxoOu6p8,NAME_SEARCH,g2em)
253
+ ```
254
+
255
+ ### Profile Enrichment Pattern
256
+
257
+ For best results, use this two-step pattern:
258
+
259
+ ```typescript
260
+ // Step 1: Search (lightweight, returns many results quickly)
261
+ const searchResults = await searchSalesLeads('software engineer berlin');
262
+
263
+ // Step 2: Enrich selected results (slower, returns full data)
264
+ for (const result of searchResults.items) {
265
+ if (result.fsdKey) {
266
+ const fullProfile = await getProfileByUrn(result.fsdKey);
267
+ // Now you have positions[] and educations[] arrays
268
+ console.log(`${fullProfile.firstName} has ${fullProfile.positions.length} jobs`);
269
+ }
270
+ }
271
+ ```
272
+
224
273
 
225
274
  ## Playground (React + Vite)\n\nDev-only UI to exercise the library safely via a local server.\n\n- Start both server and client: \n - pnpm dev:playground\n- Or separately: \n - pnpm -C apps/playground run server (http://localhost:5175)\n - pnpm -C apps/playground run client (http://localhost:5173)\n\nThe server initializes the library and exposes endpoints for profiles, companies, typeahead, sales search, and sales profile.\nMetrics and request history are exposed at /api/metrics and /api/history.\n\nBy default the server uses a dev stub for cookies at /api/flexiq/linkedin-cookies/all.\nTo use your Cosiall backend, set env vars: COSIALL_API_URL and COSIALL_API_KEY and remove the stub in pps/playground/server/index.ts.\n
226
275
  ## Playground (local UI)
@@ -204,8 +204,15 @@ async function searchSalesLeads(keywords, options) {
204
204
  const pagingVal = rrec && 'paging' in rrec ? rrec.paging : undefined;
205
205
  const p = (pagingVal && typeof pagingVal === 'object') ? pagingVal : undefined;
206
206
  const paging = p ?? { start, count };
207
+ // Extract metadata.totalDisplayCount (LinkedIn's display string like "500K+")
208
+ const metadataVal = rrec && 'metadata' in rrec ? rrec.metadata : undefined;
209
+ const metadata = (metadataVal && typeof metadataVal === 'object') ? metadataVal : undefined;
207
210
  const result = options
208
- ? { items, page: { start: Number(paging.start ?? start), count: Number(paging.count ?? count), total: paging?.total } }
211
+ ? {
212
+ items,
213
+ page: { start: Number(paging.start ?? start), count: Number(paging.count ?? count), total: paging?.total },
214
+ metadata: metadata?.totalDisplayCount ? { totalDisplayCount: metadata.totalDisplayCount } : undefined
215
+ }
209
216
  : items; // backward-compat: old tests expect an array when no options passed
210
217
  searchCache.set(cacheKey, { data: result, ts: Date.now() });
211
218
  (0, metrics_1.incrementMetric)('searchCacheMisses');
@@ -18,16 +18,16 @@ function parseSalesSearchResults(rawResponse) {
18
18
  };
19
19
  // fsdKey from URN patterns
20
20
  const urn = String(el?.entityUrn || '');
21
- const m3 = urn.match(/^urn:li:(?:fs_salesProfile|fsd_profile):\(([^,\s)]+),([^,\s)]+),([^,\s)]+)\)/i);
21
+ const m3 = urn.match(/^urn:li:(?:fs_salesProfile|fsd_profile):\(([^,)]+),([^,)]+),([^,)]+)\)/i);
22
22
  if (m3) {
23
- res.fsdKey = m3[1];
24
- res.salesAuthType = m3[2];
25
- res.salesAuthToken = m3[3];
23
+ res.fsdKey = m3[1]?.trim();
24
+ res.salesAuthType = m3[2]?.trim();
25
+ res.salesAuthToken = m3[3]?.trim();
26
26
  }
27
27
  else {
28
- const m1 = urn.match(/^urn:li:(?:fs_salesProfile|fsd_profile):\(([^,\s)]+)/i);
28
+ const m1 = urn.match(/^urn:li:(?:fs_salesProfile|fsd_profile):\(([^,)]+)/i);
29
29
  if (m1)
30
- res.fsdKey = m1[1];
30
+ res.fsdKey = m1[1]?.trim();
31
31
  }
32
32
  // Image best artifact
33
33
  const img = el?.profilePictureDisplayImage;
package/dist/types.d.ts CHANGED
@@ -141,6 +141,9 @@ export interface SearchSalesResult {
141
141
  count: number;
142
142
  total?: number;
143
143
  };
144
+ metadata?: {
145
+ totalDisplayCount?: string;
146
+ };
144
147
  }
145
148
  export interface Company {
146
149
  companyId: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "linkedin-secret-sauce",
3
- "version": "0.3.8",
3
+ "version": "0.3.10",
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",