linkedin-api-voyager 1.1.0 → 1.3.1
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/lib/company.js +4 -1
- package/lib/config.js +3 -9
- package/lib/index.d.ts +1 -1
- package/lib/index.js +1 -1
- package/lib/posts.d.ts +11 -1
- package/lib/posts.js +83 -6
- package/lib/search.d.ts +3 -26
- package/lib/search.js +163 -112
- package/lib/teste.d.ts +1 -0
- package/lib/teste.js +10 -0
- package/lib/types.d.ts +794 -0
- package/lib/types.js +2 -0
- package/lib/user.d.ts +38 -0
- package/lib/user.js +172 -0
- package/lib/utils.d.ts +35 -0
- package/lib/utils.js +440 -27
- package/package.json +4 -3
- package/lib/account.d.ts +0 -18
- package/lib/account.js +0 -98
- package/lib/linkedin.d.ts +0 -0
- package/lib/linkedin.js +0 -1
package/lib/utils.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
/* eslint-disable prefer-const */
|
|
3
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
4
|
var __rest = (this && this.__rest) || function (s, e) {
|
|
3
5
|
var t = {};
|
|
4
6
|
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
@@ -11,6 +13,7 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
11
13
|
return t;
|
|
12
14
|
};
|
|
13
15
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
exports.omit = exports.getDataIncludedForEntity = void 0;
|
|
14
17
|
exports.filterKeys = filterKeys;
|
|
15
18
|
exports.filterOutKeys = filterOutKeys;
|
|
16
19
|
exports.getNestedValue = getNestedValue;
|
|
@@ -21,6 +24,17 @@ exports.extractDataWithReferences = extractDataWithReferences;
|
|
|
21
24
|
exports.debugResolvedStructure = debugResolvedStructure;
|
|
22
25
|
exports.extractFieldsFromIncluded = extractFieldsFromIncluded;
|
|
23
26
|
exports.mergeExtraFields = mergeExtraFields;
|
|
27
|
+
exports.extractExperiences = extractExperiences;
|
|
28
|
+
exports.assert = assert;
|
|
29
|
+
exports.getIdFromUrn = getIdFromUrn;
|
|
30
|
+
exports.getUrnFromRawUpdate = getUrnFromRawUpdate;
|
|
31
|
+
exports.isLinkedInUrn = isLinkedInUrn;
|
|
32
|
+
exports.parseExperienceItem = parseExperienceItem;
|
|
33
|
+
exports.getGroupedItemId = getGroupedItemId;
|
|
34
|
+
exports.resolveImageUrl = resolveImageUrl;
|
|
35
|
+
exports.resolveLinkedVectorImageUrl = resolveLinkedVectorImageUrl;
|
|
36
|
+
exports.stringifyLinkedInDate = stringifyLinkedInDate;
|
|
37
|
+
exports.normalizeRawOrganization = normalizeRawOrganization;
|
|
24
38
|
function filterKeys(obj, keysToKeep) {
|
|
25
39
|
const filteredObject = {};
|
|
26
40
|
keysToKeep.forEach((key) => {
|
|
@@ -41,12 +55,12 @@ function filterOutKeys(obj, keysToIgnore) {
|
|
|
41
55
|
}
|
|
42
56
|
// Nova função para extrair valores de caminhos aninhados
|
|
43
57
|
function getNestedValue(obj, path) {
|
|
44
|
-
return path.split(
|
|
58
|
+
return path.split(".").reduce((current, key) => {
|
|
45
59
|
var _a;
|
|
46
60
|
// Lidar com arrays como attributes[0]
|
|
47
|
-
if (key.includes(
|
|
48
|
-
const [arrayKey, indexStr] = key.split(
|
|
49
|
-
const index = parseInt(indexStr.replace(
|
|
61
|
+
if (key.includes("[") && key.includes("]")) {
|
|
62
|
+
const [arrayKey, indexStr] = key.split("[");
|
|
63
|
+
const index = parseInt(indexStr.replace("]", ""));
|
|
50
64
|
return (_a = current === null || current === void 0 ? void 0 : current[arrayKey]) === null || _a === void 0 ? void 0 : _a[index];
|
|
51
65
|
}
|
|
52
66
|
return current === null || current === void 0 ? void 0 : current[key];
|
|
@@ -54,7 +68,7 @@ function getNestedValue(obj, path) {
|
|
|
54
68
|
}
|
|
55
69
|
// Nova função melhorada para filtrar com caminhos aninhados
|
|
56
70
|
function extractFields(data, fieldsMap) {
|
|
57
|
-
return data.map(item => {
|
|
71
|
+
return data === null || data === void 0 ? void 0 : data.map((item) => {
|
|
58
72
|
const extracted = {};
|
|
59
73
|
Object.entries(fieldsMap).forEach(([newKey, path]) => {
|
|
60
74
|
const value = getNestedValue(item, path);
|
|
@@ -69,7 +83,7 @@ function extractFields(data, fieldsMap) {
|
|
|
69
83
|
function debugObjectStructure(obj, maxDepth = 3, currentDepth = 0) {
|
|
70
84
|
if (currentDepth >= maxDepth)
|
|
71
85
|
return;
|
|
72
|
-
const indent =
|
|
86
|
+
const indent = " ".repeat(currentDepth);
|
|
73
87
|
if (Array.isArray(obj)) {
|
|
74
88
|
console.log(`${indent}Array[${obj.length}]:`);
|
|
75
89
|
if (obj.length > 0) {
|
|
@@ -77,10 +91,12 @@ function debugObjectStructure(obj, maxDepth = 3, currentDepth = 0) {
|
|
|
77
91
|
debugObjectStructure(obj[0], maxDepth, currentDepth + 2);
|
|
78
92
|
}
|
|
79
93
|
}
|
|
80
|
-
else if (obj && typeof obj ===
|
|
81
|
-
Object.keys(obj)
|
|
94
|
+
else if (obj && typeof obj === "object") {
|
|
95
|
+
Object.keys(obj)
|
|
96
|
+
.slice(0, 10)
|
|
97
|
+
.forEach((key) => {
|
|
82
98
|
const value = obj[key];
|
|
83
|
-
if (typeof value ===
|
|
99
|
+
if (typeof value === "object" && value !== null) {
|
|
84
100
|
console.log(`${indent}${key}:`);
|
|
85
101
|
debugObjectStructure(value, maxDepth, currentDepth + 1);
|
|
86
102
|
}
|
|
@@ -96,7 +112,7 @@ function resolveReferences(data, included) {
|
|
|
96
112
|
return data;
|
|
97
113
|
// Criar um mapa de URN para acesso rápido
|
|
98
114
|
const urnMap = new Map();
|
|
99
|
-
included.forEach(item => {
|
|
115
|
+
included.forEach((item) => {
|
|
100
116
|
if (item.entityUrn) {
|
|
101
117
|
urnMap.set(item.entityUrn, item);
|
|
102
118
|
}
|
|
@@ -104,13 +120,13 @@ function resolveReferences(data, included) {
|
|
|
104
120
|
// Função recursiva para resolver referências
|
|
105
121
|
function resolveObject(obj) {
|
|
106
122
|
if (Array.isArray(obj)) {
|
|
107
|
-
return obj.map(item => resolveObject(item));
|
|
123
|
+
return obj.map((item) => resolveObject(item));
|
|
108
124
|
}
|
|
109
|
-
if (obj && typeof obj ===
|
|
125
|
+
if (obj && typeof obj === "object") {
|
|
110
126
|
const resolved = {};
|
|
111
127
|
Object.entries(obj).forEach(([key, value]) => {
|
|
112
128
|
// Detectar chaves que começam com * (referências URN)
|
|
113
|
-
if (key.startsWith(
|
|
129
|
+
if (key.startsWith("*") && typeof value === "string") {
|
|
114
130
|
const referencedData = urnMap.get(value);
|
|
115
131
|
if (referencedData) {
|
|
116
132
|
// Remover o * e usar como chave
|
|
@@ -122,15 +138,20 @@ function resolveReferences(data, included) {
|
|
|
122
138
|
}
|
|
123
139
|
}
|
|
124
140
|
// Detectar arrays de URNs
|
|
125
|
-
else if (Array.isArray(value) &&
|
|
126
|
-
|
|
141
|
+
else if (Array.isArray(value) &&
|
|
142
|
+
value.length > 0 &&
|
|
143
|
+
typeof value[0] === "string" &&
|
|
144
|
+
value[0].startsWith("urn:li:")) {
|
|
145
|
+
const resolvedArray = value
|
|
146
|
+
.map((urn) => {
|
|
127
147
|
const referencedData = urnMap.get(urn);
|
|
128
148
|
return referencedData ? resolveObject(referencedData) : urn;
|
|
129
|
-
})
|
|
149
|
+
})
|
|
150
|
+
.filter((item) => item !== null);
|
|
130
151
|
resolved[key] = resolvedArray;
|
|
131
152
|
}
|
|
132
153
|
// Recursão para objetos aninhados
|
|
133
|
-
else if (value && typeof value ===
|
|
154
|
+
else if (value && typeof value === "object") {
|
|
134
155
|
resolved[key] = resolveObject(value);
|
|
135
156
|
}
|
|
136
157
|
// Valores primitivos
|
|
@@ -147,9 +168,9 @@ function resolveReferences(data, included) {
|
|
|
147
168
|
// Função para extrair dados com resolução automática de referências
|
|
148
169
|
function extractDataWithReferences(elements, included, fieldsMap) {
|
|
149
170
|
// Filtrar dados pelos elementos
|
|
150
|
-
const filteredData = included.filter(item => elements.includes(item.entityUrn));
|
|
171
|
+
const filteredData = included.filter((item) => elements.includes(item.entityUrn));
|
|
151
172
|
// Resolver todas as referências
|
|
152
|
-
const resolvedData = filteredData.map(item => resolveReferences(item, included));
|
|
173
|
+
const resolvedData = filteredData.map((item) => resolveReferences(item, included));
|
|
153
174
|
// Se há mapeamento de campos, aplicar
|
|
154
175
|
if (fieldsMap) {
|
|
155
176
|
return extractFields(resolvedData, fieldsMap);
|
|
@@ -158,21 +179,21 @@ function extractDataWithReferences(elements, included, fieldsMap) {
|
|
|
158
179
|
}
|
|
159
180
|
// Função para debug de estrutura com referências resolvidas
|
|
160
181
|
function debugResolvedStructure(elements, included, maxDepth = 2) {
|
|
161
|
-
console.log(
|
|
182
|
+
console.log("🔍 Estrutura dos dados com referências resolvidas:");
|
|
162
183
|
const resolved = extractDataWithReferences(elements, included);
|
|
163
184
|
if (resolved.length > 0) {
|
|
164
185
|
console.log(`📊 Total de itens: ${resolved.length}`);
|
|
165
|
-
console.log(
|
|
186
|
+
console.log("📋 Estrutura do primeiro item:");
|
|
166
187
|
debugObjectStructure(resolved[0], maxDepth);
|
|
167
188
|
}
|
|
168
189
|
}
|
|
169
190
|
// Função para extrair campos específicos de todos os objetos no included
|
|
170
191
|
function extractFieldsFromIncluded(included, fields) {
|
|
171
192
|
return included
|
|
172
|
-
.filter(item => fields.some(field => item[field] !== undefined))
|
|
173
|
-
.map(item => {
|
|
193
|
+
.filter((item) => fields.some((field) => item[field] !== undefined))
|
|
194
|
+
.map((item) => {
|
|
174
195
|
const extracted = { entityUrn: item.entityUrn };
|
|
175
|
-
fields.forEach(field => {
|
|
196
|
+
fields.forEach((field) => {
|
|
176
197
|
if (item[field] !== undefined) {
|
|
177
198
|
extracted[field] = item[field];
|
|
178
199
|
}
|
|
@@ -181,9 +202,9 @@ function extractFieldsFromIncluded(included, fields) {
|
|
|
181
202
|
});
|
|
182
203
|
}
|
|
183
204
|
// Função para associar dados extras aos dados principais
|
|
184
|
-
function mergeExtraFields(mainData, extraData, matchKey =
|
|
185
|
-
return mainData.map(item => {
|
|
186
|
-
const extraItem = extraData.find(extra => item[matchKey] && extra.entityUrn === item[matchKey]);
|
|
205
|
+
function mergeExtraFields(mainData, extraData, matchKey = "companyUrn") {
|
|
206
|
+
return mainData.map((item) => {
|
|
207
|
+
const extraItem = extraData.find((extra) => item[matchKey] && extra.entityUrn === item[matchKey]);
|
|
187
208
|
if (extraItem) {
|
|
188
209
|
const { entityUrn } = extraItem, extraFields = __rest(extraItem, ["entityUrn"]);
|
|
189
210
|
return Object.assign(Object.assign({}, item), extraFields);
|
|
@@ -191,3 +212,395 @@ function mergeExtraFields(mainData, extraData, matchKey = 'companyUrn') {
|
|
|
191
212
|
return item;
|
|
192
213
|
});
|
|
193
214
|
}
|
|
215
|
+
const getDataIncludedForEntity = (jsonData, entityUrn) => {
|
|
216
|
+
const data = jsonData === null || jsonData === void 0 ? void 0 : jsonData.included;
|
|
217
|
+
if (data.length) {
|
|
218
|
+
const dataEntityUrn = data.find((item) => item.entityUrn.toLowerCase().includes(entityUrn.toLowerCase()));
|
|
219
|
+
return dataEntityUrn;
|
|
220
|
+
}
|
|
221
|
+
return [];
|
|
222
|
+
};
|
|
223
|
+
exports.getDataIncludedForEntity = getDataIncludedForEntity;
|
|
224
|
+
function extractExperiences(jsonData) {
|
|
225
|
+
var _a, _b, _c, _d, _e, _f;
|
|
226
|
+
const experiences = [];
|
|
227
|
+
try {
|
|
228
|
+
const included = (_a = jsonData === null || jsonData === void 0 ? void 0 : jsonData.included) !== null && _a !== void 0 ? _a : [];
|
|
229
|
+
if (!included.length) {
|
|
230
|
+
console.warn("[PROFILE] No 'included' array found");
|
|
231
|
+
return experiences;
|
|
232
|
+
}
|
|
233
|
+
// ===== PASS 1: Build component map by URN =====
|
|
234
|
+
console.info(`[PROFILE] Pass 1: Building component map from ${included.length} items`);
|
|
235
|
+
const componentMap = {};
|
|
236
|
+
for (const item of included) {
|
|
237
|
+
const urn = item === null || item === void 0 ? void 0 : item.entityUrn;
|
|
238
|
+
if (urn)
|
|
239
|
+
componentMap[urn] = item;
|
|
240
|
+
}
|
|
241
|
+
console.info(`[PROFILE] Pass 1: Indexed ${Object.keys(componentMap).length} components by URN`);
|
|
242
|
+
// ===== PASS 2: Find anchor and traverse =====
|
|
243
|
+
let mainExperienceUrn = null;
|
|
244
|
+
for (const urn of Object.keys(componentMap)) {
|
|
245
|
+
if (urn.includes("EXPERIENCE_VIEW_DETAILS") &&
|
|
246
|
+
urn.includes("fsd_profile:")) {
|
|
247
|
+
mainExperienceUrn = urn;
|
|
248
|
+
console.info(`[PROFILE] Pass 2: Found main experience anchor: ${urn}`);
|
|
249
|
+
break;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
if (!mainExperienceUrn) {
|
|
253
|
+
console.warn("[PROFILE] Pass 2: No experience anchor found");
|
|
254
|
+
return experiences;
|
|
255
|
+
}
|
|
256
|
+
const mainList = componentMap[mainExperienceUrn];
|
|
257
|
+
if (!mainList) {
|
|
258
|
+
console.error("[PROFILE] Pass 2: Anchor URN not in map (shouldn't happen)");
|
|
259
|
+
return experiences;
|
|
260
|
+
}
|
|
261
|
+
let elements = (_d = (_b = mainList.elements) !== null && _b !== void 0 ? _b : (_c = mainList.components) === null || _c === void 0 ? void 0 : _c.elements) !== null && _d !== void 0 ? _d : [];
|
|
262
|
+
console.info(`[PROFILE] Pass 2: Found ${elements.length} experience blocks`);
|
|
263
|
+
const paging = (_e = mainList.paging) !== null && _e !== void 0 ? _e : (_f = mainList.components) === null || _f === void 0 ? void 0 : _f.paging;
|
|
264
|
+
if (paging) {
|
|
265
|
+
const { total = "unknown", count = "unknown", start = 0 } = paging;
|
|
266
|
+
console.warn(`[PROFILE] PAGINATION: ${count} of ${total} experiences (start: ${start})`);
|
|
267
|
+
}
|
|
268
|
+
if (!elements.length) {
|
|
269
|
+
console.warn("[PROFILE] Pass 2: No elements in main list");
|
|
270
|
+
return experiences;
|
|
271
|
+
}
|
|
272
|
+
// Step 4: Process each experience block
|
|
273
|
+
elements.forEach((elem, idx) => {
|
|
274
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
275
|
+
try {
|
|
276
|
+
if (typeof elem !== "object" || elem === null)
|
|
277
|
+
return;
|
|
278
|
+
const entity = (_a = elem === null || elem === void 0 ? void 0 : elem.components) === null || _a === void 0 ? void 0 : _a.entityComponent;
|
|
279
|
+
if (typeof entity !== "object" || !entity) {
|
|
280
|
+
console.debug(`[PROFILE] Element ${idx}: No entityComponent`);
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
// Detect nested grouped roles (company with multiple positions)
|
|
284
|
+
let nestedUrn = null;
|
|
285
|
+
const subCompsWrapper = entity.subComponents;
|
|
286
|
+
if (typeof subCompsWrapper === "object" && subCompsWrapper) {
|
|
287
|
+
const subComponents = subCompsWrapper.components;
|
|
288
|
+
if (Array.isArray(subComponents) && subComponents.length > 0) {
|
|
289
|
+
const firstSub = subComponents[0];
|
|
290
|
+
const subComps = firstSub === null || firstSub === void 0 ? void 0 : firstSub.components;
|
|
291
|
+
if (typeof subComps === "object" && subComps) {
|
|
292
|
+
for (const key of ["*pagedListComponent", "pagedListComponent"]) {
|
|
293
|
+
const value = subComps[key];
|
|
294
|
+
if (value) {
|
|
295
|
+
nestedUrn =
|
|
296
|
+
typeof value === "string"
|
|
297
|
+
? value
|
|
298
|
+
: ((_b = value === null || value === void 0 ? void 0 : value.entityUrn) !== null && _b !== void 0 ? _b : null);
|
|
299
|
+
if (nestedUrn)
|
|
300
|
+
break;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
if (!nestedUrn) {
|
|
304
|
+
for (const [key, value] of Object.entries(subComps)) {
|
|
305
|
+
if (key.toLowerCase().includes("pagedlistcomponent") &&
|
|
306
|
+
value) {
|
|
307
|
+
nestedUrn =
|
|
308
|
+
typeof value === "string"
|
|
309
|
+
? value
|
|
310
|
+
: ((_c = value === null || value === void 0 ? void 0 : value.entityUrn) !== null && _c !== void 0 ? _c : null);
|
|
311
|
+
if (nestedUrn)
|
|
312
|
+
break;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
if (nestedUrn) {
|
|
320
|
+
// GROUPED ENTRY (company with multiple roles)
|
|
321
|
+
let companyName = "";
|
|
322
|
+
const titleV2 = entity.titleV2;
|
|
323
|
+
if (titleV2 && typeof titleV2 === "object") {
|
|
324
|
+
const textObj = titleV2.text;
|
|
325
|
+
companyName =
|
|
326
|
+
typeof textObj === "string" ? textObj : ((_d = textObj === null || textObj === void 0 ? void 0 : textObj.text) !== null && _d !== void 0 ? _d : "");
|
|
327
|
+
}
|
|
328
|
+
let totalDuration = "";
|
|
329
|
+
const subtitle = entity.subtitle;
|
|
330
|
+
if (subtitle && typeof subtitle === "object") {
|
|
331
|
+
const textObj = subtitle.text;
|
|
332
|
+
totalDuration =
|
|
333
|
+
typeof textObj === "string" ? textObj : ((_e = textObj === null || textObj === void 0 ? void 0 : textObj.text) !== null && _e !== void 0 ? _e : "");
|
|
334
|
+
}
|
|
335
|
+
console.info(`[PROFILE] Element ${idx}: Grouped company '${companyName}' (${totalDuration})`);
|
|
336
|
+
const nestedList = componentMap[nestedUrn];
|
|
337
|
+
if (nestedList) {
|
|
338
|
+
const nestedElements = (_h = (_f = nestedList.elements) !== null && _f !== void 0 ? _f : (_g = nestedList.components) === null || _g === void 0 ? void 0 : _g.elements) !== null && _h !== void 0 ? _h : [];
|
|
339
|
+
console.info(`[PROFILE] Found ${nestedElements.length} roles for '${companyName}'`);
|
|
340
|
+
for (const [roleIdx, roleElem] of nestedElements.entries()) {
|
|
341
|
+
const roleEntity = (_j = roleElem === null || roleElem === void 0 ? void 0 : roleElem.components) === null || _j === void 0 ? void 0 : _j.entityComponent;
|
|
342
|
+
if (roleEntity && typeof roleEntity === "object") {
|
|
343
|
+
const exp = extractOneExperience(roleEntity, companyName);
|
|
344
|
+
if (exp) {
|
|
345
|
+
console.debug(`[PROFILE] Extracted role ${roleIdx + 1}/${nestedElements.length}: ${exp.role} at ${companyName}`);
|
|
346
|
+
experiences.push(exp);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
else {
|
|
352
|
+
console.warn(`[PROFILE] Nested URN not found in map: ${nestedUrn}`);
|
|
353
|
+
}
|
|
354
|
+
// Continue to next element without extracting parent
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
// SINGLE ENTRY
|
|
358
|
+
const titleV2 = entity.titleV2;
|
|
359
|
+
const caption = entity.caption;
|
|
360
|
+
if (titleV2 && !caption) {
|
|
361
|
+
console.warn(`[PROFILE] Element ${idx}: Skipping potential parent block`);
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
const exp = extractOneExperience(entity);
|
|
365
|
+
if (exp)
|
|
366
|
+
experiences.push(exp);
|
|
367
|
+
}
|
|
368
|
+
catch (err) {
|
|
369
|
+
console.warn(`[PROFILE] Error on element ${idx}: ${err.message}`);
|
|
370
|
+
}
|
|
371
|
+
});
|
|
372
|
+
console.info(`[PROFILE] Successfully extracted ${experiences.length} total experiences`);
|
|
373
|
+
}
|
|
374
|
+
catch (err) {
|
|
375
|
+
console.error(`[PROFILE] Fatal error: ${err.message}`);
|
|
376
|
+
}
|
|
377
|
+
return experiences;
|
|
378
|
+
}
|
|
379
|
+
// Helper function
|
|
380
|
+
function extractOneExperience(entity, companyOverride) {
|
|
381
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
382
|
+
if (!entity || typeof entity !== "object")
|
|
383
|
+
return null;
|
|
384
|
+
const safeGetText = (obj, ...keys) => {
|
|
385
|
+
var _a;
|
|
386
|
+
let current = obj;
|
|
387
|
+
for (const key of keys) {
|
|
388
|
+
if (typeof current !== "object" || current === null)
|
|
389
|
+
return "";
|
|
390
|
+
current = current[key];
|
|
391
|
+
if (current === undefined || current === null)
|
|
392
|
+
return "";
|
|
393
|
+
}
|
|
394
|
+
return typeof current === "string" ? current : ((_a = current === null || current === void 0 ? void 0 : current.text) !== null && _a !== void 0 ? _a : "");
|
|
395
|
+
};
|
|
396
|
+
const title = safeGetText(entity, "titleV2", "text", "text");
|
|
397
|
+
const idCompany = ((_b = (_a = safeGetText(entity, "textActionTarget")) === null || _a === void 0 ? void 0 : _a.match(/\/(\d+)\/?$/)) === null || _b === void 0 ? void 0 : _b[1]) || null;
|
|
398
|
+
if (!title)
|
|
399
|
+
return null;
|
|
400
|
+
let company = companyOverride !== null && companyOverride !== void 0 ? companyOverride : "";
|
|
401
|
+
if (!company) {
|
|
402
|
+
const subtitle = entity.subtitle;
|
|
403
|
+
if (subtitle && typeof subtitle === "object") {
|
|
404
|
+
company =
|
|
405
|
+
typeof subtitle.text === "string"
|
|
406
|
+
? subtitle.text
|
|
407
|
+
: ((_d = (_c = subtitle.text) === null || _c === void 0 ? void 0 : _c.text) !== null && _d !== void 0 ? _d : "");
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
let dates = "";
|
|
411
|
+
const caption = entity.caption;
|
|
412
|
+
if (caption && typeof caption === "object") {
|
|
413
|
+
dates =
|
|
414
|
+
typeof caption.text === "string"
|
|
415
|
+
? caption.text
|
|
416
|
+
: ((_f = (_e = caption.text) === null || _e === void 0 ? void 0 : _e.text) !== null && _f !== void 0 ? _f : "");
|
|
417
|
+
}
|
|
418
|
+
let location = "";
|
|
419
|
+
const metadata = entity.metadata;
|
|
420
|
+
if (metadata && typeof metadata === "object") {
|
|
421
|
+
location =
|
|
422
|
+
typeof metadata.text === "string"
|
|
423
|
+
? metadata.text
|
|
424
|
+
: ((_h = (_g = metadata.text) === null || _g === void 0 ? void 0 : _g.text) !== null && _h !== void 0 ? _h : "");
|
|
425
|
+
}
|
|
426
|
+
let description = "";
|
|
427
|
+
try {
|
|
428
|
+
const subcomps = entity.subComponents;
|
|
429
|
+
const components = subcomps === null || subcomps === void 0 ? void 0 : subcomps.components;
|
|
430
|
+
if (Array.isArray(components)) {
|
|
431
|
+
for (const sc of components) {
|
|
432
|
+
const scComps = sc === null || sc === void 0 ? void 0 : sc.components;
|
|
433
|
+
const fixed = scComps === null || scComps === void 0 ? void 0 : scComps.fixedListComponent;
|
|
434
|
+
const fixedComps = fixed === null || fixed === void 0 ? void 0 : fixed.components;
|
|
435
|
+
if (Array.isArray(fixedComps)) {
|
|
436
|
+
for (const fc of fixedComps) {
|
|
437
|
+
const txtComp = (_j = fc === null || fc === void 0 ? void 0 : fc.components) === null || _j === void 0 ? void 0 : _j.textComponent;
|
|
438
|
+
const txt = safeGetText(txtComp, "text", "text");
|
|
439
|
+
if (txt) {
|
|
440
|
+
description = txt;
|
|
441
|
+
break;
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
if (description)
|
|
446
|
+
break;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
catch (err) {
|
|
451
|
+
console.debug(`[PROFILE] Error extracting description: ${err.message}`);
|
|
452
|
+
}
|
|
453
|
+
const result = {
|
|
454
|
+
role: title,
|
|
455
|
+
idCompany,
|
|
456
|
+
company: company || null,
|
|
457
|
+
time_duration: dates || "",
|
|
458
|
+
location: location || "",
|
|
459
|
+
description: description || null,
|
|
460
|
+
};
|
|
461
|
+
if (dates.includes("·")) {
|
|
462
|
+
const parts = dates.split("·");
|
|
463
|
+
result.time_period = parts[0].trim();
|
|
464
|
+
if (parts[1])
|
|
465
|
+
result.duration = parts[1].trim();
|
|
466
|
+
}
|
|
467
|
+
console.info(`[PROFILE] ✓ ${title} at ${company || null}`);
|
|
468
|
+
return result;
|
|
469
|
+
}
|
|
470
|
+
function assert(value, message) {
|
|
471
|
+
if (value) {
|
|
472
|
+
return;
|
|
473
|
+
}
|
|
474
|
+
if (!message) {
|
|
475
|
+
throw new Error("Assertion failed");
|
|
476
|
+
}
|
|
477
|
+
throw typeof message === "string" ? new Error(message) : message;
|
|
478
|
+
}
|
|
479
|
+
function getIdFromUrn(urn) {
|
|
480
|
+
return urn === null || urn === void 0 ? void 0 : urn.split(":").at(-1);
|
|
481
|
+
}
|
|
482
|
+
/**
|
|
483
|
+
* Return the URN of a raw group update
|
|
484
|
+
*
|
|
485
|
+
* Example: urn:li:fs_miniProfile:<id>
|
|
486
|
+
* Example: urn:li:fs_updateV2:(<urn>,GROUP_FEED,EMPTY,DEFAULT,false)
|
|
487
|
+
*/
|
|
488
|
+
function getUrnFromRawUpdate(update) {
|
|
489
|
+
var _a, _b;
|
|
490
|
+
return (_b = (_a = update === null || update === void 0 ? void 0 : update.split("(")[1]) === null || _a === void 0 ? void 0 : _a.split(",").at(0)) === null || _b === void 0 ? void 0 : _b.trim();
|
|
491
|
+
}
|
|
492
|
+
function isLinkedInUrn(urn) {
|
|
493
|
+
return (urn === null || urn === void 0 ? void 0 : urn.startsWith("urn:li:")) && urn.split(":").length >= 4;
|
|
494
|
+
}
|
|
495
|
+
function parseExperienceItem(item, { isGroupItem = false, included }) {
|
|
496
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s;
|
|
497
|
+
const component = item.components.entityComponent;
|
|
498
|
+
const title = component.titleV2.text.text;
|
|
499
|
+
const subtitle = component.subtitle;
|
|
500
|
+
const subtitleParts = (_a = subtitle === null || subtitle === void 0 ? void 0 : subtitle.text) === null || _a === void 0 ? void 0 : _a.split(" · ");
|
|
501
|
+
const company = subtitleParts === null || subtitleParts === void 0 ? void 0 : subtitleParts[0];
|
|
502
|
+
const employmentType = subtitleParts === null || subtitleParts === void 0 ? void 0 : subtitleParts[1];
|
|
503
|
+
const companyId = (_e = getIdFromUrn((_d = (_c = (_b = component.image) === null || _b === void 0 ? void 0 : _b.attributes) === null || _c === void 0 ? void 0 : _c[0]) === null || _d === void 0 ? void 0 : _d["*companyLogo"])) !== null && _e !== void 0 ? _e : (_g = (_f = component.image) === null || _f === void 0 ? void 0 : _f.actionTarget) === null || _g === void 0 ? void 0 : _g.split("/").findLast(Boolean);
|
|
504
|
+
const companyUrn = companyId ? `urn:li:fsd_company:${companyId}` : undefined;
|
|
505
|
+
let companyImage;
|
|
506
|
+
if (companyId) {
|
|
507
|
+
const companyEntity = included.find((i) => { var _a; return (_a = i.entityUrn) === null || _a === void 0 ? void 0 : _a.endsWith(companyId); });
|
|
508
|
+
if (companyEntity) {
|
|
509
|
+
companyImage = resolveImageUrl((_h = companyEntity.logoResolutionResult) === null || _h === void 0 ? void 0 : _h.vectorImage);
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
const metadata = (component === null || component === void 0 ? void 0 : component.metadata) || {};
|
|
513
|
+
const location = metadata === null || metadata === void 0 ? void 0 : metadata.text;
|
|
514
|
+
const durationText = (_j = component.caption) === null || _j === void 0 ? void 0 : _j.text;
|
|
515
|
+
const durationParts = durationText === null || durationText === void 0 ? void 0 : durationText.split(" · ");
|
|
516
|
+
const dateParts = (_k = durationParts === null || durationParts === void 0 ? void 0 : durationParts[0]) === null || _k === void 0 ? void 0 : _k.split(" - ");
|
|
517
|
+
const duration = durationParts === null || durationParts === void 0 ? void 0 : durationParts[1];
|
|
518
|
+
const startDate = dateParts === null || dateParts === void 0 ? void 0 : dateParts[0];
|
|
519
|
+
const endDate = dateParts === null || dateParts === void 0 ? void 0 : dateParts[1];
|
|
520
|
+
const subComponents = component.subComponents;
|
|
521
|
+
const fixedListComponent = (_o = (_m = (_l = subComponents === null || subComponents === void 0 ? void 0 : subComponents.components) === null || _l === void 0 ? void 0 : _l[0]) === null || _m === void 0 ? void 0 : _m.components) === null || _o === void 0 ? void 0 : _o.fixedListComponent;
|
|
522
|
+
const fixedListTextComponent = (_r = (_q = (_p = fixedListComponent === null || fixedListComponent === void 0 ? void 0 : fixedListComponent.components) === null || _p === void 0 ? void 0 : _p[0]) === null || _q === void 0 ? void 0 : _q.components) === null || _r === void 0 ? void 0 : _r.textComponent;
|
|
523
|
+
const description = (_s = fixedListTextComponent === null || fixedListTextComponent === void 0 ? void 0 : fixedListTextComponent.text) === null || _s === void 0 ? void 0 : _s.text;
|
|
524
|
+
const parsedData = {
|
|
525
|
+
title,
|
|
526
|
+
companyName: !isGroupItem ? company : undefined,
|
|
527
|
+
employmentType: isGroupItem ? company : employmentType,
|
|
528
|
+
location,
|
|
529
|
+
duration,
|
|
530
|
+
startDate,
|
|
531
|
+
endDate,
|
|
532
|
+
description,
|
|
533
|
+
company: {
|
|
534
|
+
entityUrn: companyUrn,
|
|
535
|
+
id: companyId,
|
|
536
|
+
name: !isGroupItem ? company : undefined,
|
|
537
|
+
logo: companyImage,
|
|
538
|
+
},
|
|
539
|
+
};
|
|
540
|
+
return parsedData;
|
|
541
|
+
}
|
|
542
|
+
function getGroupedItemId(item) {
|
|
543
|
+
var _a, _b, _c, _d;
|
|
544
|
+
const subComponents = (_b = (_a = item.components) === null || _a === void 0 ? void 0 : _a.entityComponent) === null || _b === void 0 ? void 0 : _b.subComponents;
|
|
545
|
+
const subComponentsComponents = (_d = (_c = subComponents === null || subComponents === void 0 ? void 0 : subComponents.components) === null || _c === void 0 ? void 0 : _c[0]) === null || _d === void 0 ? void 0 : _d.components;
|
|
546
|
+
const pagedListComponentId = subComponentsComponents === null || subComponentsComponents === void 0 ? void 0 : subComponentsComponents["*pagedListComponent"];
|
|
547
|
+
if (pagedListComponentId === null || pagedListComponentId === void 0 ? void 0 : pagedListComponentId.includes("fsd_profilePositionGroup")) {
|
|
548
|
+
const pattern = /urn:li:fsd_profilePositionGroup:\([\dA-z]+,[\dA-z]+\)/;
|
|
549
|
+
const match = pagedListComponentId.match(pattern);
|
|
550
|
+
return match === null || match === void 0 ? void 0 : match[0];
|
|
551
|
+
}
|
|
552
|
+
return undefined;
|
|
553
|
+
}
|
|
554
|
+
const omit = (inputObj, ...keys) => {
|
|
555
|
+
const keysSet = new Set(keys);
|
|
556
|
+
return Object.fromEntries(Object.entries(inputObj).filter(([k]) => !keysSet.has(k)));
|
|
557
|
+
};
|
|
558
|
+
exports.omit = omit;
|
|
559
|
+
function resolveImageUrl(vectorImage) {
|
|
560
|
+
var _a, _b;
|
|
561
|
+
if (!(vectorImage === null || vectorImage === void 0 ? void 0 : vectorImage.rootUrl))
|
|
562
|
+
return;
|
|
563
|
+
if (!((_a = vectorImage.artifacts) === null || _a === void 0 ? void 0 : _a.length))
|
|
564
|
+
return;
|
|
565
|
+
const largestArtifact = vectorImage.artifacts.reduce((a, b) => {
|
|
566
|
+
if (b.width > a.width)
|
|
567
|
+
return b;
|
|
568
|
+
return a;
|
|
569
|
+
}, (_b = vectorImage.artifacts[0]) !== null && _b !== void 0 ? _b : { width: 0, height: 0 });
|
|
570
|
+
if (!(largestArtifact === null || largestArtifact === void 0 ? void 0 : largestArtifact.fileIdentifyingUrlPathSegment))
|
|
571
|
+
return;
|
|
572
|
+
return `${vectorImage.rootUrl}${largestArtifact.fileIdentifyingUrlPathSegment}`;
|
|
573
|
+
}
|
|
574
|
+
function resolveLinkedVectorImageUrl(linkedVectorImage) {
|
|
575
|
+
return resolveImageUrl(linkedVectorImage === null || linkedVectorImage === void 0 ? void 0 : linkedVectorImage["com.linkedin.common.VectorImage"]);
|
|
576
|
+
}
|
|
577
|
+
function stringifyLinkedInDate(date) {
|
|
578
|
+
if (!date)
|
|
579
|
+
return undefined;
|
|
580
|
+
if (date.year === undefined)
|
|
581
|
+
return undefined;
|
|
582
|
+
return [date.year, date.month].filter(Boolean).join("-");
|
|
583
|
+
}
|
|
584
|
+
function normalizeRawOrganization(o) {
|
|
585
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
|
|
586
|
+
assert(o, "Missing organization");
|
|
587
|
+
assert(o.entityUrn, "Invalid organization: missing entityUrn");
|
|
588
|
+
const id = getIdFromUrn(o.entityUrn);
|
|
589
|
+
assert(id, `Invalid organization ID: ${o.entityUrn}`);
|
|
590
|
+
return Object.assign(Object.assign({}, (0, exports.omit)(o, "universalName", "logo", "backgroundCoverImage", "coverPhoto", "overviewPhoto", "$recipeType", "callToAction", "phone", "permissions", "followingInfo", "adsRule", "autoGenerated", "lcpTreatment", "staffingCompany", "showcase", "paidCompany", "claimable", "claimableByViewer", "viewerPendingAdministrator", "viewerConnectedToAdministrator", "viewerFollowingJobsUpdates", "viewerEmployee", "associatedHashtags", "associatedHashtagsResolutionResults", "affiliatedCompaniesResolutionResults", "groupsResolutionResults", "showcasePagesResolutionResults")), { id, publicIdentifier: o.universalName, logo: resolveLinkedVectorImageUrl((_a = o.logo) === null || _a === void 0 ? void 0 : _a.image), backgroundCoverImage: resolveLinkedVectorImageUrl((_b = o.backgroundCoverImage) === null || _b === void 0 ? void 0 : _b.image), coverPhoto: (_d = (_c = o.coverPhoto) === null || _c === void 0 ? void 0 : _c["com.linkedin.voyager.common.MediaProcessorImage"]) === null || _d === void 0 ? void 0 : _d.id, overviewPhoto: (_f = (_e = o.overviewPhoto) === null || _e === void 0 ? void 0 : _e["com.linkedin.voyager.common.MediaProcessorImage"]) === null || _f === void 0 ? void 0 : _f.id, callToActionUrl: (_g = o.callToAction) === null || _g === void 0 ? void 0 : _g.url, phone: (_h = o.phone) === null || _h === void 0 ? void 0 : _h.number, numFollowers: (_j = o.followingInfo) === null || _j === void 0 ? void 0 : _j.followerCount, affiliatedCompaniesResolutionResults: Object.fromEntries(Object.entries((_k = o.affiliatedCompaniesResolutionResults) !== null && _k !== void 0 ? _k : {}).map(([k, v]) => {
|
|
591
|
+
var _a, _b;
|
|
592
|
+
return [
|
|
593
|
+
k,
|
|
594
|
+
Object.assign(Object.assign({}, (0, exports.omit)(v, "universalName", "logo", "$recipeType", "followingInfo", "showcase", "paidCompany")), { id: getIdFromUrn(v.entityUrn), publicIdentifier: v.universalName, numFollowers: (_a = v.followingInfo) === null || _a === void 0 ? void 0 : _a.followerCount, logo: resolveLinkedVectorImageUrl((_b = v.logo) === null || _b === void 0 ? void 0 : _b.image) }),
|
|
595
|
+
];
|
|
596
|
+
})), groupsResolutionResults: Object.fromEntries(Object.entries((_l = o.groupsResolutionResults) !== null && _l !== void 0 ? _l : {}).map(([k, v]) => [
|
|
597
|
+
k,
|
|
598
|
+
Object.assign(Object.assign({}, (0, exports.omit)(v, "logo", "$recipeType")), { id: getIdFromUrn(v.entityUrn), logo: resolveLinkedVectorImageUrl(v.logo) }),
|
|
599
|
+
])), showcasePagesResolutionResults: Object.fromEntries(Object.entries((_m = o.showcasePagesResolutionResults) !== null && _m !== void 0 ? _m : {}).map(([k, v]) => {
|
|
600
|
+
var _a, _b;
|
|
601
|
+
return [
|
|
602
|
+
k,
|
|
603
|
+
Object.assign(Object.assign({}, (0, exports.omit)(v, "universalName", "logo", "$recipeType", "followingInfo", "showcase", "paidCompany")), { id: getIdFromUrn(v.entityUrn), publicIdentifier: v.universalName, numFollowers: (_a = v.followingInfo) === null || _a === void 0 ? void 0 : _a.followerCount, logo: resolveLinkedVectorImageUrl((_b = v.logo) === null || _b === void 0 ? void 0 : _b.image) }),
|
|
604
|
+
];
|
|
605
|
+
})) });
|
|
606
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "linkedin-api-voyager",
|
|
3
|
-
"version": "1.1
|
|
3
|
+
"version": "1.3.1",
|
|
4
4
|
"description": "Uma biblioteca TypeScript para interagir com a API interna do LinkedIn (Voyager)",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"types": "lib/index.d.ts",
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"scripts": {
|
|
12
12
|
"build": "tsc",
|
|
13
13
|
"prepare": "npm run build",
|
|
14
|
-
"dev": "nodemon src/
|
|
14
|
+
"dev": "nodemon src/teste.ts",
|
|
15
15
|
"prepublishOnly": "npm run build"
|
|
16
16
|
},
|
|
17
17
|
"devDependencies": {
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"axios": "^1.11.0",
|
|
26
|
-
"fs-extra": "^11.3.1"
|
|
26
|
+
"fs-extra": "^11.3.1",
|
|
27
|
+
"path": "^0.12.7"
|
|
27
28
|
}
|
|
28
29
|
}
|
package/lib/account.d.ts
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
export declare const getProfile: (identifier: string) => Promise<{
|
|
2
|
-
publicIdentifier: any;
|
|
3
|
-
firstName: any;
|
|
4
|
-
lastName: any;
|
|
5
|
-
fullName: string;
|
|
6
|
-
birthDate: string | null;
|
|
7
|
-
profilePicture: string | null;
|
|
8
|
-
backgroundPicture: string | null;
|
|
9
|
-
location: {
|
|
10
|
-
country: any;
|
|
11
|
-
city: any;
|
|
12
|
-
};
|
|
13
|
-
address: any;
|
|
14
|
-
industry: any;
|
|
15
|
-
headline: any;
|
|
16
|
-
summary: any;
|
|
17
|
-
}>;
|
|
18
|
-
export declare const getProfissionalExperiences: (identifier: string) => Promise<any[]>;
|