salesprompter-cli 0.1.18 → 0.1.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +53 -24
- package/dist/cli.js +1431 -119
- package/dist/linkedin-products.js +29 -8
- package/dist/linkedin-session.js +751 -0
- package/dist/sales-navigator.js +207 -36
- package/dist/salesnav-backfill.js +710 -0
- package/package.json +4 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { load } from "cheerio";
|
|
2
|
-
const
|
|
2
|
+
const DEFAULT_LINKEDIN_BASE_URL = "https://www.linkedin.com";
|
|
3
3
|
const DEFAULT_USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36";
|
|
4
4
|
function normalizeWhitespace(value) {
|
|
5
5
|
return (value ?? "").replace(/\s+/g, " ").trim();
|
|
@@ -12,6 +12,27 @@ function normalizeDomainInput(value) {
|
|
|
12
12
|
.replace(/^www\./, "")
|
|
13
13
|
.split("/")[0] ?? "";
|
|
14
14
|
}
|
|
15
|
+
function getLinkedInBaseUrl(env = process.env) {
|
|
16
|
+
const override = env.SALESPROMPTER_LINKEDIN_BASE_URL?.trim();
|
|
17
|
+
if (!override) {
|
|
18
|
+
return DEFAULT_LINKEDIN_BASE_URL;
|
|
19
|
+
}
|
|
20
|
+
return override.replace(/\/+$/, "");
|
|
21
|
+
}
|
|
22
|
+
function isAllowedLinkedInHostname(hostname, env = process.env) {
|
|
23
|
+
if (/(^|\.)linkedin\.com$/i.test(hostname)) {
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
try {
|
|
27
|
+
return hostname.toLowerCase() === new URL(getLinkedInBaseUrl(env)).hostname.toLowerCase();
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function buildLinkedInUrl(pathname, env = process.env) {
|
|
34
|
+
return new URL(pathname, `${getLinkedInBaseUrl(env)}/`).toString();
|
|
35
|
+
}
|
|
15
36
|
function toAbsoluteLinkedInUrl(value) {
|
|
16
37
|
const trimmed = normalizeWhitespace(value);
|
|
17
38
|
if (trimmed.length === 0) {
|
|
@@ -19,8 +40,8 @@ function toAbsoluteLinkedInUrl(value) {
|
|
|
19
40
|
}
|
|
20
41
|
const url = trimmed.startsWith("http://") || trimmed.startsWith("https://")
|
|
21
42
|
? new URL(trimmed)
|
|
22
|
-
: new URL(trimmed,
|
|
23
|
-
if (
|
|
43
|
+
: new URL(trimmed, `${getLinkedInBaseUrl()}/`);
|
|
44
|
+
if (!isAllowedLinkedInHostname(url.hostname)) {
|
|
24
45
|
return undefined;
|
|
25
46
|
}
|
|
26
47
|
url.search = "";
|
|
@@ -33,7 +54,7 @@ function toAbsoluteUrl(value) {
|
|
|
33
54
|
return undefined;
|
|
34
55
|
}
|
|
35
56
|
try {
|
|
36
|
-
return new URL(trimmed,
|
|
57
|
+
return new URL(trimmed, `${getLinkedInBaseUrl()}/`).toString();
|
|
37
58
|
}
|
|
38
59
|
catch {
|
|
39
60
|
return undefined;
|
|
@@ -424,7 +445,7 @@ function classifyLinkedInInput(input) {
|
|
|
424
445
|
return {
|
|
425
446
|
kind: "search-query",
|
|
426
447
|
input: trimmed,
|
|
427
|
-
url:
|
|
448
|
+
url: buildLinkedInUrl(`/products/search/?q=${encodeURIComponent(query)}`),
|
|
428
449
|
query
|
|
429
450
|
};
|
|
430
451
|
}
|
|
@@ -435,7 +456,7 @@ function classifyLinkedInInput(input) {
|
|
|
435
456
|
return {
|
|
436
457
|
kind: "search-query",
|
|
437
458
|
input: trimmed,
|
|
438
|
-
url:
|
|
459
|
+
url: buildLinkedInUrl(`/products/search/?q=${encodeURIComponent(query)}`),
|
|
439
460
|
query
|
|
440
461
|
};
|
|
441
462
|
}
|
|
@@ -516,7 +537,7 @@ export function createLinkedInHtmlFetcher(fetchImpl = fetch) {
|
|
|
516
537
|
};
|
|
517
538
|
}
|
|
518
539
|
async function findCategoryByCode(categoryCode, fetchHtml) {
|
|
519
|
-
const browseHtml = await fetchHtml(
|
|
540
|
+
const browseHtml = await fetchHtml(buildLinkedInUrl("/products/categories/browse"));
|
|
520
541
|
const $ = load(browseHtml);
|
|
521
542
|
const urls = new Set();
|
|
522
543
|
$('a[href*="/products/categories/"]').each((_, element) => {
|
|
@@ -581,7 +602,7 @@ export async function resolveLinkedInProductSource(input, fetchHtml) {
|
|
|
581
602
|
}
|
|
582
603
|
const searchQuery = resolvedInput.query ?? "";
|
|
583
604
|
const searchUrl = resolvedInput.kind === "domain"
|
|
584
|
-
?
|
|
605
|
+
? buildLinkedInUrl(`/products/search/?q=${encodeURIComponent(searchQuery)}`)
|
|
585
606
|
: resolvedInput.url;
|
|
586
607
|
const html = await fetchHtml(searchUrl);
|
|
587
608
|
const search = parseLinkedInProductSearchPage(html, searchUrl);
|