@entergreat/unipile-wrapper 2.1.4 → 2.1.5
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/package.json +7 -15
- package/src/client/httpClient.ts +0 -61
- package/src/client/index.ts +0 -1
- package/src/index.ts +0 -31
- package/src/services/accountService.ts +0 -21
- package/src/services/baseService.ts +0 -31
- package/src/services/companyService.ts +0 -66
- package/src/services/index.ts +0 -5
- package/src/services/profileService.ts +0 -177
- package/src/services/searchService.ts +0 -88
- package/src/types/account.ts +0 -9
- package/src/types/common.ts +0 -21
- package/src/types/company.ts +0 -41
- package/src/types/index.ts +0 -5
- package/src/types/profile.ts +0 -69
- package/src/types/search.ts +0 -27
- package/src/utils/helpers.ts +0 -41
- package/src/utils/index.ts +0 -3
- package/src/utils/urlBuilder.ts +0 -37
- package/src/utils/validators.ts +0 -47
- package/tsconfig.json +0 -21
package/package.json
CHANGED
|
@@ -1,32 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@entergreat/unipile-wrapper",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.5",
|
|
4
4
|
"description": "A lightweight TypeScript wrapper for Unipile's LinkedIn API",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"type": "module",
|
|
8
|
+
"files": ["dist/"],
|
|
9
|
+
"publishConfig": { "access": "restricted" },
|
|
8
10
|
"scripts": {
|
|
9
11
|
"build": "tsc",
|
|
10
12
|
"build:watch": "tsc --watch",
|
|
11
|
-
"prepublishOnly": "npm run build"
|
|
12
|
-
"test": "echo \"Error: no test specified\" && exit 1"
|
|
13
|
+
"prepublishOnly": "npm run build"
|
|
13
14
|
},
|
|
14
15
|
"repository": {
|
|
15
16
|
"type": "git",
|
|
16
|
-
"url": "
|
|
17
|
+
"url": "https://github.com/entergreat/unipile-wrapper.git"
|
|
17
18
|
},
|
|
18
|
-
"keywords": [
|
|
19
|
-
|
|
20
|
-
"linkedin",
|
|
21
|
-
"automation",
|
|
22
|
-
"api-wrapper"
|
|
23
|
-
],
|
|
24
|
-
"author": "",
|
|
19
|
+
"keywords": ["unipile", "linkedin", "automation", "api-wrapper", "entergreat"],
|
|
20
|
+
"author": "EnterGreat",
|
|
25
21
|
"license": "MIT",
|
|
26
|
-
"bugs": {
|
|
27
|
-
"url": "https://github.com/nivkman/unipile-wrapper/issues"
|
|
28
|
-
},
|
|
29
|
-
"homepage": "https://github.com/nivkman/unipile-wrapper#readme",
|
|
30
22
|
"dependencies": {
|
|
31
23
|
"axios": "^1.7.9",
|
|
32
24
|
"form-data": "^4.0.0"
|
package/src/client/httpClient.ts
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import axios, { AxiosInstance, AxiosRequestConfig } from "axios";
|
|
2
|
-
import FormData from "form-data";
|
|
3
|
-
import { ServiceConfig } from "../types/index.js";
|
|
4
|
-
|
|
5
|
-
export class HttpClient {
|
|
6
|
-
private client: AxiosInstance;
|
|
7
|
-
private apiKey: string;
|
|
8
|
-
|
|
9
|
-
constructor(config: ServiceConfig) {
|
|
10
|
-
const baseURL = `https://${config.subdomain}.unipile.com:${config.port}/api/v1`;
|
|
11
|
-
this.apiKey = config.unipileToken;
|
|
12
|
-
|
|
13
|
-
this.client = axios.create({
|
|
14
|
-
baseURL,
|
|
15
|
-
headers: {
|
|
16
|
-
accept: "application/json",
|
|
17
|
-
"content-type": "application/json",
|
|
18
|
-
},
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
this.setupInterceptors();
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
private setupInterceptors(): void {
|
|
25
|
-
this.client.interceptors.request.use((config) => {
|
|
26
|
-
config.headers["X-API-KEY"] = this.apiKey;
|
|
27
|
-
return config;
|
|
28
|
-
});
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
get baseURL(): string {
|
|
32
|
-
return this.client.defaults.baseURL || "";
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
async get<T>(url: string): Promise<T> {
|
|
36
|
-
const response = await this.client.get<T>(url);
|
|
37
|
-
return response.data;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
async post<T>(url: string, body: Record<string, unknown> = {}): Promise<T> {
|
|
41
|
-
const response = await this.client.post<T>(url, body);
|
|
42
|
-
return response.data;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
async patch<T>(url: string, body: Record<string, unknown> = {}): Promise<T> {
|
|
46
|
-
const response = await this.client.patch<T>(url, body);
|
|
47
|
-
return response.data;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
async patchFormData<T>(url: string, formData: FormData): Promise<T> {
|
|
51
|
-
const config: AxiosRequestConfig = {
|
|
52
|
-
headers: {
|
|
53
|
-
...formData.getHeaders(),
|
|
54
|
-
"X-API-KEY": this.apiKey,
|
|
55
|
-
accept: "application/json",
|
|
56
|
-
},
|
|
57
|
-
};
|
|
58
|
-
const response = await this.client.patch<T>(url, formData, config);
|
|
59
|
-
return response.data;
|
|
60
|
-
}
|
|
61
|
-
}
|
package/src/client/index.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from "./httpClient.js";
|
package/src/index.ts
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { HttpClient } from "./client/index.js";
|
|
2
|
-
import {
|
|
3
|
-
AccountService,
|
|
4
|
-
SearchService,
|
|
5
|
-
ProfileService,
|
|
6
|
-
CompanyService,
|
|
7
|
-
} from "./services/index.js";
|
|
8
|
-
import { ServiceConfig } from "./types/index.js";
|
|
9
|
-
|
|
10
|
-
export class LinkedinService {
|
|
11
|
-
public account: AccountService;
|
|
12
|
-
public search: SearchService;
|
|
13
|
-
public profile: ProfileService;
|
|
14
|
-
public company: CompanyService;
|
|
15
|
-
|
|
16
|
-
constructor(config: ServiceConfig) {
|
|
17
|
-
const client = new HttpClient(config);
|
|
18
|
-
const { accountId } = config;
|
|
19
|
-
|
|
20
|
-
this.account = new AccountService(client, accountId);
|
|
21
|
-
this.search = new SearchService(client, accountId);
|
|
22
|
-
this.profile = new ProfileService(client, accountId);
|
|
23
|
-
this.company = new CompanyService(client, accountId);
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// Re-export types for consumers
|
|
28
|
-
export * from "./types/index.js";
|
|
29
|
-
|
|
30
|
-
// Re-export utilities that might be useful
|
|
31
|
-
export { ValidationError } from "./utils/validators.js";
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { HttpClient } from "../client/index.js";
|
|
2
|
-
import { BaseService } from "./baseService.js";
|
|
3
|
-
import { ConnectParams, ConnectResponse } from "../types/index.js";
|
|
4
|
-
|
|
5
|
-
export class AccountService extends BaseService {
|
|
6
|
-
constructor(client: HttpClient, defaultAccountId?: string) {
|
|
7
|
-
super(client, defaultAccountId);
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Connect a LinkedIn account to Unipile using a li_at cookie
|
|
12
|
-
*/
|
|
13
|
-
async connect({ loginCookie }: ConnectParams): Promise<ConnectResponse> {
|
|
14
|
-
const body = {
|
|
15
|
-
provider: "LINKEDIN",
|
|
16
|
-
access_token: loginCookie,
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
return this.client.post<ConnectResponse>("/accounts", body);
|
|
20
|
-
}
|
|
21
|
-
}
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { HttpClient } from "../client/index.js";
|
|
2
|
-
import { buildUrl, appendArrayParams, ValidationError } from "../utils/index.js";
|
|
3
|
-
|
|
4
|
-
export abstract class BaseService {
|
|
5
|
-
protected client: HttpClient;
|
|
6
|
-
protected defaultAccountId?: string;
|
|
7
|
-
|
|
8
|
-
constructor(client: HttpClient, defaultAccountId?: string) {
|
|
9
|
-
this.client = client;
|
|
10
|
-
this.defaultAccountId = defaultAccountId;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
protected resolveAccountId(accountId?: string): string {
|
|
14
|
-
const resolved = accountId ?? this.defaultAccountId;
|
|
15
|
-
if (!resolved) {
|
|
16
|
-
throw new ValidationError("accountId must be provided either in config or method call");
|
|
17
|
-
}
|
|
18
|
-
return resolved;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
protected buildUrl(
|
|
22
|
-
path: string,
|
|
23
|
-
params: Record<string, string | number | boolean | undefined> = {}
|
|
24
|
-
): string {
|
|
25
|
-
return buildUrl(this.client.baseURL, path, params);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
protected appendArrayParams(url: string, key: string, values: string[]): string {
|
|
29
|
-
return appendArrayParams(url, key, values);
|
|
30
|
-
}
|
|
31
|
-
}
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import { HttpClient } from "../client/index.js";
|
|
2
|
-
import { BaseService } from "./baseService.js";
|
|
3
|
-
import {
|
|
4
|
-
RetrieveCompanyRequest,
|
|
5
|
-
SearchCompanyRequest,
|
|
6
|
-
CompanyResponse,
|
|
7
|
-
CompanySearchResponse,
|
|
8
|
-
} from "../types/index.js";
|
|
9
|
-
import { validateCompanyIdOrUrl, extractCompanyIdFromUrl } from "../utils/index.js";
|
|
10
|
-
|
|
11
|
-
export class CompanyService extends BaseService {
|
|
12
|
-
constructor(client: HttpClient, defaultAccountId?: string) {
|
|
13
|
-
super(client, defaultAccountId);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Retrieve LinkedIn company information by ID or URL
|
|
18
|
-
*/
|
|
19
|
-
async retrieve({
|
|
20
|
-
accountId,
|
|
21
|
-
companyId,
|
|
22
|
-
companyUrl,
|
|
23
|
-
}: RetrieveCompanyRequest): Promise<CompanyResponse> {
|
|
24
|
-
const resolvedAccountId = this.resolveAccountId(accountId);
|
|
25
|
-
validateCompanyIdOrUrl(companyId, companyUrl);
|
|
26
|
-
|
|
27
|
-
let id = companyId;
|
|
28
|
-
if (!id && companyUrl) {
|
|
29
|
-
id = extractCompanyIdFromUrl(companyUrl) || undefined;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
if (!id) {
|
|
33
|
-
throw new Error("Could not extract company ID from URL");
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const url = this.buildUrl(`/linkedin/company/${id}`, {
|
|
37
|
-
account_id: resolvedAccountId,
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
return this.client.get<CompanyResponse>(url);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Search for companies by name/keywords
|
|
45
|
-
*/
|
|
46
|
-
async search({
|
|
47
|
-
accountId,
|
|
48
|
-
keywords,
|
|
49
|
-
limit = 5,
|
|
50
|
-
}: SearchCompanyRequest): Promise<CompanySearchResponse> {
|
|
51
|
-
const resolvedAccountId = this.resolveAccountId(accountId);
|
|
52
|
-
|
|
53
|
-
if (!keywords) {
|
|
54
|
-
throw new Error("keywords is required for company search");
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
const url = this.buildUrl("/linkedin/search/parameters", {
|
|
58
|
-
account_id: resolvedAccountId,
|
|
59
|
-
keywords,
|
|
60
|
-
type: "COMPANY",
|
|
61
|
-
limit,
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
return this.client.get<CompanySearchResponse>(url);
|
|
65
|
-
}
|
|
66
|
-
}
|
package/src/services/index.ts
DELETED
|
@@ -1,177 +0,0 @@
|
|
|
1
|
-
import FormData from "form-data";
|
|
2
|
-
import { HttpClient } from "../client/index.js";
|
|
3
|
-
import { BaseService } from "./baseService.js";
|
|
4
|
-
import {
|
|
5
|
-
RetrieveProfileRequest,
|
|
6
|
-
EditProfileRequest,
|
|
7
|
-
AddExperienceRequest,
|
|
8
|
-
EditExperienceRequest,
|
|
9
|
-
ProfileResponse,
|
|
10
|
-
} from "../types/index.js";
|
|
11
|
-
import {
|
|
12
|
-
validateProfileId,
|
|
13
|
-
validateProfileData,
|
|
14
|
-
validateExperience,
|
|
15
|
-
convertToBracketNotation,
|
|
16
|
-
} from "../utils/index.js";
|
|
17
|
-
|
|
18
|
-
export class ProfileService extends BaseService {
|
|
19
|
-
constructor(client: HttpClient, defaultAccountId?: string) {
|
|
20
|
-
super(client, defaultAccountId);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Retrieve a LinkedIn user profile
|
|
25
|
-
*/
|
|
26
|
-
async retrieve({
|
|
27
|
-
accountId,
|
|
28
|
-
profileId,
|
|
29
|
-
linkedinSections = [],
|
|
30
|
-
notify = false,
|
|
31
|
-
}: RetrieveProfileRequest): Promise<ProfileResponse> {
|
|
32
|
-
const resolvedAccountId = this.resolveAccountId(accountId);
|
|
33
|
-
validateProfileId(profileId);
|
|
34
|
-
|
|
35
|
-
let url = this.buildUrl(`/users/${profileId}`, {
|
|
36
|
-
account_id: resolvedAccountId,
|
|
37
|
-
notify,
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
if (linkedinSections.length > 0) {
|
|
41
|
-
url = this.appendArrayParams(url, "linkedin_sections", linkedinSections);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
return this.client.get<ProfileResponse>(url);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Edit the LinkedIn profile of the account owner
|
|
49
|
-
*/
|
|
50
|
-
async edit({ accountId, profileData }: EditProfileRequest): Promise<ProfileResponse> {
|
|
51
|
-
const resolvedAccountId = this.resolveAccountId(accountId);
|
|
52
|
-
validateProfileData(profileData);
|
|
53
|
-
|
|
54
|
-
const url = this.buildUrl("/users/me/edit", {
|
|
55
|
-
account_id: resolvedAccountId,
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
const body = convertToBracketNotation(profileData);
|
|
59
|
-
|
|
60
|
-
return this.client.patch<ProfileResponse>(url, body);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Add a work experience position to the profile
|
|
65
|
-
*/
|
|
66
|
-
async addExperience({
|
|
67
|
-
accountId,
|
|
68
|
-
experience,
|
|
69
|
-
}: AddExperienceRequest): Promise<ProfileResponse> {
|
|
70
|
-
const resolvedAccountId = this.resolveAccountId(accountId);
|
|
71
|
-
validateExperience(experience);
|
|
72
|
-
|
|
73
|
-
const url = this.buildUrl("/users/me/edit", {
|
|
74
|
-
account_id: resolvedAccountId,
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
const formData = this.buildExperienceFormData(resolvedAccountId, experience);
|
|
78
|
-
|
|
79
|
-
return this.client.patchFormData<ProfileResponse>(url, formData);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Edit an existing work experience position
|
|
84
|
-
*/
|
|
85
|
-
async editExperience({
|
|
86
|
-
accountId,
|
|
87
|
-
experienceId,
|
|
88
|
-
experience,
|
|
89
|
-
}: EditExperienceRequest): Promise<ProfileResponse> {
|
|
90
|
-
const resolvedAccountId = this.resolveAccountId(accountId);
|
|
91
|
-
|
|
92
|
-
if (!experienceId) {
|
|
93
|
-
throw new Error("experienceId is required");
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
const url = this.buildUrl("/users/me/edit", {
|
|
97
|
-
account_id: resolvedAccountId,
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
const formData = this.buildExperienceFormData(resolvedAccountId, experience, experienceId);
|
|
101
|
-
|
|
102
|
-
return this.client.patchFormData<ProfileResponse>(url, formData);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
private buildExperienceFormData(
|
|
106
|
-
accountId: string,
|
|
107
|
-
experience: AddExperienceRequest["experience"] | EditExperienceRequest["experience"],
|
|
108
|
-
experienceId?: string
|
|
109
|
-
): FormData {
|
|
110
|
-
const formData = new FormData();
|
|
111
|
-
|
|
112
|
-
formData.append("type", "LINKEDIN");
|
|
113
|
-
formData.append("account_id", accountId);
|
|
114
|
-
|
|
115
|
-
// If editing, include the experience ID
|
|
116
|
-
if (experienceId) {
|
|
117
|
-
formData.append("experience[id]", experienceId);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
if (experience.role) {
|
|
121
|
-
formData.append("experience[role]", experience.role);
|
|
122
|
-
}
|
|
123
|
-
if (experience.company) {
|
|
124
|
-
formData.append("experience[company]", experience.company);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
if (experience.company_id) {
|
|
128
|
-
formData.append("company_id", experience.company_id);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
if (experience.description) {
|
|
132
|
-
formData.append("description", experience.description);
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
if (experience.location) {
|
|
136
|
-
formData.append("location", experience.location);
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
if (experience.notify_network !== undefined) {
|
|
140
|
-
formData.append("notify_network", experience.notify_network.toString());
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
if (experience.start_month) {
|
|
144
|
-
formData.append(
|
|
145
|
-
"experience[seniority][start_date][month]",
|
|
146
|
-
experience.start_month.toString()
|
|
147
|
-
);
|
|
148
|
-
}
|
|
149
|
-
if (experience.start_year) {
|
|
150
|
-
formData.append(
|
|
151
|
-
"experience[seniority][start_date][year]",
|
|
152
|
-
experience.start_year.toString()
|
|
153
|
-
);
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
if (experience.end_month) {
|
|
157
|
-
formData.append(
|
|
158
|
-
"experience[seniority][end_date][month]",
|
|
159
|
-
experience.end_month.toString()
|
|
160
|
-
);
|
|
161
|
-
}
|
|
162
|
-
if (experience.end_year) {
|
|
163
|
-
formData.append(
|
|
164
|
-
"experience[seniority][end_date][year]",
|
|
165
|
-
experience.end_year.toString()
|
|
166
|
-
);
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
if (experience.skills && Array.isArray(experience.skills)) {
|
|
170
|
-
for (const skill of experience.skills) {
|
|
171
|
-
formData.append("skills", skill);
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
return formData;
|
|
176
|
-
}
|
|
177
|
-
}
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
import { HttpClient } from "../client/index.js";
|
|
2
|
-
import { BaseService } from "./baseService.js";
|
|
3
|
-
import {
|
|
4
|
-
SearchByParamsRequest,
|
|
5
|
-
SearchByUrlRequest,
|
|
6
|
-
RetrieveSearchParametersRequest,
|
|
7
|
-
SearchResult,
|
|
8
|
-
SearchParametersResult,
|
|
9
|
-
} from "../types/index.js";
|
|
10
|
-
|
|
11
|
-
export class SearchService extends BaseService {
|
|
12
|
-
constructor(client: HttpClient, defaultAccountId?: string) {
|
|
13
|
-
super(client, defaultAccountId);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Perform a LinkedIn search using parameters
|
|
18
|
-
*/
|
|
19
|
-
async byParams({
|
|
20
|
-
accountId,
|
|
21
|
-
params = {},
|
|
22
|
-
cursor,
|
|
23
|
-
limit = 10,
|
|
24
|
-
}: SearchByParamsRequest): Promise<SearchResult> {
|
|
25
|
-
const resolvedAccountId = this.resolveAccountId(accountId);
|
|
26
|
-
|
|
27
|
-
const url = this.buildUrl("/linkedin/search", {
|
|
28
|
-
account_id: resolvedAccountId,
|
|
29
|
-
limit,
|
|
30
|
-
cursor,
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
const body = {
|
|
34
|
-
...params,
|
|
35
|
-
api: "classic",
|
|
36
|
-
category: "people",
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
return this.client.post<SearchResult>(url, body);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Perform a LinkedIn search using a LinkedIn URL
|
|
44
|
-
*/
|
|
45
|
-
async byUrl({
|
|
46
|
-
accountId,
|
|
47
|
-
searchUrl,
|
|
48
|
-
cursor,
|
|
49
|
-
limit = 10,
|
|
50
|
-
}: SearchByUrlRequest): Promise<SearchResult> {
|
|
51
|
-
const resolvedAccountId = this.resolveAccountId(accountId);
|
|
52
|
-
|
|
53
|
-
const url = this.buildUrl("/linkedin/search", {
|
|
54
|
-
account_id: resolvedAccountId,
|
|
55
|
-
limit,
|
|
56
|
-
cursor,
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
const body = {
|
|
60
|
-
api: "classic",
|
|
61
|
-
category: "people",
|
|
62
|
-
url: searchUrl,
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
return this.client.post<SearchResult>(url, body);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Retrieve LinkedIn search parameter suggestions
|
|
70
|
-
*/
|
|
71
|
-
async retrieveParameters({
|
|
72
|
-
accountId,
|
|
73
|
-
type,
|
|
74
|
-
keywords = "",
|
|
75
|
-
limit = 3,
|
|
76
|
-
}: RetrieveSearchParametersRequest): Promise<SearchParametersResult> {
|
|
77
|
-
const resolvedAccountId = this.resolveAccountId(accountId);
|
|
78
|
-
|
|
79
|
-
const url = this.buildUrl("/linkedin/search/parameters", {
|
|
80
|
-
account_id: resolvedAccountId,
|
|
81
|
-
keywords,
|
|
82
|
-
type: type.toUpperCase(),
|
|
83
|
-
limit,
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
return this.client.get<SearchParametersResult>(url);
|
|
87
|
-
}
|
|
88
|
-
}
|
package/src/types/account.ts
DELETED
package/src/types/common.ts
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
export interface ServiceConfig {
|
|
2
|
-
unipileToken: string;
|
|
3
|
-
subdomain: string;
|
|
4
|
-
port: number | string;
|
|
5
|
-
accountId?: string;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export interface RequestHeaders {
|
|
9
|
-
accept: string;
|
|
10
|
-
"content-type": string;
|
|
11
|
-
"X-API-KEY": string;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export interface PaginationParams {
|
|
15
|
-
cursor?: string;
|
|
16
|
-
limit?: number;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export interface AccountIdParam {
|
|
20
|
-
accountId?: string;
|
|
21
|
-
}
|
package/src/types/company.ts
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import { AccountIdParam } from "./common.js";
|
|
2
|
-
|
|
3
|
-
export interface RetrieveCompanyRequest extends AccountIdParam {
|
|
4
|
-
companyId?: string;
|
|
5
|
-
companyUrl?: string;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export interface SearchCompanyRequest extends AccountIdParam {
|
|
9
|
-
keywords: string;
|
|
10
|
-
limit?: number;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export interface CompanySearchResult {
|
|
14
|
-
id: string;
|
|
15
|
-
name: string;
|
|
16
|
-
linkedin_url?: string;
|
|
17
|
-
logo_url?: string;
|
|
18
|
-
industry?: string;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export interface CompanySearchResponse {
|
|
22
|
-
object: string;
|
|
23
|
-
items: CompanySearchResult[];
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export interface CompanyResponse {
|
|
27
|
-
object: string;
|
|
28
|
-
id: string;
|
|
29
|
-
name?: string;
|
|
30
|
-
industry?: string;
|
|
31
|
-
company_size?: string;
|
|
32
|
-
description?: string;
|
|
33
|
-
linkedin_url?: string;
|
|
34
|
-
website?: string;
|
|
35
|
-
locations?: Array<{
|
|
36
|
-
id?: string;
|
|
37
|
-
city?: string;
|
|
38
|
-
country?: string;
|
|
39
|
-
name?: string;
|
|
40
|
-
}>;
|
|
41
|
-
}
|
package/src/types/index.ts
DELETED
package/src/types/profile.ts
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
import { AccountIdParam } from "./common.js";
|
|
2
|
-
|
|
3
|
-
export interface RetrieveProfileRequest extends AccountIdParam {
|
|
4
|
-
profileId: string;
|
|
5
|
-
linkedinSections?: string[];
|
|
6
|
-
notify?: boolean;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export interface ProfileData {
|
|
10
|
-
headline?: string;
|
|
11
|
-
summary?: string;
|
|
12
|
-
location?: { id: string };
|
|
13
|
-
picture?: Record<string, unknown>;
|
|
14
|
-
experience?: {
|
|
15
|
-
skills?: string[];
|
|
16
|
-
[key: string]: unknown;
|
|
17
|
-
};
|
|
18
|
-
education?: Record<string, unknown>;
|
|
19
|
-
[key: string]: unknown;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export interface EditProfileRequest extends AccountIdParam {
|
|
23
|
-
profileData: ProfileData;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export interface ExperienceData {
|
|
27
|
-
role: string;
|
|
28
|
-
company: string;
|
|
29
|
-
company_id?: string;
|
|
30
|
-
description?: string;
|
|
31
|
-
location?: string;
|
|
32
|
-
start_month?: number;
|
|
33
|
-
start_year?: number;
|
|
34
|
-
end_month?: number;
|
|
35
|
-
end_year?: number;
|
|
36
|
-
skills?: string[];
|
|
37
|
-
notify_network?: boolean;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export interface AddExperienceRequest extends AccountIdParam {
|
|
41
|
-
experience: ExperienceData;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export interface EditExperienceRequest extends AccountIdParam {
|
|
45
|
-
experienceId: string;
|
|
46
|
-
experience: Partial<ExperienceData>;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export interface ProfileResponse {
|
|
50
|
-
object: string;
|
|
51
|
-
id: string;
|
|
52
|
-
name?: string;
|
|
53
|
-
headline?: string;
|
|
54
|
-
occupation?: string;
|
|
55
|
-
about?: string;
|
|
56
|
-
work_experience?: WorkExperience[];
|
|
57
|
-
work_experience_total_count?: number;
|
|
58
|
-
public_identifier?: string;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
export interface WorkExperience {
|
|
62
|
-
id?: string;
|
|
63
|
-
title?: string;
|
|
64
|
-
company_name?: string;
|
|
65
|
-
company_id?: string;
|
|
66
|
-
description?: string;
|
|
67
|
-
start_date?: { month?: number; year?: number };
|
|
68
|
-
end_date?: { month?: number; year?: number };
|
|
69
|
-
}
|
package/src/types/search.ts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { AccountIdParam, PaginationParams } from "./common.js";
|
|
2
|
-
|
|
3
|
-
export interface SearchByParamsRequest extends AccountIdParam, PaginationParams {
|
|
4
|
-
params?: Record<string, unknown>;
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
export interface SearchByUrlRequest extends AccountIdParam, PaginationParams {
|
|
8
|
-
searchUrl: string;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export interface RetrieveSearchParametersRequest extends AccountIdParam {
|
|
12
|
-
type: string;
|
|
13
|
-
keywords?: string;
|
|
14
|
-
limit?: number;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export interface SearchResult {
|
|
18
|
-
object: string;
|
|
19
|
-
items: unknown[];
|
|
20
|
-
cursor?: string;
|
|
21
|
-
// Add more fields as needed
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export interface SearchParametersResult {
|
|
25
|
-
object: string;
|
|
26
|
-
items: unknown[];
|
|
27
|
-
}
|
package/src/utils/helpers.ts
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Extracts company ID from a LinkedIn company URL
|
|
3
|
-
* Handles URLs like:
|
|
4
|
-
* - https://www.linkedin.com/company/company-name/
|
|
5
|
-
* - https://www.linkedin.com/company/12345678/
|
|
6
|
-
* - https://linkedin.com/company/company-name
|
|
7
|
-
*/
|
|
8
|
-
export function extractCompanyIdFromUrl(url: string): string | null {
|
|
9
|
-
const match = url.match(/linkedin\.com\/company\/([^\/\?]+)/i);
|
|
10
|
-
return match ? match[1] : null;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Converts nested objects to bracket notation for API compatibility
|
|
15
|
-
* Example: { experience: { role: "Dev" } } -> { "experience[role]": "Dev" }
|
|
16
|
-
*/
|
|
17
|
-
export function convertToBracketNotation(
|
|
18
|
-
obj: Record<string, unknown>,
|
|
19
|
-
prefix = ""
|
|
20
|
-
): Record<string, unknown> {
|
|
21
|
-
const result: Record<string, unknown> = {};
|
|
22
|
-
|
|
23
|
-
for (const key in obj) {
|
|
24
|
-
if (obj[key] === undefined) continue;
|
|
25
|
-
|
|
26
|
-
const fullKey = prefix ? `${prefix}[${key}]` : key;
|
|
27
|
-
|
|
28
|
-
if (Array.isArray(obj[key])) {
|
|
29
|
-
result[fullKey] = obj[key];
|
|
30
|
-
} else if (typeof obj[key] === "object" && obj[key] !== null) {
|
|
31
|
-
Object.assign(
|
|
32
|
-
result,
|
|
33
|
-
convertToBracketNotation(obj[key] as Record<string, unknown>, fullKey)
|
|
34
|
-
);
|
|
35
|
-
} else {
|
|
36
|
-
result[fullKey] = obj[key];
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
return result;
|
|
41
|
-
}
|
package/src/utils/index.ts
DELETED
package/src/utils/urlBuilder.ts
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
type QueryParams = Record<string, string | number | boolean | undefined>;
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Builds a URL with query parameters, filtering out undefined values
|
|
5
|
-
* Properly joins baseUrl path with the given path
|
|
6
|
-
*/
|
|
7
|
-
export function buildUrl(baseUrl: string, path: string, params: QueryParams = {}): string {
|
|
8
|
-
// Parse the base URL to get its components
|
|
9
|
-
const base = new URL(baseUrl);
|
|
10
|
-
|
|
11
|
-
// Join the base path with the new path (remove leading slash from path if base has trailing content)
|
|
12
|
-
const basePath = base.pathname.endsWith('/') ? base.pathname.slice(0, -1) : base.pathname;
|
|
13
|
-
const newPath = path.startsWith('/') ? path : `/${path}`;
|
|
14
|
-
base.pathname = basePath + newPath;
|
|
15
|
-
|
|
16
|
-
// Add query parameters
|
|
17
|
-
for (const [key, value] of Object.entries(params)) {
|
|
18
|
-
if (value !== undefined && value !== "") {
|
|
19
|
-
base.searchParams.append(key, String(value));
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
return base.toString();
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Appends array values as repeated query parameters
|
|
28
|
-
*/
|
|
29
|
-
export function appendArrayParams(url: string, key: string, values: string[]): string {
|
|
30
|
-
const urlObj = new URL(url);
|
|
31
|
-
|
|
32
|
-
for (const value of values) {
|
|
33
|
-
urlObj.searchParams.append(key, value);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
return urlObj.toString();
|
|
37
|
-
}
|
package/src/utils/validators.ts
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
export class ValidationError extends Error {
|
|
2
|
-
constructor(message: string) {
|
|
3
|
-
super(message);
|
|
4
|
-
this.name = "ValidationError";
|
|
5
|
-
}
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export function validateRequired(
|
|
9
|
-
params: Record<string, unknown>,
|
|
10
|
-
requiredFields: string[]
|
|
11
|
-
): void {
|
|
12
|
-
for (const field of requiredFields) {
|
|
13
|
-
if (!params[field]) {
|
|
14
|
-
throw new ValidationError(`${field} must be provided`);
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export function validateAccountId(accountId: string | undefined): void {
|
|
20
|
-
if (!accountId) {
|
|
21
|
-
throw new ValidationError("accountId must be provided");
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export function validateProfileId(profileId: string | undefined): void {
|
|
26
|
-
if (!profileId) {
|
|
27
|
-
throw new ValidationError("profileId must be provided");
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export function validateExperience(experience: { role?: string; company?: string } | undefined): void {
|
|
32
|
-
if (!experience || !experience.role || !experience.company) {
|
|
33
|
-
throw new ValidationError("experience with role and company is required");
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export function validateProfileData(profileData: Record<string, unknown> | undefined): void {
|
|
38
|
-
if (!profileData || Object.keys(profileData).length === 0) {
|
|
39
|
-
throw new ValidationError("profileData must be provided with at least one field to update");
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export function validateCompanyIdOrUrl(companyId?: string, companyUrl?: string): void {
|
|
44
|
-
if (!companyId && !companyUrl) {
|
|
45
|
-
throw new ValidationError("companyId or companyUrl must be provided");
|
|
46
|
-
}
|
|
47
|
-
}
|
package/tsconfig.json
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "ES2022",
|
|
4
|
-
"module": "NodeNext",
|
|
5
|
-
"moduleResolution": "NodeNext",
|
|
6
|
-
"lib": ["ES2022"],
|
|
7
|
-
"declaration": true,
|
|
8
|
-
"declarationMap": true,
|
|
9
|
-
"sourceMap": true,
|
|
10
|
-
"outDir": "./dist",
|
|
11
|
-
"rootDir": "./src",
|
|
12
|
-
"strict": true,
|
|
13
|
-
"esModuleInterop": true,
|
|
14
|
-
"skipLibCheck": true,
|
|
15
|
-
"forceConsistentCasingInFileNames": true,
|
|
16
|
-
"resolveJsonModule": true,
|
|
17
|
-
"isolatedModules": true
|
|
18
|
-
},
|
|
19
|
-
"include": ["src/**/*"],
|
|
20
|
-
"exclude": ["node_modules", "dist"]
|
|
21
|
-
}
|