salesprompter-cli 0.1.55 → 0.1.57
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 +2 -2
- package/dist/cli.js +216 -12
- package/dist/sales-navigator.js +39 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -66,9 +66,9 @@ salesprompter companies:resolve-linkedin-urls --in ./companies.txt --out ./compa
|
|
|
66
66
|
salesprompter contacts:resolve-emails --in ./contacts.tsv --out-dir ./email-run --dry-run
|
|
67
67
|
|
|
68
68
|
# Collect a Sales Navigator people search into the active workspace
|
|
69
|
+
# Searches above the native window are split by company headcount and deduplicated.
|
|
69
70
|
salesprompter leads:collect \
|
|
70
|
-
--linkedin-url "$SALES_NAV_PEOPLE_URL"
|
|
71
|
-
--max-results 1000
|
|
71
|
+
--linkedin-url "$SALES_NAV_PEOPLE_URL"
|
|
72
72
|
|
|
73
73
|
# Enrich one or more stored imports: Sales Navigator company -> official domain -> Hunter email
|
|
74
74
|
# This updates workspace data only. It does not create or start outreach.
|
package/dist/cli.js
CHANGED
|
@@ -31,7 +31,7 @@ import { crawlLinkedInProductCategory } from "./linkedin-products.js";
|
|
|
31
31
|
import { claimLinkedInSessionCookieForCli, claimValidatedSalesNavigatorSessionCookieForCli, createLinkedInSessionSupabaseClient, recordLinkedInSessionCookieAudit, resolveConfiguredEnvValue } from "./linkedin-session.js";
|
|
32
32
|
import { buildLeadlistsFunnelQueries } from "./leadlists-funnel.js";
|
|
33
33
|
import { readJsonFile, splitCsv, writeJsonFile, writeTextFile } from "./io.js";
|
|
34
|
-
import { buildSalesNavigatorCrawlPreview, buildSalesNavigatorAccountQuery, createSalesNavigatorCrawlSeed, DEFAULT_SALES_NAVIGATOR_ACCOUNT_CRAWL_DIMENSIONS, DEFAULT_SALES_NAVIGATOR_CRAWL_DIMENSIONS, buildSalesNavigatorPeopleSearchUrl, buildSalesNavigatorPeopleSlice, deriveSalesNavigatorTitleQuerySeeds, executeSalesNavigatorAdaptiveCrawl, expandSalesNavigatorCrawlAttempt, SalesNavigatorCrawlStopError, SalesNavigatorSliceTooBroadError } from "./sales-navigator.js";
|
|
34
|
+
import { buildSalesNavigatorCrawlPreview, buildSalesNavigatorAccountQuery, buildSalesNavigatorResidualCrawlAttempt, createSalesNavigatorCrawlSeed, DEFAULT_SALES_NAVIGATOR_ACCOUNT_CRAWL_DIMENSIONS, DEFAULT_SALES_NAVIGATOR_CRAWL_DIMENSIONS, buildSalesNavigatorPeopleSearchUrl, buildSalesNavigatorPeopleSlice, deriveSalesNavigatorTitleQuerySeeds, executeSalesNavigatorAdaptiveCrawl, expandSalesNavigatorCrawlAttempt, getSalesNavigatorPeopleAccessibleResultLimit, salesNavigatorPeopleSearchHasFilter, SalesNavigatorCrawlStopError, SalesNavigatorSliceTooBroadError } from "./sales-navigator.js";
|
|
35
35
|
import { buildSalesNavigatorHistoricalBackfillPlan, ensureSalesNavigatorPeopleCount, resolveSalesNavigatorHistoricalBackfillConfig, resolveSalesNavigatorHistoricalBackfillResumeState, resolveSalesNavigatorHistoricalBackfillOrgId, salesNavigatorHistoricalBackfillDefaults } from "./salesnav-backfill.js";
|
|
36
36
|
const require = createRequire(import.meta.url);
|
|
37
37
|
const { version: packageVersion } = require("../package.json");
|
|
@@ -8676,6 +8676,18 @@ async function fetchAllLocalSalesNavigatorPeople(parsedRequest, options) {
|
|
|
8676
8676
|
fetchedPages += 1;
|
|
8677
8677
|
totalResults =
|
|
8678
8678
|
totalResults ?? extractLocalSalesNavigatorTotalResults(responseBody);
|
|
8679
|
+
if (totalResults != null &&
|
|
8680
|
+
options.maxAllowedResults != null &&
|
|
8681
|
+
totalResults > Math.max(1, Math.trunc(options.maxAllowedResults))) {
|
|
8682
|
+
throw new SalesNavigatorSliceTooBroadError(`People slice has ${totalResults} results, above ${options.maxAllowedResults}.`, {
|
|
8683
|
+
totalResults,
|
|
8684
|
+
details: {
|
|
8685
|
+
fetchedPages,
|
|
8686
|
+
retryCount,
|
|
8687
|
+
totalDelayMs,
|
|
8688
|
+
},
|
|
8689
|
+
});
|
|
8690
|
+
}
|
|
8679
8691
|
const pagePeople = normalizeLocalSalesNavigatorPeople(responseBody, pageRequest.url);
|
|
8680
8692
|
let added = 0;
|
|
8681
8693
|
for (const person of pagePeople) {
|
|
@@ -8712,6 +8724,149 @@ async function fetchAllLocalSalesNavigatorPeople(parsedRequest, options) {
|
|
|
8712
8724
|
},
|
|
8713
8725
|
};
|
|
8714
8726
|
}
|
|
8727
|
+
function canonicalLocalSalesNavigatorLeadKey(person) {
|
|
8728
|
+
try {
|
|
8729
|
+
const url = new URL(person.profileUrl);
|
|
8730
|
+
url.hash = "";
|
|
8731
|
+
url.search = "";
|
|
8732
|
+
url.hostname = url.hostname.toLowerCase();
|
|
8733
|
+
url.pathname = url.pathname.replace(/\/+$/, "");
|
|
8734
|
+
return url.toString();
|
|
8735
|
+
}
|
|
8736
|
+
catch {
|
|
8737
|
+
return person.profileUrl.trim().toLowerCase();
|
|
8738
|
+
}
|
|
8739
|
+
}
|
|
8740
|
+
function localPeopleSplitDimensions(sourceQueryUrl) {
|
|
8741
|
+
return DEFAULT_SALES_NAVIGATOR_CRAWL_DIMENSIONS.filter((dimension) => !salesNavigatorPeopleSearchHasFilter(sourceQueryUrl, dimension.filterType));
|
|
8742
|
+
}
|
|
8743
|
+
async function fetchCompleteLocalSalesNavigatorPeople(sourceQueryUrl, requestHeaders, options) {
|
|
8744
|
+
const root = createSalesNavigatorCrawlSeed({
|
|
8745
|
+
sourceQueryUrl,
|
|
8746
|
+
baselineFilters: [],
|
|
8747
|
+
maxResultsPerSearch: options.maxResultsPerSearch,
|
|
8748
|
+
numberOfProfiles: options.maxResultsPerSearch,
|
|
8749
|
+
slicePreset: "local-salesnav-people-adaptive",
|
|
8750
|
+
searchType: "people",
|
|
8751
|
+
});
|
|
8752
|
+
const dimensions = localPeopleSplitDimensions(root.sourceQueryUrl);
|
|
8753
|
+
const queue = [root];
|
|
8754
|
+
const queuedUrls = new Set([root.slicedQueryUrl]);
|
|
8755
|
+
const peopleByKey = new Map();
|
|
8756
|
+
const slices = [];
|
|
8757
|
+
let rootTotalResults = null;
|
|
8758
|
+
let fetchedPages = 0;
|
|
8759
|
+
let totalDelayMs = 0;
|
|
8760
|
+
let retryCount = 0;
|
|
8761
|
+
while (queue.length > 0) {
|
|
8762
|
+
const attempt = queue.shift();
|
|
8763
|
+
const parsedRequest = {
|
|
8764
|
+
url: buildSalesNavigatorLeadApiUrlFromSearchUrl(attempt.slicedQueryUrl, options.pageSize),
|
|
8765
|
+
headers: requestHeaders,
|
|
8766
|
+
};
|
|
8767
|
+
let collected = null;
|
|
8768
|
+
let tooBroadTotal = null;
|
|
8769
|
+
let probeFetchedPages = 0;
|
|
8770
|
+
try {
|
|
8771
|
+
collected = await fetchAllLocalSalesNavigatorPeople(parsedRequest, {
|
|
8772
|
+
requestedProfiles: options.maxResultsPerSearch,
|
|
8773
|
+
maxAllowedResults: options.maxResultsPerSearch,
|
|
8774
|
+
pageSize: options.pageSize,
|
|
8775
|
+
pageDelayMinMs: options.pageDelayMinMs,
|
|
8776
|
+
pageDelayMaxMs: options.pageDelayMaxMs,
|
|
8777
|
+
retry: options.retry,
|
|
8778
|
+
executeRequest: options.executeRequest,
|
|
8779
|
+
});
|
|
8780
|
+
fetchedPages += collected.fetchedPages;
|
|
8781
|
+
totalDelayMs += collected.pacing.totalDelayMs;
|
|
8782
|
+
retryCount += collected.pacing.retryCount;
|
|
8783
|
+
if (attempt.depth === 0) {
|
|
8784
|
+
rootTotalResults = collected.totalResults;
|
|
8785
|
+
}
|
|
8786
|
+
}
|
|
8787
|
+
catch (error) {
|
|
8788
|
+
if (!(error instanceof SalesNavigatorSliceTooBroadError)) {
|
|
8789
|
+
throw error;
|
|
8790
|
+
}
|
|
8791
|
+
tooBroadTotal = error.totalResults;
|
|
8792
|
+
const details = error.details && typeof error.details === "object"
|
|
8793
|
+
? error.details
|
|
8794
|
+
: {};
|
|
8795
|
+
probeFetchedPages = Number(details.fetchedPages ?? 1);
|
|
8796
|
+
fetchedPages += probeFetchedPages;
|
|
8797
|
+
retryCount += Number(details.retryCount ?? 0);
|
|
8798
|
+
totalDelayMs += Number(details.totalDelayMs ?? 0);
|
|
8799
|
+
if (attempt.depth === 0) {
|
|
8800
|
+
rootTotalResults = tooBroadTotal;
|
|
8801
|
+
}
|
|
8802
|
+
}
|
|
8803
|
+
const needsSplit = tooBroadTotal != null ||
|
|
8804
|
+
(collected?.totalResults == null &&
|
|
8805
|
+
collected != null &&
|
|
8806
|
+
collected.people.length >= options.maxResultsPerSearch);
|
|
8807
|
+
if (needsSplit) {
|
|
8808
|
+
const usedDimensionKeys = new Set(attempt.splitTrail.map((entry) => entry.key.replace(/-residual$/, "")));
|
|
8809
|
+
const nextDimension = dimensions.find((dimension) => !usedDimensionKeys.has(dimension.key));
|
|
8810
|
+
if (!nextDimension) {
|
|
8811
|
+
throw new Error(`Sales Navigator search still exceeds the ${options.maxResultsPerSearch}-person window after all safe split dimensions were used. Narrow the source search further.`);
|
|
8812
|
+
}
|
|
8813
|
+
const children = expandSalesNavigatorCrawlAttempt(attempt, nextDimension, "people");
|
|
8814
|
+
if (nextDimension.supportsResidualExclusion !== false) {
|
|
8815
|
+
children.push(buildSalesNavigatorResidualCrawlAttempt(attempt, nextDimension, "people"));
|
|
8816
|
+
}
|
|
8817
|
+
for (const child of children) {
|
|
8818
|
+
if (queuedUrls.has(child.slicedQueryUrl))
|
|
8819
|
+
continue;
|
|
8820
|
+
queuedUrls.add(child.slicedQueryUrl);
|
|
8821
|
+
queue.push(child);
|
|
8822
|
+
}
|
|
8823
|
+
slices.push({
|
|
8824
|
+
depth: attempt.depth,
|
|
8825
|
+
splitTrail: attempt.splitTrail,
|
|
8826
|
+
totalResults: tooBroadTotal ?? collected?.totalResults ?? null,
|
|
8827
|
+
collected: 0,
|
|
8828
|
+
fetchedPages: probeFetchedPages || collected?.fetchedPages || 0,
|
|
8829
|
+
status: "split",
|
|
8830
|
+
splitDimension: nextDimension.key,
|
|
8831
|
+
});
|
|
8832
|
+
continue;
|
|
8833
|
+
}
|
|
8834
|
+
if (!collected) {
|
|
8835
|
+
throw new Error("Sales Navigator slice did not return a collection result.");
|
|
8836
|
+
}
|
|
8837
|
+
for (const person of collected.people) {
|
|
8838
|
+
peopleByKey.set(canonicalLocalSalesNavigatorLeadKey(person), person);
|
|
8839
|
+
}
|
|
8840
|
+
slices.push({
|
|
8841
|
+
depth: attempt.depth,
|
|
8842
|
+
splitTrail: attempt.splitTrail,
|
|
8843
|
+
totalResults: collected.totalResults,
|
|
8844
|
+
collected: collected.people.length,
|
|
8845
|
+
fetchedPages: collected.fetchedPages,
|
|
8846
|
+
status: "collected",
|
|
8847
|
+
splitDimension: null,
|
|
8848
|
+
});
|
|
8849
|
+
}
|
|
8850
|
+
const people = [...peopleByKey.values()];
|
|
8851
|
+
if (rootTotalResults != null && people.length < rootTotalResults) {
|
|
8852
|
+
throw new Error(`Adaptive collection found ${people.length} unique people but the root search reported ${rootTotalResults}. No import was written because coverage is incomplete.`);
|
|
8853
|
+
}
|
|
8854
|
+
return {
|
|
8855
|
+
people,
|
|
8856
|
+
totalResults: rootTotalResults,
|
|
8857
|
+
rootTotalResults,
|
|
8858
|
+
fetchedPages,
|
|
8859
|
+
sourceQueryUrl,
|
|
8860
|
+
collectionMode: "adaptive",
|
|
8861
|
+
slices,
|
|
8862
|
+
pacing: {
|
|
8863
|
+
delayMinMs: options.pageDelayMinMs,
|
|
8864
|
+
delayMaxMs: options.pageDelayMaxMs,
|
|
8865
|
+
totalDelayMs,
|
|
8866
|
+
retryCount,
|
|
8867
|
+
},
|
|
8868
|
+
};
|
|
8869
|
+
}
|
|
8715
8870
|
async function fetchAllLocalSalesNavigatorAccounts(parsedRequest, options) {
|
|
8716
8871
|
const accounts = [];
|
|
8717
8872
|
const seen = new Set();
|
|
@@ -8913,6 +9068,7 @@ async function importLocalSalesNavigatorPeopleViaApp(session, payload) {
|
|
|
8913
9068
|
fetchedPages: payload.fetchedPages,
|
|
8914
9069
|
people: payload.people,
|
|
8915
9070
|
rawPayload: {
|
|
9071
|
+
...payload.rawPayload,
|
|
8916
9072
|
clientId: workspaceClientId ?? undefined,
|
|
8917
9073
|
client_id: workspaceClientId ?? undefined,
|
|
8918
9074
|
leadList: workspaceClientId
|
|
@@ -13385,7 +13541,6 @@ program
|
|
|
13385
13541
|
});
|
|
13386
13542
|
async function runSalesNavigatorPeopleCollectCommand(options) {
|
|
13387
13543
|
const linkedInUrl = z.string().url().parse(options.linkedinUrl);
|
|
13388
|
-
const maxResults = z.coerce.number().int().min(1).max(100000).parse(options.maxResults);
|
|
13389
13544
|
const pageSize = z.coerce.number().int().min(1).max(100).parse(options.pageSize);
|
|
13390
13545
|
const normalizedSourceUrl = (() => {
|
|
13391
13546
|
const source = new URL(linkedInUrl);
|
|
@@ -13395,12 +13550,24 @@ async function runSalesNavigatorPeopleCollectCommand(options) {
|
|
|
13395
13550
|
}
|
|
13396
13551
|
return source.toString();
|
|
13397
13552
|
})();
|
|
13553
|
+
const accessibleResultLimit = getSalesNavigatorPeopleAccessibleResultLimit(normalizedSourceUrl);
|
|
13554
|
+
const maxResults = options.maxResults == null
|
|
13555
|
+
? accessibleResultLimit
|
|
13556
|
+
: z.coerce
|
|
13557
|
+
.number()
|
|
13558
|
+
.int()
|
|
13559
|
+
.min(1)
|
|
13560
|
+
.max(accessibleResultLimit)
|
|
13561
|
+
.parse(options.maxResults);
|
|
13562
|
+
const complete = Boolean(options.complete || options.maxResults == null);
|
|
13398
13563
|
if (options.dryRun) {
|
|
13399
13564
|
const payload = {
|
|
13400
13565
|
status: "ok",
|
|
13401
13566
|
dryRun: true,
|
|
13402
13567
|
linkedInUrl: normalizedSourceUrl,
|
|
13403
13568
|
maxResults,
|
|
13569
|
+
accessibleResultLimit,
|
|
13570
|
+
complete,
|
|
13404
13571
|
destination: "/leads/cli-imports"
|
|
13405
13572
|
};
|
|
13406
13573
|
if (options.out)
|
|
@@ -13432,24 +13599,35 @@ async function runSalesNavigatorPeopleCollectCommand(options) {
|
|
|
13432
13599
|
: await createLocalAccountSearchBrowserRelay(browserRelayPort);
|
|
13433
13600
|
let collected;
|
|
13434
13601
|
try {
|
|
13435
|
-
|
|
13436
|
-
requestedProfiles: maxResults,
|
|
13602
|
+
const collectorOptions = {
|
|
13437
13603
|
pageSize,
|
|
13438
13604
|
pageDelayMinMs,
|
|
13439
13605
|
pageDelayMaxMs,
|
|
13440
13606
|
retry: {
|
|
13441
13607
|
maxRetries: 2,
|
|
13442
13608
|
retryBaseDelayMs: 2_000,
|
|
13443
|
-
retryMaxDelayMs: 10_000
|
|
13609
|
+
retryMaxDelayMs: 10_000,
|
|
13444
13610
|
},
|
|
13445
13611
|
executeRequest: browserRelay
|
|
13446
13612
|
? (request) => browserRelay.request(request)
|
|
13447
|
-
: undefined
|
|
13448
|
-
}
|
|
13613
|
+
: undefined,
|
|
13614
|
+
};
|
|
13615
|
+
collected = complete
|
|
13616
|
+
? await fetchCompleteLocalSalesNavigatorPeople(normalizedSourceUrl, parsedRequest.headers, {
|
|
13617
|
+
maxResultsPerSearch: maxResults,
|
|
13618
|
+
...collectorOptions,
|
|
13619
|
+
})
|
|
13620
|
+
: await fetchAllLocalSalesNavigatorPeople(parsedRequest, {
|
|
13621
|
+
requestedProfiles: maxResults,
|
|
13622
|
+
...collectorOptions,
|
|
13623
|
+
});
|
|
13449
13624
|
}
|
|
13450
13625
|
finally {
|
|
13451
13626
|
await browserRelay?.close();
|
|
13452
13627
|
}
|
|
13628
|
+
const adaptiveCollection = "collectionMode" in collected && collected.collectionMode === "adaptive"
|
|
13629
|
+
? collected
|
|
13630
|
+
: null;
|
|
13453
13631
|
const imported = await importLocalSalesNavigatorPeopleViaApp(session, {
|
|
13454
13632
|
sourceQueryUrl: normalizedSourceUrl,
|
|
13455
13633
|
people: collected.people,
|
|
@@ -13457,8 +13635,21 @@ async function runSalesNavigatorPeopleCollectCommand(options) {
|
|
|
13457
13635
|
fetchedPages: collected.fetchedPages,
|
|
13458
13636
|
pacing: collected.pacing,
|
|
13459
13637
|
maxResultsPerSearch: maxResults,
|
|
13460
|
-
numberOfProfiles: maxResults,
|
|
13461
|
-
slicePreset:
|
|
13638
|
+
numberOfProfiles: adaptiveCollection ? collected.people.length : maxResults,
|
|
13639
|
+
slicePreset: adaptiveCollection
|
|
13640
|
+
? "local-salesnav-people-adaptive"
|
|
13641
|
+
: "local-salesnav-people",
|
|
13642
|
+
rawPayload: adaptiveCollection
|
|
13643
|
+
? {
|
|
13644
|
+
collectionMode: adaptiveCollection.collectionMode,
|
|
13645
|
+
accessibleResultLimit,
|
|
13646
|
+
sliceCount: adaptiveCollection.slices.filter((slice) => slice.status === "collected").length,
|
|
13647
|
+
slices: adaptiveCollection.slices,
|
|
13648
|
+
}
|
|
13649
|
+
: {
|
|
13650
|
+
collectionMode: "bounded",
|
|
13651
|
+
accessibleResultLimit,
|
|
13652
|
+
},
|
|
13462
13653
|
});
|
|
13463
13654
|
const payload = {
|
|
13464
13655
|
status: "ok",
|
|
@@ -13474,6 +13665,10 @@ async function runSalesNavigatorPeopleCollectCommand(options) {
|
|
|
13474
13665
|
? null
|
|
13475
13666
|
: Math.max(0, collected.totalResults - collected.people.length),
|
|
13476
13667
|
fetchedPages: collected.fetchedPages,
|
|
13668
|
+
accessibleResultLimit,
|
|
13669
|
+
collectionMode: adaptiveCollection ? "adaptive" : "bounded",
|
|
13670
|
+
sliceCount: adaptiveCollection?.slices.filter((slice) => slice.status === "collected")
|
|
13671
|
+
.length ?? 1,
|
|
13477
13672
|
runId: imported.runId,
|
|
13478
13673
|
imported: imported.imported,
|
|
13479
13674
|
upserted: imported.upserted,
|
|
@@ -14241,7 +14436,15 @@ async function runSalesNavigatorImportEnrichmentCommand(options) {
|
|
|
14241
14436
|
async function runAffiliateLaunchCommand(options) {
|
|
14242
14437
|
const affiliateLink = z.string().url().parse(options.affiliateLink);
|
|
14243
14438
|
const linkedInUrl = z.string().url().parse(options.linkedinUrl);
|
|
14244
|
-
const
|
|
14439
|
+
const accessibleResultLimit = getSalesNavigatorPeopleAccessibleResultLimit(linkedInUrl);
|
|
14440
|
+
const maxResults = options.maxResults == null
|
|
14441
|
+
? accessibleResultLimit
|
|
14442
|
+
: z.coerce
|
|
14443
|
+
.number()
|
|
14444
|
+
.int()
|
|
14445
|
+
.min(1)
|
|
14446
|
+
.max(accessibleResultLimit)
|
|
14447
|
+
.parse(options.maxResults);
|
|
14245
14448
|
const session = await requireAuthSession();
|
|
14246
14449
|
const isSalesNavigatorPeopleSearch = new URL(linkedInUrl).pathname.includes("/sales/search/people");
|
|
14247
14450
|
let localResults;
|
|
@@ -14314,7 +14517,7 @@ function addAffiliateAudienceOptions(command) {
|
|
|
14314
14517
|
return command
|
|
14315
14518
|
.requiredOption("--affiliate-link <url>", "Affiliate link to promote")
|
|
14316
14519
|
.requiredOption("--linkedin-url <url>", "Sales Navigator people or LinkedIn content search URL")
|
|
14317
|
-
.option("--max-results <number>", "Maximum audience results
|
|
14520
|
+
.option("--max-results <number>", "Maximum audience results (people searches: 2500; Connections of: 1000)")
|
|
14318
14521
|
.option("--curl-file <path>", "Optional copied Sales Navigator Lead Search curl request")
|
|
14319
14522
|
.option("--browser-relay-port <number>", "Use the signed-in browser through a loopback relay")
|
|
14320
14523
|
.option("--page-size <number>", "Direct Sales Navigator page size", "100")
|
|
@@ -14328,7 +14531,8 @@ program
|
|
|
14328
14531
|
.alias("leads:collect")
|
|
14329
14532
|
.description("Collect a Sales Navigator people search into the active workspace.")
|
|
14330
14533
|
.requiredOption("--linkedin-url <url>", "Sales Navigator people search URL")
|
|
14331
|
-
.option("--max-results <number>", "
|
|
14534
|
+
.option("--max-results <number>", "Bounded collection size; defaults to exhaustive collection with a 2500-person window")
|
|
14535
|
+
.option("--complete", "Split oversized searches by company headcount and deduplicate the complete result", false)
|
|
14332
14536
|
.option("--curl-file <path>", "Optional copied Sales Navigator Lead Search curl request")
|
|
14333
14537
|
.option("--browser-relay-port <number>", "Use the signed-in browser through a loopback relay")
|
|
14334
14538
|
.option("--page-size <number>", "Direct Sales Navigator page size", "100")
|
package/dist/sales-navigator.js
CHANGED
|
@@ -92,6 +92,45 @@ export const DEFAULT_SALES_NAVIGATOR_PEOPLE_SLICE_FILTERS = [
|
|
|
92
92
|
];
|
|
93
93
|
const LINKEDIN_SALES_NAVIGATOR_PEOPLE_SEARCH_URL = "https://www.linkedin.com/sales/search/people";
|
|
94
94
|
const LINKEDIN_SALES_NAVIGATOR_ACCOUNT_SEARCH_URL = "https://www.linkedin.com/sales/search/company";
|
|
95
|
+
export const SALES_NAVIGATOR_PEOPLE_MAX_RESULTS = 2_500;
|
|
96
|
+
export const SALES_NAVIGATOR_CONNECTIONS_OF_MAX_RESULTS = 1_000;
|
|
97
|
+
export const SALES_NAVIGATOR_ACCOUNT_MAX_RESULTS = 1_000;
|
|
98
|
+
function decodeSalesNavigatorQueryValue(value) {
|
|
99
|
+
let decoded = value;
|
|
100
|
+
for (let attempt = 0; attempt < 3; attempt += 1) {
|
|
101
|
+
try {
|
|
102
|
+
const next = decodeURIComponent(decoded);
|
|
103
|
+
if (next === decoded)
|
|
104
|
+
break;
|
|
105
|
+
decoded = next;
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return decoded;
|
|
112
|
+
}
|
|
113
|
+
function getSalesNavigatorQueryValue(sourceQueryUrl) {
|
|
114
|
+
const url = new URL(sourceQueryUrl);
|
|
115
|
+
const queryFromSearch = url.searchParams.get("query");
|
|
116
|
+
const queryFromHash = url.hash.startsWith("#query=")
|
|
117
|
+
? new URLSearchParams(url.hash.slice(1)).get("query")
|
|
118
|
+
: null;
|
|
119
|
+
return decodeSalesNavigatorQueryValue(queryFromSearch ?? queryFromHash ?? "");
|
|
120
|
+
}
|
|
121
|
+
export function isSalesNavigatorConnectionsOfSearch(sourceQueryUrl) {
|
|
122
|
+
const queryValue = getSalesNavigatorQueryValue(sourceQueryUrl);
|
|
123
|
+
return /(?:type\s*:\s*)?(?:CONNECTIONS?_OF|CONNECTION_OF|TEAMLINK_CONNECTION_OF)\b/i.test(queryValue) || /\bconnections?\s+of\b/i.test(queryValue);
|
|
124
|
+
}
|
|
125
|
+
export function getSalesNavigatorPeopleAccessibleResultLimit(sourceQueryUrl) {
|
|
126
|
+
return isSalesNavigatorConnectionsOfSearch(sourceQueryUrl)
|
|
127
|
+
? SALES_NAVIGATOR_CONNECTIONS_OF_MAX_RESULTS
|
|
128
|
+
: SALES_NAVIGATOR_PEOPLE_MAX_RESULTS;
|
|
129
|
+
}
|
|
130
|
+
export function salesNavigatorPeopleSearchHasFilter(sourceQueryUrl, filterType) {
|
|
131
|
+
const escapedFilterType = filterType.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
132
|
+
return new RegExp(`\\btype\\s*:\\s*${escapedFilterType}\\b`, "i").test(getSalesNavigatorQueryValue(sourceQueryUrl));
|
|
133
|
+
}
|
|
95
134
|
export const DEFAULT_SALES_NAVIGATOR_CRAWL_BASELINE_FILTERS = [
|
|
96
135
|
{
|
|
97
136
|
type: "FUNCTION",
|
package/package.json
CHANGED