added npm files

This commit is contained in:
2024-05-09 08:10:20 -07:00
parent 9f0b036391
commit c58dab1807
11 changed files with 937 additions and 1 deletions
+2
View File
@@ -128,3 +128,5 @@ dist
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*
temp.js
+37 -1
View File
@@ -1,2 +1,38 @@
# linkedin-api
The NodeJS version of tomquirk's linkedin-api python package
An actually useful LinkedIn Nodejs package.
## Disclaimers
This package is not in any way affiliated with LinkedIn. In fact, your account may be banned for using this (hasn't happened to me though).
You can use your LinkedIn credentials in code, making this super simple to use!
*This project was HEAVILY inspired by Tomquirk's LinkedIn-API PyPy package, which you can find at https://github.com/tomquirk/linkedin-api*
## Quick-Start
```JS
// log in
const LAPI = new linkedInAPIClass();
await LAPI.login("pinknodders@pinknodders.lol", "**********");
// GET a company
const comp = await LAPI.searchCompanies("Linux");
// GET one employee
const emp = await comp[0].getEmployees("Torvalds", 1);
```
## Contributions
If you want to contribute, just fork, add your features, then make a PR
If you do contribute, please follow these guidelines
1. Document your changes in the PR (i.e. make a bulleted list of changes)
2. If you change existing code, you must specify that
3. Please please please don't be me and push credentials
## Reporting Bugs
Please open an issue using the following guildelines
1. Be clear and concise
2. Provide the code necessary to reproduce the problem
Thanks for using my project!
ION606
+70
View File
@@ -0,0 +1,70 @@
import { LinkedInProfile } from "./Profile.js";
export class Company {
/** @type {import('../index.js').linkedInAPIClass} */
#APIRef;
/**
* gets the employees of a company
* @param {Number} [limit=Infinity] to make things simpler I used increments of 50, so the limit is essentially ceiled to the nearest multiple of 50
* @param {Boolean} [raw=false] whether or not you want the raw JSON returned
* @returns {Promise<JSON[] | LinkedInProfile[]>}
* @note This function skips over any employees who's profile is marked as private
*/
async getEmployees(limit = Infinity, raw = false) {
const employeesInit = await this.#APIRef._makeReq(`variables=(start:0,origin:FACETED_SEARCH,query:(flagshipSearchIntent:ORGANIZATIONS_PEOPLE_ALUMNI,queryParameters:List((key:currentCompany,value:List(${this.urn})),(key:resultType,value:List(ORGANIZATION_ALUMNI))),includeFiltersInResponse:true),count:1)`)
const numEmp = employeesInit.data.data.searchDashClustersByAll.paging.total,
empAll = []
// since the max cap is 50, we need to iterate until it's over 50
for (let i = 0; i < numEmp; i += 50) {
if (empAll.length >= limit) break;
const c = (i + 50 >= limit) ? limit - i : 50;
const employeeRes = await this.#APIRef._makeReq(`variables=(start:${i},origin:FACETED_SEARCH,query:(flagshipSearchIntent:ORGANIZATIONS_PEOPLE_ALUMNI,queryParameters:List((key:currentCompany,value:List(${this.urn})),(key:resultType,value:List(ORGANIZATION_ALUMNI))),includeFiltersInResponse:true),count:${c})`);
const employees = employeeRes.included;
const empParsed = employees.filter(e => (e.$type === "com.linkedin.voyager.dash.search.EntityResultViewModel"))
.filter(o => o.title.text !== "LinkedIn Member");
if (empParsed.length) empAll.push(...empParsed);
await this.#APIRef.evade();
}
return (raw) ? empAll : empAll.map(eRaw => new LinkedInProfile(eRaw, this.#APIRef));
}
/**
* @returns {Promise<JSON[] | LinkedInProfile[]>}
* @param {string} name
* @param {boolean} raw
*/
getEmployees = (name, limit=1000, raw=false) => this.#APIRef.searchEmployees(name, limit, !raw, [this.urn]);
async getInfo() {
const toAdd = `q=universalName&universalName=${this.urn}`;
return await this.#APIRef._makeReq(toAdd);
}
checkIfCompleted = () => !!(this.name && this.url && this.urn && this.#APIRef);
/**
* @param {{title: {text: String}, entityUrn: String, navigationUrl: String}} data
* @param {import('../index.js').linkedInAPIClass} APIRef
*/
constructor(data, APIRef) {
this.#APIRef = APIRef;
// this.entityUrn = entityUrn?.replace("urn:li:fsd_company:", "");
this.name = data.title.text;
this.urn = data.entityUrn;
this.url = data.navigationUrl;
if (!this.checkIfCompleted()) throw "NOT ALL NEEDED PARAMS FOUND!";
}
}
+47
View File
@@ -0,0 +1,47 @@
import { linkedInAPIClass } from "../index.js";
export class LinkedInProfile {
/** @type {linkedInAPIClass} */
#APIRef;
/**
* Attempts to get the contact info for the person
* @returns {Promise<{websites: [{label:string, category:string, url:string}], emailAddress:string, phoneNumbers:[string],weChatContactInfo:any,twitterHandles:[string], instantMessengers:[]}>}
*/
async getContactInfo() {
const r = await this.#APIRef._makeReq(`includeWebMetadata=true&variables=(memberIdentity:${this.entityNameJoined})`, true);
if (r?.data?.errors) return console.error(JSON.stringify(r.data.errors));
const uObj = r.included.find(o => o.publicIdentifier === this.entityNameJoined);
if (!uObj) return;
let { phoneNumbers, emailAddress, websites, weChatContactInfo, twitterHandles, instantMessengers } = uObj;
if (websites) websites = websites.map(w => ({ label: w.label, category: w.category, url: w.url }));
return { phoneNumbers, emailAddress, websites, weChatContactInfo, twitterHandles, instantMessengers };
}
// Parse specific services offered, contained in the 'simpleInsight' section of 'insightsResolutionResults'
parseServices(insights) {
for (let insight of insights) {
if (insight.simpleInsight && insight.simpleInsight.title && insight.simpleInsight.title.text) {
return insight.simpleInsight.title.text;
}
}
return '';
}
constructor(jsonData, apiRef) {
this.#APIRef = apiRef;
this.name = jsonData.title ? jsonData.title.text : '';
this.entityNameJoined = jsonData.navigationUrl?.split("/in/")[1]?.split("?")[0];
this.profileUrl = jsonData.navigationUrl || '';
this.trackingUrn = jsonData.trackingUrn || '';
const match = jsonData.entityUrn?.match(/urn:li:fsd_profile:(.*?),/);
this.entityUrn = match ? match[1] : '';
this.jobServices = jsonData.insightsResolutionResults ? this.parseServices(jsonData.insightsResolutionResults) : '';
this.primarySubtitle = jsonData.primarySubtitle ? jsonData.primarySubtitle.text : '';
this.secondarySubtitle = jsonData.secondarySubtitle ? jsonData.secondarySubtitle.text : '';
this.bserpEntityNavigationalUrl = jsonData.bserpEntityNavigationalUrl || '';
}
}
+7
View File
@@ -0,0 +1,7 @@
import linkedInAPIClass from "../index.js";
import { LinkedInProfile } from "./Profile.js";
import { Company } from "./Company.js";
import { GenericEntity, Group, ReactionTypeCount, SocialActivityCounts } from './misc.js'
export { linkedInAPIClass, LinkedInProfile, Company, GenericEntity, Group, ReactionTypeCount, SocialActivityCounts };
+30
View File
@@ -0,0 +1,30 @@
export class ReactionTypeCount {
constructor({ count, reactionType }) {
this.count = count;
this.reactionType = reactionType;
}
}
export class SocialActivityCounts {
constructor({ entityUrn, numComments, numLikes, reactionTypeCounts, liked, preDashEntityUrn }) {
this.entityUrn = entityUrn;
this.numComments = numComments;
this.numLikes = numLikes;
this.reactionTypeCounts = reactionTypeCounts.map(count => new ReactionTypeCount(count));
this.liked = liked;
this.preDashEntityUrn = preDashEntityUrn;
}
}
export class Group {
constructor({ entityUrn }) {
this.entityUrn = entityUrn;
}
}
// Placeholder class for any other types
export class GenericEntity {
constructor(data) {
Object.assign(this, data);
}
}
+296
View File
@@ -0,0 +1,296 @@
import fs from 'fs';
import { wrapper } from 'axios-cookiejar-support';
import { CookieJar } from 'tough-cookie';
import axiosModule from 'axios';
import { LinkedInProfile, Company, GenericEntity, Group, SocialActivityCounts } from './classes/classes.js';
const cookieJar = new CookieJar();
const axios = wrapper(axiosModule.create({
jar: cookieJar, // attach cookie jar
withCredentials: true
}));
export const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
export const randomIntFromInterval = (boundLower, boundUpper) => Math.floor(Math.random() * (boundUpper - boundLower + 1) + boundLower);
export const inRange = (inp, minAmt, maxAmt) => (inp >= minAmt && inp <= maxAmt);
/**
* Function to dynamically create class instances based on type
* @param {Object[]} included
* @param {linkedInAPIClass} APIRef
* @param {boolean} [excludeGeneric=false] whether or not to exclude any object that does not exactly fit the Company type
* @returns {[SocialActivityCounts | Group | Company | GenericEntity]}
*/
function parseIncludedData(included, APIRef, excludeGeneric = false) {
const classMap = {
'com.linkedin.voyager.dash.feed.SocialActivityCounts': SocialActivityCounts,
'com.linkedin.voyager.dash.groups.Group': Group,
"com.linkedin.voyager.dash.search.EntityResultViewModel": Company,
// 'com.linkedin.voyager.dash.organization.Company': Company,
'default': GenericEntity // Default class for any other type
},
toIgnore = {
"com.linkedin.voyager.dash.search.FeedbackCard": -1,
"com.linkedin.voyager.dash.search.LazyLoadedActions": -1, // companies again
'com.linkedin.voyager.dash.organization.Company': -1
}
return included.map(item => {
const EntityClass = classMap[item.$type] || toIgnore[item.$type] || classMap['default'];
if (EntityClass === -1) return;
// ignore the strange companies like "linkedIn pages"
// if (!item.name) return null;
// APIRef may be ignored here
return new EntityClass(item, APIRef);
}).filter(o => (excludeGeneric && o instanceof Company) || (!excludeGeneric && o));
}
export async function parseResponse(data, APIRef, excludeGeneric) {
const jsonData = (typeof data === 'string') ? JSON.parse(data) : data;
const includedData = jsonData.included || [];
return parseIncludedData(includedData, APIRef, excludeGeneric);
}
function findRangeIndex(number) {
const rangeMap = ["B", "C", "D", "E", "F", "G", "H", "I"]
const companySizeRanges = [
[1, 10],
[11, 50],
[51, 200],
[201, 500],
[501, 1000],
[1001, 5000],
[5001, 10000],
[10001, Infinity],
];
for (let i = 0; i < companySizeRanges.length; i++) {
const [lowerBound, upperBound] = companySizeRanges[i];
if (number >= lowerBound && number <= upperBound) {
return `"${rangeMap[i]}"`;
}
}
return -1;
}
/**
* @returns {String}
* @param {Array<String>} nums
*/
export function numsToSizes(...nums) {
if (nums.length > 8) throw "MUST PROVIDE A 8 OR LESS RANGES";
const ranges = nums.map(findRangeIndex);
if (ranges.includes(-1)) throw `${nums} CONTAINS AN INVALID RANGE!`;
return `[${ranges.join("%2C")}]`;
}
/**
* @returns {String}
* @param {Array<1 | 2 | 3>} nums
*/
export const numToConDegs = (nums) => nums.map((n) => {
if (n === 1) return "F";
else if (n === 2) return "S";
else if (n === 3) return "O";
else return null;
}).filter(o => o);
// a "collection" of helper functions
class APIHelper {
parseCookiesToMap(cookies) {
const cookieMap = new Map();
cookies.forEach(cookie => {
const parts = cookie.split(';'); // Split cookie string into parts by ';'
const firstPart = parts[0]; // The first part contains the name and value
const [name, value] = firstPart.split('='); // Split the first part to get name and value
cookieMap.set(name.trim(), value.trim().replace(/"/g, '')); // Trim and remove quotes, then add to map
});
return cookieMap;
}
getCookies(username, password) {
return new Promise(async (resolve, reject) => {
const spawn = (await import("child_process")).spawn;
const pythonProcess = spawn('python', ["auth.py", username, password]);
pythonProcess.stdout.on('data', (data) => resolve(data.toString()));
});
}
/**
* @deprecated
*/
async getCookiesOld(username, password) {
const authurl = 'https://www.linkedin.com/uas/authenticate'
const res = await axios.get(authurl, {
headers: this.authheaders
});
const scookie = this.parseCookiesToMap(res.headers['set-cookie']);
const payload = {
session_key: username,
session_password: password,
JSESSIONID: scookie.get('JSESSIONID')
};
console.log(payload);
// this.authheaders['cookies'] = res.headers['set-cookie'];
try {
const response = await axios.post(
authurl,
payload,
{
withCredentials: true,
headers: this.authheaders,
cookies: cookieJar
// Commenting out the proxy setting as Axios requires special handling for proxies
// proxy: proxies
}
);
console.log(response.headers);
return response.data;
} catch (error) {
console.log(error);
console.error('Error during authentication:', error.response ? error.response.data : error.message);
}
}
constructor() {
this.authheaders = {
"X-Li-User-Agent": "LIAuthLibrary:0.0.3 com.linkedin.android:4.1.881 Asus_ASUS_Z01QD:android_9",
"User-Agent": "ANDROID OS",
"X-User-Language": "en",
"X-User-Locale": "en_us",
"Accept-Language": "en-us"
}
}
}
export default class linkedInAPIClass {
/**
* A function to try and trick the LinkedIn API rate limit/bot detector
*/
evade = () => wait(randomIntFromInterval(2, 5) * 1000);
/**
* @param {String} keyword i.e. "biotechnology"
* @param {Array<Number>?} numEmp
* @param {Number?} [start=0]
* @param {boolean?} [castToClass=true] whether the function should return a list of Company classes or just raw JSON
*/
async searchCompanies(keyword, numEmp, start = 0, castToClass = true, excludeGeneric = false) {
let urlExt = `variables=(start:${start},origin:GLOBAL_SEARCH_HEADER,query:(keywords:${keyword},flagshipSearchIntent:SEARCH_SRP,queryParameters:List((key:resultType,value:List(COMPANIES))),includeFiltersInResponse:false))`;
if (numEmp) urlExt += `&companySize=${numsToSizes(...numEmp)}`;
const r = await this._makeReq(urlExt);
if (!castToClass) return r;
else return parseResponse(r, this, excludeGeneric);
}
/**
* @returns {Promise<LinkedInProfile[]>}
* @param {String} keyword the user to search for
* @param {Number?} [limit=1000]
* @param {boolean?} [castToClass=true] whether the function should return a list of Company classes or just raw JSON
* @param {Array<String>?} currentCompanies
* @param {Array<1 | 2 | 3>?} conDeg the level(s) of connection to include (defults to all)
* @example
* // search for "John Appleseed" (who works at github), but only the first 50 results who are also first connections
* LAPI.searchEmployees("John Appleseed", 50, true, ["1418841"], [1])
*/
async searchEmployees(keyword, limit = 1000, castToClass = true, currentCompanies = [], conDeg = []) {
const empAll = [];
for (let i = 0; i < limit; i += 50) {
if (empAll.length >= limit) break;
let urlExt = `includeWebMetadata=true&variables=(start:${i},query:(keywords:${keyword},flagshipSearchIntent:SEARCH_SRP,queryParameters:List((key:resultType,value:List(PEOPLE)))))`;
if (currentCompanies.length) urlExt += `&currentCompany=["${currentCompanies.join('"%2C"')}"]`;
if (conDeg.length) {
const conFiltered = numToConDegs(conDeg);
if (conFiltered.length) urlExt += `network=["${currentCompanies.join('"%2C"')}"]}`
}
const r = await this._makeReq(urlExt);
if (!r) return [];
const filtered = r.included.filter(e => e.template === 'UNIVERSAL');
if (filtered.length) empAll.push(...filtered);
await this.evade();
}
if (!castToClass) return empAll;
else return empAll.map(o => new LinkedInProfile(o, this));
}
/**
* @param {String} reqPath
* @returns {Promise<Buffer | null>}
* @throws The promise will reject on error
*/
async _makeReq(reqPath, isProfile = false) {
await this.evade();
const res = await axios.get(`https://www.linkedin.com/voyager/api/graphql?${reqPath}&queryId=${(!isProfile) ? "voyagerSearchDashClusters.f0c4f21d8a526c4a5dd0ae253c9b6e02" : "voyagerIdentityDashProfiles.5a6722404e6afd08958f5105e51cad51"}`, { headers: this.headers }).catch((err) => {
console.error(err);
return null;
});
return res?.data;
}
// attempts to use cookies and otherwise uses auth
async login(username, password) {
let cookie;
if (this.resetCookies || !fs.existsSync("cookie.txt")) {
cookie = await this.helper.getCookies(username, password);
console.log(cookie);
if (cookie['login_result'] != "CHALLENGE") throw `\n\nIP IS NOT AUTHORIZED, PLEASE LOG IN THROUGH YOUR BROWSER USING:\nusername: ${username}\npassword: ${password}\n\n`;
if (cookie['login_result'] != "PASS") throw cookie;
// const sessioncookie = await this.#getCookies();
// throw "NO COOKIE FOUND!";
} else cookie = fs.readFileSync('cookie.txt');
this.headers = {
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:124.0) Gecko/20100101 Firefox/124.0',
'Accept': 'application/vnd.linkedin.normalized+json+2.1',
'Accept-Language': 'en-US,en;q=0.5',
'Accept-Encoding': 'gzip, deflate, br',
'x-li-lang': 'en_US',
'x-li-track': '{"clientVersion":"1.13.14725","mpVersion":"1.13.14725","osName":"web","timezoneOffset":-7,"timezone":"America/Los_Angeles","deviceFormFactor":"DESKTOP","mpName":"voyager-web","displayDensity":1,"displayWidth":1920,"displayHeight":1080}',
'x-li-page-instance': 'urn:li:page:d_flagship3_search_srp_jobs;TvndZ7TATTy2i/ZUyyD3Zg==',
'csrf-token': 'ajax:2538600735149500238',
'x-restli-protocol-version': '2.0.0',
'x-li-pem-metadata': 'Voyager - Organization - LCP_Member=interest-pipeline',
'Connection': 'keep-alive',
'Referer': 'https://www.linkedin.com/jobs/search/?currentJobId=3840672849&distance=25.0&geoId=103644278&keywords=temp&origin=HISTORY',
'Cookie': cookie,
'Sec-Fetch-Dest': 'empty',
'Sec-Fetch-Mode': 'cors',
'Sec-Fetch-Site': 'same-origin',
'TE': 'trailers'
};
}
constructor(resetCookies = false) {
this.resetCookies = resetCookies;
this.helper = new APIHelper();
}
}
+421
View File
@@ -0,0 +1,421 @@
{
"name": "linkedin-api",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "linkedin-api",
"version": "1.0.0",
"license": "ISC",
"dependencies": {
"axios": "^1.6.8",
"axios-cookiejar-support": "^5.0.1",
"cheerio": "^1.0.0-rc.12",
"tough-cookie": "^4.1.3"
}
},
"node_modules/agent-base": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz",
"integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==",
"dependencies": {
"debug": "^4.3.4"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
},
"node_modules/axios": {
"version": "1.6.8",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.6.8.tgz",
"integrity": "sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==",
"dependencies": {
"follow-redirects": "^1.15.6",
"form-data": "^4.0.0",
"proxy-from-env": "^1.1.0"
}
},
"node_modules/axios-cookiejar-support": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/axios-cookiejar-support/-/axios-cookiejar-support-5.0.1.tgz",
"integrity": "sha512-lwjyusKDJ1dLMRPnARWJCdcRXqmYvNITHnCQHRJGtIIL2QXj/lYPjMg04XZDp8QItzluu0KJqbjYlvI0+5X5Xw==",
"dependencies": {
"http-cookie-agent": "^6.0.3"
},
"engines": {
"node": ">=18.0.0"
},
"funding": {
"url": "https://github.com/sponsors/3846masa"
},
"peerDependencies": {
"axios": ">=0.20.0",
"tough-cookie": ">=4.0.0"
}
},
"node_modules/boolbase": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
"integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="
},
"node_modules/cheerio": {
"version": "1.0.0-rc.12",
"resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz",
"integrity": "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==",
"dependencies": {
"cheerio-select": "^2.1.0",
"dom-serializer": "^2.0.0",
"domhandler": "^5.0.3",
"domutils": "^3.0.1",
"htmlparser2": "^8.0.1",
"parse5": "^7.0.0",
"parse5-htmlparser2-tree-adapter": "^7.0.0"
},
"engines": {
"node": ">= 6"
},
"funding": {
"url": "https://github.com/cheeriojs/cheerio?sponsor=1"
}
},
"node_modules/cheerio-select": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz",
"integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==",
"dependencies": {
"boolbase": "^1.0.0",
"css-select": "^5.1.0",
"css-what": "^6.1.0",
"domelementtype": "^2.3.0",
"domhandler": "^5.0.3",
"domutils": "^3.0.1"
},
"funding": {
"url": "https://github.com/sponsors/fb55"
}
},
"node_modules/combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"dependencies": {
"delayed-stream": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/css-select": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz",
"integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==",
"dependencies": {
"boolbase": "^1.0.0",
"css-what": "^6.1.0",
"domhandler": "^5.0.2",
"domutils": "^3.0.1",
"nth-check": "^2.0.1"
},
"funding": {
"url": "https://github.com/sponsors/fb55"
}
},
"node_modules/css-what": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz",
"integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==",
"engines": {
"node": ">= 6"
},
"funding": {
"url": "https://github.com/sponsors/fb55"
}
},
"node_modules/debug": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
"dependencies": {
"ms": "2.1.2"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/dom-serializer": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
"integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
"dependencies": {
"domelementtype": "^2.3.0",
"domhandler": "^5.0.2",
"entities": "^4.2.0"
},
"funding": {
"url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
}
},
"node_modules/domelementtype": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
"integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/fb55"
}
]
},
"node_modules/domhandler": {
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
"integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
"dependencies": {
"domelementtype": "^2.3.0"
},
"engines": {
"node": ">= 4"
},
"funding": {
"url": "https://github.com/fb55/domhandler?sponsor=1"
}
},
"node_modules/domutils": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz",
"integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==",
"dependencies": {
"dom-serializer": "^2.0.0",
"domelementtype": "^2.3.0",
"domhandler": "^5.0.3"
},
"funding": {
"url": "https://github.com/fb55/domutils?sponsor=1"
}
},
"node_modules/entities": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
"engines": {
"node": ">=0.12"
},
"funding": {
"url": "https://github.com/fb55/entities?sponsor=1"
}
},
"node_modules/follow-redirects": {
"version": "1.15.6",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz",
"integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==",
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/RubenVerborgh"
}
],
"engines": {
"node": ">=4.0"
},
"peerDependenciesMeta": {
"debug": {
"optional": true
}
}
},
"node_modules/form-data": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"mime-types": "^2.1.12"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/htmlparser2": {
"version": "8.0.2",
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz",
"integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==",
"funding": [
"https://github.com/fb55/htmlparser2?sponsor=1",
{
"type": "github",
"url": "https://github.com/sponsors/fb55"
}
],
"dependencies": {
"domelementtype": "^2.3.0",
"domhandler": "^5.0.3",
"domutils": "^3.0.1",
"entities": "^4.4.0"
}
},
"node_modules/http-cookie-agent": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/http-cookie-agent/-/http-cookie-agent-6.0.3.tgz",
"integrity": "sha512-6JdymEgWgsg9VQ5VN9FGpRRcivyu4WdM0Ud3kW+Q0PB7knt0EFtlhNPU8wCuscXLfIEI5y6jEMdFTBODNsJR6g==",
"dependencies": {
"agent-base": "^7.1.1"
},
"engines": {
"node": ">=18.0.0"
},
"funding": {
"url": "https://github.com/sponsors/3846masa"
},
"peerDependencies": {
"deasync": "^0.1.26",
"tough-cookie": "^4.0.0",
"undici": "^5.11.0 || ^6.0.0"
},
"peerDependenciesMeta": {
"deasync": {
"optional": true
},
"undici": {
"optional": true
}
}
},
"node_modules/mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime-types": {
"version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"dependencies": {
"mime-db": "1.52.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
},
"node_modules/nth-check": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
"integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
"dependencies": {
"boolbase": "^1.0.0"
},
"funding": {
"url": "https://github.com/fb55/nth-check?sponsor=1"
}
},
"node_modules/parse5": {
"version": "7.1.2",
"resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz",
"integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==",
"dependencies": {
"entities": "^4.4.0"
},
"funding": {
"url": "https://github.com/inikulin/parse5?sponsor=1"
}
},
"node_modules/parse5-htmlparser2-tree-adapter": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz",
"integrity": "sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==",
"dependencies": {
"domhandler": "^5.0.2",
"parse5": "^7.0.0"
},
"funding": {
"url": "https://github.com/inikulin/parse5?sponsor=1"
}
},
"node_modules/proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
},
"node_modules/psl": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz",
"integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag=="
},
"node_modules/punycode": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
"engines": {
"node": ">=6"
}
},
"node_modules/querystringify": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
"integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ=="
},
"node_modules/requires-port": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
"integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ=="
},
"node_modules/tough-cookie": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz",
"integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==",
"dependencies": {
"psl": "^1.1.33",
"punycode": "^2.1.1",
"universalify": "^0.2.0",
"url-parse": "^1.5.3"
},
"engines": {
"node": ">=6"
}
},
"node_modules/tough-cookie/node_modules/universalify": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz",
"integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==",
"engines": {
"node": ">= 4.0.0"
}
},
"node_modules/url-parse": {
"version": "1.5.10",
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz",
"integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==",
"dependencies": {
"querystringify": "^2.1.1",
"requires-port": "^1.0.0"
}
}
}
}
+18
View File
@@ -0,0 +1,18 @@
{
"dependencies": {
"axios": "^1.6.8",
"axios-cookiejar-support": "^5.0.1",
"cheerio": "^1.0.0-rc.12",
"tough-cookie": "^4.1.3"
},
"type": "module",
"name": "linkedin-api-js",
"version": "1.0.0-0",
"main": "APIMain.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "ION606",
"license": "ISC",
"description": ""
}
+1
View File
@@ -0,0 +1 @@
declare module 'linkedin-api';
+8
View File
@@ -0,0 +1,8 @@
{
"compilerOptions": {
"typeRoots": [
"./node_modules/@types",
"./src/types"
]
}
}