@signaliz/sdk 1.0.34 → 1.0.36
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 +9 -4
- package/dist/{chunk-BGVNI5E2.mjs → chunk-WKE2PNXT.mjs} +81 -26
- package/dist/index.d.mts +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +81 -26
- package/dist/index.mjs +1 -1
- package/dist/mcp-config.js +81 -26
- package/dist/mcp-config.mjs +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -72,6 +72,9 @@ const signalBatch = await signaliz.enrichCompanies(companies, { concurrency: 5 }
|
|
|
72
72
|
const copyBatch = await signaliz.createSignalCopyBatch(copyRequests, { concurrency: 5 });
|
|
73
73
|
```
|
|
74
74
|
|
|
75
|
+
For an ambiguous submission failure, retry with the same
|
|
76
|
+
`idempotencyKey` to recover the original job without duplicate work.
|
|
77
|
+
|
|
75
78
|
Each batch accepts up to 5,000 items. Company Signal Enrichment transparently
|
|
76
79
|
resumes long-running research through the same `/company-signals` endpoint;
|
|
77
80
|
callers never need an internal Trigger.dev status URL. The SDK follows the
|
|
@@ -80,10 +83,12 @@ when a caller explicitly overrides the cadence.
|
|
|
80
83
|
Exact duplicate tasks are single-flighted across each full batch without
|
|
81
84
|
changing input order or result cardinality. Find Email and Verify Email batches
|
|
82
85
|
larger than 25 use one cache-aware recoverable REST job with 500-row result
|
|
83
|
-
pages
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
86
|
+
pages. Uniform Company Signal Enrichment batches larger than 25 use one
|
|
87
|
+
cache-aware recoverable REST job with 25-row result pages; heterogeneous,
|
|
88
|
+
no-wait, and per-run recovery inputs keep the 25-row request path. Signal to
|
|
89
|
+
Copy batches larger than 25 use one recoverable REST job when they can reuse
|
|
90
|
+
available signal data; fresh or deep-search requests keep the 25-row path.
|
|
91
|
+
Signal to Copy also shares company research across copy recipients.
|
|
87
92
|
|
|
88
93
|
Company Signal Enrichment defaults `online` and `enableDeepSearch` to `true`
|
|
89
94
|
unless the caller explicitly disables them. Signal to Copy AI returns three
|
|
@@ -431,12 +431,21 @@ var Signaliz = class {
|
|
|
431
431
|
validateBatchSize(companies);
|
|
432
432
|
const startedAt = Date.now();
|
|
433
433
|
const results = new Array(companies.length);
|
|
434
|
-
|
|
434
|
+
const initialTasks = companies.map((params, index) => ({
|
|
435
435
|
index,
|
|
436
436
|
request: companySignalRequestBody(params)
|
|
437
437
|
}));
|
|
438
|
+
const recoverableBatch = companySignalRecoverableBatchOptions(companies, initialTasks);
|
|
439
|
+
let pending = initialTasks;
|
|
440
|
+
let firstRound = true;
|
|
438
441
|
while (pending.length > 0) {
|
|
439
|
-
const round = await this.runCoreBatchRound(
|
|
442
|
+
const round = await this.runCoreBatchRound(
|
|
443
|
+
"api/v1/company-signals",
|
|
444
|
+
pending,
|
|
445
|
+
options,
|
|
446
|
+
firstRound ? recoverableBatch : void 0
|
|
447
|
+
);
|
|
448
|
+
firstRound = false;
|
|
440
449
|
const taskByIndex = new Map(pending.map((task) => [task.index, task]));
|
|
441
450
|
const nextPending = [];
|
|
442
451
|
let nextDelayMs = 6e4;
|
|
@@ -521,7 +530,7 @@ var Signaliz = class {
|
|
|
521
530
|
const canUseRecoverableAvailableDataJob = uniqueRequests.length > 25 && uniqueRequests.every(
|
|
522
531
|
(params) => params.waitForResult !== false && params.skipCache !== true && params.enableDeepSearch !== true && params.maxWaitMs === void 0 && params.pollIntervalMs === void 0
|
|
523
532
|
);
|
|
524
|
-
const uniqueResults = canUseRecoverableAvailableDataJob ? await this.runAvailableSignalCopyBatchJob(uniqueRequests) : await this.runSignalCopyBatchChunks(uniqueRequests, options);
|
|
533
|
+
const uniqueResults = canUseRecoverableAvailableDataJob ? await this.runAvailableSignalCopyBatchJob(uniqueRequests, options) : await this.runSignalCopyBatchChunks(uniqueRequests, options);
|
|
525
534
|
const results = requests.map((_, index) => ({
|
|
526
535
|
...uniqueResults[deduplicated.sourceIndexByInput[index]],
|
|
527
536
|
index
|
|
@@ -565,11 +574,14 @@ var Signaliz = class {
|
|
|
565
574
|
}
|
|
566
575
|
return uniqueResults;
|
|
567
576
|
}
|
|
568
|
-
async runAvailableSignalCopyBatchJob(requests) {
|
|
577
|
+
async runAvailableSignalCopyBatchJob(requests, options) {
|
|
569
578
|
const rows = await this.runRecoverableCoreBatchJob(
|
|
570
579
|
"api/v1/signal-to-copy",
|
|
571
580
|
requests.map(signalToCopyRequestBody),
|
|
572
|
-
"Signal to Copy"
|
|
581
|
+
"Signal to Copy",
|
|
582
|
+
500,
|
|
583
|
+
20 * 6e4,
|
|
584
|
+
options?.idempotencyKey
|
|
573
585
|
);
|
|
574
586
|
const results = new Array(requests.length);
|
|
575
587
|
for (const row of rows) {
|
|
@@ -578,19 +590,28 @@ var Signaliz = class {
|
|
|
578
590
|
}
|
|
579
591
|
return results;
|
|
580
592
|
}
|
|
581
|
-
async runRecoverableCoreBatchJob(path, requests, label) {
|
|
593
|
+
async runRecoverableCoreBatchJob(path, requests, label, pageSize = 500, maxWaitMs = 20 * 6e4, idempotencyKey) {
|
|
582
594
|
const startedAt = Date.now();
|
|
583
|
-
const
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
595
|
+
const submissionIdempotencyKey = idempotencyKey?.trim() || newBatchIdempotencyKey(label);
|
|
596
|
+
let submission;
|
|
597
|
+
try {
|
|
598
|
+
submission = await this.client.post(path, {
|
|
599
|
+
requests,
|
|
600
|
+
idempotency_key: submissionIdempotencyKey
|
|
601
|
+
});
|
|
602
|
+
} catch (error) {
|
|
603
|
+
if (error instanceof SignalizError && !error.isRetryable) throw error;
|
|
604
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
605
|
+
throw new Error(
|
|
606
|
+
`${label} batch submission was not confirmed. Retry with idempotency key "${submissionIdempotencyKey}" to recover the same job. ${message}`
|
|
607
|
+
);
|
|
608
|
+
}
|
|
588
609
|
const jobId = String(submission.job_id || "").trim();
|
|
589
610
|
if (!jobId) throw new Error(`${label} batch did not return a recoverable job_id`);
|
|
590
611
|
let status = await this.client.post(path, {
|
|
591
612
|
job_id: jobId,
|
|
592
613
|
page: 1,
|
|
593
|
-
page_size:
|
|
614
|
+
page_size: pageSize
|
|
594
615
|
});
|
|
595
616
|
while (!["completed", "partial", "failed"].includes(String(status.status || "").toLowerCase())) {
|
|
596
617
|
if (Date.now() - startedAt >= maxWaitMs) {
|
|
@@ -601,21 +622,28 @@ var Signaliz = class {
|
|
|
601
622
|
status = await this.client.post(path, {
|
|
602
623
|
job_id: jobId,
|
|
603
624
|
page: 1,
|
|
604
|
-
page_size:
|
|
625
|
+
page_size: pageSize
|
|
605
626
|
});
|
|
606
627
|
}
|
|
607
628
|
const totalPages = Math.max(1, Number(status.total_pages || 1));
|
|
608
629
|
const pages = [Array.isArray(status.results) ? status.results : []];
|
|
609
630
|
if (totalPages > 1) {
|
|
610
|
-
const
|
|
611
|
-
|
|
612
|
-
|
|
631
|
+
const pageNumbers = Array.from({ length: totalPages - 1 }, (_, index) => index + 2);
|
|
632
|
+
const remainingPages = await runBatch(
|
|
633
|
+
pageNumbers,
|
|
634
|
+
async (page) => await this.client.post(path, {
|
|
613
635
|
job_id: jobId,
|
|
614
|
-
page
|
|
615
|
-
page_size:
|
|
616
|
-
})
|
|
617
|
-
|
|
618
|
-
|
|
636
|
+
page,
|
|
637
|
+
page_size: pageSize
|
|
638
|
+
}),
|
|
639
|
+
{ concurrency: Math.min(10, pageNumbers.length) }
|
|
640
|
+
);
|
|
641
|
+
for (const page of remainingPages.results) {
|
|
642
|
+
if (!page.success || !page.data) {
|
|
643
|
+
throw new Error(`${label} batch result page ${page.index + 2} failed: ${page.error || "unknown error"}`);
|
|
644
|
+
}
|
|
645
|
+
pages.push(Array.isArray(page.data.results) ? page.data.results : []);
|
|
646
|
+
}
|
|
619
647
|
}
|
|
620
648
|
const rows = pages.flat();
|
|
621
649
|
if (rows.length !== requests.length) {
|
|
@@ -694,22 +722,25 @@ var Signaliz = class {
|
|
|
694
722
|
}
|
|
695
723
|
return results;
|
|
696
724
|
}
|
|
697
|
-
async runCoreBatchRound(path, tasks, options) {
|
|
725
|
+
async runCoreBatchRound(path, tasks, options, companyRecoverableBatch) {
|
|
698
726
|
const { serverConcurrency, chunkConcurrency } = batchConcurrencyPlan(options);
|
|
699
727
|
const deduplicated = dedupeExactBatchItems(tasks, (task) => JSON.stringify(task.request));
|
|
700
728
|
const uniqueTasks = deduplicated.uniqueItems;
|
|
701
|
-
|
|
702
|
-
|
|
729
|
+
const recoverableBatch = path === "api/v1/find-email" ? { label: "Find Email", pageSize: 500, maxWaitMs: 20 * 6e4 } : path === "api/v1/verify-email" ? { label: "Verify Email", pageSize: 500, maxWaitMs: 20 * 6e4 } : companyRecoverableBatch;
|
|
730
|
+
if (recoverableBatch && uniqueTasks.length > 25) {
|
|
703
731
|
const rows = await this.runRecoverableCoreBatchJob(
|
|
704
732
|
path,
|
|
705
733
|
uniqueTasks.map((task) => task.request),
|
|
706
|
-
label
|
|
734
|
+
recoverableBatch.label,
|
|
735
|
+
recoverableBatch.pageSize,
|
|
736
|
+
recoverableBatch.maxWaitMs,
|
|
737
|
+
options?.idempotencyKey
|
|
707
738
|
);
|
|
708
739
|
const uniqueResults2 = rows.map((raw) => ({
|
|
709
740
|
index: uniqueTasks[Number(raw.index)].index,
|
|
710
741
|
success: !recoverableJobRowFailed(raw),
|
|
711
742
|
raw,
|
|
712
|
-
error: recoverableJobRowFailed(raw) ? raw.error || raw._error || raw.message || raw.failure_reason || `${label} item failed` : void 0
|
|
743
|
+
error: recoverableJobRowFailed(raw) ? raw.error || raw._error || raw.message || raw.failure_reason || `${recoverableBatch.label} item failed` : void 0
|
|
713
744
|
}));
|
|
714
745
|
return tasks.map((task, index) => ({
|
|
715
746
|
...uniqueResults2[deduplicated.sourceIndexByInput[index]],
|
|
@@ -851,6 +882,7 @@ function companySignalRequestBody(params) {
|
|
|
851
882
|
lookback_days: params.lookbackDays,
|
|
852
883
|
online,
|
|
853
884
|
enable_deep_search: params.enableDeepSearch ?? online,
|
|
885
|
+
include_summary: params.includeSummary,
|
|
854
886
|
enable_outreach_intelligence: params.enableOutreachIntelligence,
|
|
855
887
|
enable_predictive_intelligence: params.enablePredictiveIntelligence,
|
|
856
888
|
skip_cache: params.skipCache,
|
|
@@ -860,6 +892,29 @@ function companySignalRequestBody(params) {
|
|
|
860
892
|
max_wait_ms: params.maxWaitMs
|
|
861
893
|
});
|
|
862
894
|
}
|
|
895
|
+
function companySignalRecoverableBatchOptions(companies, tasks) {
|
|
896
|
+
if (companies.length <= 25 || companies.some((params) => params.waitForResult === false || Boolean(params.signalRunId))) {
|
|
897
|
+
return void 0;
|
|
898
|
+
}
|
|
899
|
+
const maxWaitMs = Math.max(1e3, companies[0].maxWaitMs ?? 20 * 6e4);
|
|
900
|
+
if (companies.some((params) => Math.max(1e3, params.maxWaitMs ?? 20 * 6e4) !== maxWaitMs)) {
|
|
901
|
+
return void 0;
|
|
902
|
+
}
|
|
903
|
+
const configKey = (request) => {
|
|
904
|
+
const {
|
|
905
|
+
company_domain: _companyDomain,
|
|
906
|
+
company_name: _companyName,
|
|
907
|
+
signal_run_id: _signalRunId,
|
|
908
|
+
wait_for_result: _waitForResult,
|
|
909
|
+
max_wait_ms: _maxWaitMs,
|
|
910
|
+
...config
|
|
911
|
+
} = request;
|
|
912
|
+
return JSON.stringify(config);
|
|
913
|
+
};
|
|
914
|
+
const sharedConfig = configKey(tasks[0].request);
|
|
915
|
+
if (tasks.some((task) => configKey(task.request) !== sharedConfig)) return void 0;
|
|
916
|
+
return { label: "Company Signals", pageSize: 25, maxWaitMs };
|
|
917
|
+
}
|
|
863
918
|
function normalizeCompanySignalResult(params, data) {
|
|
864
919
|
return {
|
|
865
920
|
success: data.success ?? true,
|
package/dist/index.d.mts
CHANGED
|
@@ -9,6 +9,8 @@ interface SignalizConfig {
|
|
|
9
9
|
interface BatchOptions {
|
|
10
10
|
/** Maximum simultaneous API requests. Defaults to 10; allowed range is 1-50. */
|
|
11
11
|
concurrency?: number;
|
|
12
|
+
/** Stable key for safely retrying an ambiguous recoverable batch submission. */
|
|
13
|
+
idempotencyKey?: string;
|
|
12
14
|
}
|
|
13
15
|
interface BatchItemResult<T> {
|
|
14
16
|
index: number;
|
|
@@ -88,6 +90,7 @@ interface CompanySignalEnrichmentParams {
|
|
|
88
90
|
lookbackDays?: number;
|
|
89
91
|
online?: boolean;
|
|
90
92
|
enableDeepSearch?: boolean;
|
|
93
|
+
includeSummary?: boolean;
|
|
91
94
|
enableOutreachIntelligence?: boolean;
|
|
92
95
|
enablePredictiveIntelligence?: boolean;
|
|
93
96
|
skipCache?: boolean;
|
package/dist/index.d.ts
CHANGED
|
@@ -9,6 +9,8 @@ interface SignalizConfig {
|
|
|
9
9
|
interface BatchOptions {
|
|
10
10
|
/** Maximum simultaneous API requests. Defaults to 10; allowed range is 1-50. */
|
|
11
11
|
concurrency?: number;
|
|
12
|
+
/** Stable key for safely retrying an ambiguous recoverable batch submission. */
|
|
13
|
+
idempotencyKey?: string;
|
|
12
14
|
}
|
|
13
15
|
interface BatchItemResult<T> {
|
|
14
16
|
index: number;
|
|
@@ -88,6 +90,7 @@ interface CompanySignalEnrichmentParams {
|
|
|
88
90
|
lookbackDays?: number;
|
|
89
91
|
online?: boolean;
|
|
90
92
|
enableDeepSearch?: boolean;
|
|
93
|
+
includeSummary?: boolean;
|
|
91
94
|
enableOutreachIntelligence?: boolean;
|
|
92
95
|
enablePredictiveIntelligence?: boolean;
|
|
93
96
|
skipCache?: boolean;
|
package/dist/index.js
CHANGED
|
@@ -458,12 +458,21 @@ var Signaliz = class {
|
|
|
458
458
|
validateBatchSize(companies);
|
|
459
459
|
const startedAt = Date.now();
|
|
460
460
|
const results = new Array(companies.length);
|
|
461
|
-
|
|
461
|
+
const initialTasks = companies.map((params, index) => ({
|
|
462
462
|
index,
|
|
463
463
|
request: companySignalRequestBody(params)
|
|
464
464
|
}));
|
|
465
|
+
const recoverableBatch = companySignalRecoverableBatchOptions(companies, initialTasks);
|
|
466
|
+
let pending = initialTasks;
|
|
467
|
+
let firstRound = true;
|
|
465
468
|
while (pending.length > 0) {
|
|
466
|
-
const round = await this.runCoreBatchRound(
|
|
469
|
+
const round = await this.runCoreBatchRound(
|
|
470
|
+
"api/v1/company-signals",
|
|
471
|
+
pending,
|
|
472
|
+
options,
|
|
473
|
+
firstRound ? recoverableBatch : void 0
|
|
474
|
+
);
|
|
475
|
+
firstRound = false;
|
|
467
476
|
const taskByIndex = new Map(pending.map((task) => [task.index, task]));
|
|
468
477
|
const nextPending = [];
|
|
469
478
|
let nextDelayMs = 6e4;
|
|
@@ -548,7 +557,7 @@ var Signaliz = class {
|
|
|
548
557
|
const canUseRecoverableAvailableDataJob = uniqueRequests.length > 25 && uniqueRequests.every(
|
|
549
558
|
(params) => params.waitForResult !== false && params.skipCache !== true && params.enableDeepSearch !== true && params.maxWaitMs === void 0 && params.pollIntervalMs === void 0
|
|
550
559
|
);
|
|
551
|
-
const uniqueResults = canUseRecoverableAvailableDataJob ? await this.runAvailableSignalCopyBatchJob(uniqueRequests) : await this.runSignalCopyBatchChunks(uniqueRequests, options);
|
|
560
|
+
const uniqueResults = canUseRecoverableAvailableDataJob ? await this.runAvailableSignalCopyBatchJob(uniqueRequests, options) : await this.runSignalCopyBatchChunks(uniqueRequests, options);
|
|
552
561
|
const results = requests.map((_, index) => ({
|
|
553
562
|
...uniqueResults[deduplicated.sourceIndexByInput[index]],
|
|
554
563
|
index
|
|
@@ -592,11 +601,14 @@ var Signaliz = class {
|
|
|
592
601
|
}
|
|
593
602
|
return uniqueResults;
|
|
594
603
|
}
|
|
595
|
-
async runAvailableSignalCopyBatchJob(requests) {
|
|
604
|
+
async runAvailableSignalCopyBatchJob(requests, options) {
|
|
596
605
|
const rows = await this.runRecoverableCoreBatchJob(
|
|
597
606
|
"api/v1/signal-to-copy",
|
|
598
607
|
requests.map(signalToCopyRequestBody),
|
|
599
|
-
"Signal to Copy"
|
|
608
|
+
"Signal to Copy",
|
|
609
|
+
500,
|
|
610
|
+
20 * 6e4,
|
|
611
|
+
options?.idempotencyKey
|
|
600
612
|
);
|
|
601
613
|
const results = new Array(requests.length);
|
|
602
614
|
for (const row of rows) {
|
|
@@ -605,19 +617,28 @@ var Signaliz = class {
|
|
|
605
617
|
}
|
|
606
618
|
return results;
|
|
607
619
|
}
|
|
608
|
-
async runRecoverableCoreBatchJob(path, requests, label) {
|
|
620
|
+
async runRecoverableCoreBatchJob(path, requests, label, pageSize = 500, maxWaitMs = 20 * 6e4, idempotencyKey) {
|
|
609
621
|
const startedAt = Date.now();
|
|
610
|
-
const
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
622
|
+
const submissionIdempotencyKey = idempotencyKey?.trim() || newBatchIdempotencyKey(label);
|
|
623
|
+
let submission;
|
|
624
|
+
try {
|
|
625
|
+
submission = await this.client.post(path, {
|
|
626
|
+
requests,
|
|
627
|
+
idempotency_key: submissionIdempotencyKey
|
|
628
|
+
});
|
|
629
|
+
} catch (error) {
|
|
630
|
+
if (error instanceof SignalizError && !error.isRetryable) throw error;
|
|
631
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
632
|
+
throw new Error(
|
|
633
|
+
`${label} batch submission was not confirmed. Retry with idempotency key "${submissionIdempotencyKey}" to recover the same job. ${message}`
|
|
634
|
+
);
|
|
635
|
+
}
|
|
615
636
|
const jobId = String(submission.job_id || "").trim();
|
|
616
637
|
if (!jobId) throw new Error(`${label} batch did not return a recoverable job_id`);
|
|
617
638
|
let status = await this.client.post(path, {
|
|
618
639
|
job_id: jobId,
|
|
619
640
|
page: 1,
|
|
620
|
-
page_size:
|
|
641
|
+
page_size: pageSize
|
|
621
642
|
});
|
|
622
643
|
while (!["completed", "partial", "failed"].includes(String(status.status || "").toLowerCase())) {
|
|
623
644
|
if (Date.now() - startedAt >= maxWaitMs) {
|
|
@@ -628,21 +649,28 @@ var Signaliz = class {
|
|
|
628
649
|
status = await this.client.post(path, {
|
|
629
650
|
job_id: jobId,
|
|
630
651
|
page: 1,
|
|
631
|
-
page_size:
|
|
652
|
+
page_size: pageSize
|
|
632
653
|
});
|
|
633
654
|
}
|
|
634
655
|
const totalPages = Math.max(1, Number(status.total_pages || 1));
|
|
635
656
|
const pages = [Array.isArray(status.results) ? status.results : []];
|
|
636
657
|
if (totalPages > 1) {
|
|
637
|
-
const
|
|
638
|
-
|
|
639
|
-
|
|
658
|
+
const pageNumbers = Array.from({ length: totalPages - 1 }, (_, index) => index + 2);
|
|
659
|
+
const remainingPages = await runBatch(
|
|
660
|
+
pageNumbers,
|
|
661
|
+
async (page) => await this.client.post(path, {
|
|
640
662
|
job_id: jobId,
|
|
641
|
-
page
|
|
642
|
-
page_size:
|
|
643
|
-
})
|
|
644
|
-
|
|
645
|
-
|
|
663
|
+
page,
|
|
664
|
+
page_size: pageSize
|
|
665
|
+
}),
|
|
666
|
+
{ concurrency: Math.min(10, pageNumbers.length) }
|
|
667
|
+
);
|
|
668
|
+
for (const page of remainingPages.results) {
|
|
669
|
+
if (!page.success || !page.data) {
|
|
670
|
+
throw new Error(`${label} batch result page ${page.index + 2} failed: ${page.error || "unknown error"}`);
|
|
671
|
+
}
|
|
672
|
+
pages.push(Array.isArray(page.data.results) ? page.data.results : []);
|
|
673
|
+
}
|
|
646
674
|
}
|
|
647
675
|
const rows = pages.flat();
|
|
648
676
|
if (rows.length !== requests.length) {
|
|
@@ -721,22 +749,25 @@ var Signaliz = class {
|
|
|
721
749
|
}
|
|
722
750
|
return results;
|
|
723
751
|
}
|
|
724
|
-
async runCoreBatchRound(path, tasks, options) {
|
|
752
|
+
async runCoreBatchRound(path, tasks, options, companyRecoverableBatch) {
|
|
725
753
|
const { serverConcurrency, chunkConcurrency } = batchConcurrencyPlan(options);
|
|
726
754
|
const deduplicated = dedupeExactBatchItems(tasks, (task) => JSON.stringify(task.request));
|
|
727
755
|
const uniqueTasks = deduplicated.uniqueItems;
|
|
728
|
-
|
|
729
|
-
|
|
756
|
+
const recoverableBatch = path === "api/v1/find-email" ? { label: "Find Email", pageSize: 500, maxWaitMs: 20 * 6e4 } : path === "api/v1/verify-email" ? { label: "Verify Email", pageSize: 500, maxWaitMs: 20 * 6e4 } : companyRecoverableBatch;
|
|
757
|
+
if (recoverableBatch && uniqueTasks.length > 25) {
|
|
730
758
|
const rows = await this.runRecoverableCoreBatchJob(
|
|
731
759
|
path,
|
|
732
760
|
uniqueTasks.map((task) => task.request),
|
|
733
|
-
label
|
|
761
|
+
recoverableBatch.label,
|
|
762
|
+
recoverableBatch.pageSize,
|
|
763
|
+
recoverableBatch.maxWaitMs,
|
|
764
|
+
options?.idempotencyKey
|
|
734
765
|
);
|
|
735
766
|
const uniqueResults2 = rows.map((raw) => ({
|
|
736
767
|
index: uniqueTasks[Number(raw.index)].index,
|
|
737
768
|
success: !recoverableJobRowFailed(raw),
|
|
738
769
|
raw,
|
|
739
|
-
error: recoverableJobRowFailed(raw) ? raw.error || raw._error || raw.message || raw.failure_reason || `${label} item failed` : void 0
|
|
770
|
+
error: recoverableJobRowFailed(raw) ? raw.error || raw._error || raw.message || raw.failure_reason || `${recoverableBatch.label} item failed` : void 0
|
|
740
771
|
}));
|
|
741
772
|
return tasks.map((task, index) => ({
|
|
742
773
|
...uniqueResults2[deduplicated.sourceIndexByInput[index]],
|
|
@@ -878,6 +909,7 @@ function companySignalRequestBody(params) {
|
|
|
878
909
|
lookback_days: params.lookbackDays,
|
|
879
910
|
online,
|
|
880
911
|
enable_deep_search: params.enableDeepSearch ?? online,
|
|
912
|
+
include_summary: params.includeSummary,
|
|
881
913
|
enable_outreach_intelligence: params.enableOutreachIntelligence,
|
|
882
914
|
enable_predictive_intelligence: params.enablePredictiveIntelligence,
|
|
883
915
|
skip_cache: params.skipCache,
|
|
@@ -887,6 +919,29 @@ function companySignalRequestBody(params) {
|
|
|
887
919
|
max_wait_ms: params.maxWaitMs
|
|
888
920
|
});
|
|
889
921
|
}
|
|
922
|
+
function companySignalRecoverableBatchOptions(companies, tasks) {
|
|
923
|
+
if (companies.length <= 25 || companies.some((params) => params.waitForResult === false || Boolean(params.signalRunId))) {
|
|
924
|
+
return void 0;
|
|
925
|
+
}
|
|
926
|
+
const maxWaitMs = Math.max(1e3, companies[0].maxWaitMs ?? 20 * 6e4);
|
|
927
|
+
if (companies.some((params) => Math.max(1e3, params.maxWaitMs ?? 20 * 6e4) !== maxWaitMs)) {
|
|
928
|
+
return void 0;
|
|
929
|
+
}
|
|
930
|
+
const configKey = (request) => {
|
|
931
|
+
const {
|
|
932
|
+
company_domain: _companyDomain,
|
|
933
|
+
company_name: _companyName,
|
|
934
|
+
signal_run_id: _signalRunId,
|
|
935
|
+
wait_for_result: _waitForResult,
|
|
936
|
+
max_wait_ms: _maxWaitMs,
|
|
937
|
+
...config
|
|
938
|
+
} = request;
|
|
939
|
+
return JSON.stringify(config);
|
|
940
|
+
};
|
|
941
|
+
const sharedConfig = configKey(tasks[0].request);
|
|
942
|
+
if (tasks.some((task) => configKey(task.request) !== sharedConfig)) return void 0;
|
|
943
|
+
return { label: "Company Signals", pageSize: 25, maxWaitMs };
|
|
944
|
+
}
|
|
890
945
|
function normalizeCompanySignalResult(params, data) {
|
|
891
946
|
return {
|
|
892
947
|
success: data.success ?? true,
|
package/dist/index.mjs
CHANGED
package/dist/mcp-config.js
CHANGED
|
@@ -462,12 +462,21 @@ var Signaliz = class {
|
|
|
462
462
|
validateBatchSize(companies);
|
|
463
463
|
const startedAt = Date.now();
|
|
464
464
|
const results = new Array(companies.length);
|
|
465
|
-
|
|
465
|
+
const initialTasks = companies.map((params, index) => ({
|
|
466
466
|
index,
|
|
467
467
|
request: companySignalRequestBody(params)
|
|
468
468
|
}));
|
|
469
|
+
const recoverableBatch = companySignalRecoverableBatchOptions(companies, initialTasks);
|
|
470
|
+
let pending = initialTasks;
|
|
471
|
+
let firstRound = true;
|
|
469
472
|
while (pending.length > 0) {
|
|
470
|
-
const round = await this.runCoreBatchRound(
|
|
473
|
+
const round = await this.runCoreBatchRound(
|
|
474
|
+
"api/v1/company-signals",
|
|
475
|
+
pending,
|
|
476
|
+
options,
|
|
477
|
+
firstRound ? recoverableBatch : void 0
|
|
478
|
+
);
|
|
479
|
+
firstRound = false;
|
|
471
480
|
const taskByIndex = new Map(pending.map((task) => [task.index, task]));
|
|
472
481
|
const nextPending = [];
|
|
473
482
|
let nextDelayMs = 6e4;
|
|
@@ -552,7 +561,7 @@ var Signaliz = class {
|
|
|
552
561
|
const canUseRecoverableAvailableDataJob = uniqueRequests.length > 25 && uniqueRequests.every(
|
|
553
562
|
(params) => params.waitForResult !== false && params.skipCache !== true && params.enableDeepSearch !== true && params.maxWaitMs === void 0 && params.pollIntervalMs === void 0
|
|
554
563
|
);
|
|
555
|
-
const uniqueResults = canUseRecoverableAvailableDataJob ? await this.runAvailableSignalCopyBatchJob(uniqueRequests) : await this.runSignalCopyBatchChunks(uniqueRequests, options);
|
|
564
|
+
const uniqueResults = canUseRecoverableAvailableDataJob ? await this.runAvailableSignalCopyBatchJob(uniqueRequests, options) : await this.runSignalCopyBatchChunks(uniqueRequests, options);
|
|
556
565
|
const results = requests.map((_, index) => ({
|
|
557
566
|
...uniqueResults[deduplicated.sourceIndexByInput[index]],
|
|
558
567
|
index
|
|
@@ -596,11 +605,14 @@ var Signaliz = class {
|
|
|
596
605
|
}
|
|
597
606
|
return uniqueResults;
|
|
598
607
|
}
|
|
599
|
-
async runAvailableSignalCopyBatchJob(requests) {
|
|
608
|
+
async runAvailableSignalCopyBatchJob(requests, options) {
|
|
600
609
|
const rows = await this.runRecoverableCoreBatchJob(
|
|
601
610
|
"api/v1/signal-to-copy",
|
|
602
611
|
requests.map(signalToCopyRequestBody),
|
|
603
|
-
"Signal to Copy"
|
|
612
|
+
"Signal to Copy",
|
|
613
|
+
500,
|
|
614
|
+
20 * 6e4,
|
|
615
|
+
options?.idempotencyKey
|
|
604
616
|
);
|
|
605
617
|
const results = new Array(requests.length);
|
|
606
618
|
for (const row of rows) {
|
|
@@ -609,19 +621,28 @@ var Signaliz = class {
|
|
|
609
621
|
}
|
|
610
622
|
return results;
|
|
611
623
|
}
|
|
612
|
-
async runRecoverableCoreBatchJob(path2, requests, label) {
|
|
624
|
+
async runRecoverableCoreBatchJob(path2, requests, label, pageSize = 500, maxWaitMs = 20 * 6e4, idempotencyKey) {
|
|
613
625
|
const startedAt = Date.now();
|
|
614
|
-
const
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
626
|
+
const submissionIdempotencyKey = idempotencyKey?.trim() || newBatchIdempotencyKey(label);
|
|
627
|
+
let submission;
|
|
628
|
+
try {
|
|
629
|
+
submission = await this.client.post(path2, {
|
|
630
|
+
requests,
|
|
631
|
+
idempotency_key: submissionIdempotencyKey
|
|
632
|
+
});
|
|
633
|
+
} catch (error) {
|
|
634
|
+
if (error instanceof SignalizError && !error.isRetryable) throw error;
|
|
635
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
636
|
+
throw new Error(
|
|
637
|
+
`${label} batch submission was not confirmed. Retry with idempotency key "${submissionIdempotencyKey}" to recover the same job. ${message}`
|
|
638
|
+
);
|
|
639
|
+
}
|
|
619
640
|
const jobId = String(submission.job_id || "").trim();
|
|
620
641
|
if (!jobId) throw new Error(`${label} batch did not return a recoverable job_id`);
|
|
621
642
|
let status = await this.client.post(path2, {
|
|
622
643
|
job_id: jobId,
|
|
623
644
|
page: 1,
|
|
624
|
-
page_size:
|
|
645
|
+
page_size: pageSize
|
|
625
646
|
});
|
|
626
647
|
while (!["completed", "partial", "failed"].includes(String(status.status || "").toLowerCase())) {
|
|
627
648
|
if (Date.now() - startedAt >= maxWaitMs) {
|
|
@@ -632,21 +653,28 @@ var Signaliz = class {
|
|
|
632
653
|
status = await this.client.post(path2, {
|
|
633
654
|
job_id: jobId,
|
|
634
655
|
page: 1,
|
|
635
|
-
page_size:
|
|
656
|
+
page_size: pageSize
|
|
636
657
|
});
|
|
637
658
|
}
|
|
638
659
|
const totalPages = Math.max(1, Number(status.total_pages || 1));
|
|
639
660
|
const pages = [Array.isArray(status.results) ? status.results : []];
|
|
640
661
|
if (totalPages > 1) {
|
|
641
|
-
const
|
|
642
|
-
|
|
643
|
-
|
|
662
|
+
const pageNumbers = Array.from({ length: totalPages - 1 }, (_, index) => index + 2);
|
|
663
|
+
const remainingPages = await runBatch(
|
|
664
|
+
pageNumbers,
|
|
665
|
+
async (page) => await this.client.post(path2, {
|
|
644
666
|
job_id: jobId,
|
|
645
|
-
page
|
|
646
|
-
page_size:
|
|
647
|
-
})
|
|
648
|
-
|
|
649
|
-
|
|
667
|
+
page,
|
|
668
|
+
page_size: pageSize
|
|
669
|
+
}),
|
|
670
|
+
{ concurrency: Math.min(10, pageNumbers.length) }
|
|
671
|
+
);
|
|
672
|
+
for (const page of remainingPages.results) {
|
|
673
|
+
if (!page.success || !page.data) {
|
|
674
|
+
throw new Error(`${label} batch result page ${page.index + 2} failed: ${page.error || "unknown error"}`);
|
|
675
|
+
}
|
|
676
|
+
pages.push(Array.isArray(page.data.results) ? page.data.results : []);
|
|
677
|
+
}
|
|
650
678
|
}
|
|
651
679
|
const rows = pages.flat();
|
|
652
680
|
if (rows.length !== requests.length) {
|
|
@@ -725,22 +753,25 @@ var Signaliz = class {
|
|
|
725
753
|
}
|
|
726
754
|
return results;
|
|
727
755
|
}
|
|
728
|
-
async runCoreBatchRound(path2, tasks, options) {
|
|
756
|
+
async runCoreBatchRound(path2, tasks, options, companyRecoverableBatch) {
|
|
729
757
|
const { serverConcurrency, chunkConcurrency } = batchConcurrencyPlan(options);
|
|
730
758
|
const deduplicated = dedupeExactBatchItems(tasks, (task) => JSON.stringify(task.request));
|
|
731
759
|
const uniqueTasks = deduplicated.uniqueItems;
|
|
732
|
-
|
|
733
|
-
|
|
760
|
+
const recoverableBatch = path2 === "api/v1/find-email" ? { label: "Find Email", pageSize: 500, maxWaitMs: 20 * 6e4 } : path2 === "api/v1/verify-email" ? { label: "Verify Email", pageSize: 500, maxWaitMs: 20 * 6e4 } : companyRecoverableBatch;
|
|
761
|
+
if (recoverableBatch && uniqueTasks.length > 25) {
|
|
734
762
|
const rows = await this.runRecoverableCoreBatchJob(
|
|
735
763
|
path2,
|
|
736
764
|
uniqueTasks.map((task) => task.request),
|
|
737
|
-
label
|
|
765
|
+
recoverableBatch.label,
|
|
766
|
+
recoverableBatch.pageSize,
|
|
767
|
+
recoverableBatch.maxWaitMs,
|
|
768
|
+
options?.idempotencyKey
|
|
738
769
|
);
|
|
739
770
|
const uniqueResults2 = rows.map((raw) => ({
|
|
740
771
|
index: uniqueTasks[Number(raw.index)].index,
|
|
741
772
|
success: !recoverableJobRowFailed(raw),
|
|
742
773
|
raw,
|
|
743
|
-
error: recoverableJobRowFailed(raw) ? raw.error || raw._error || raw.message || raw.failure_reason || `${label} item failed` : void 0
|
|
774
|
+
error: recoverableJobRowFailed(raw) ? raw.error || raw._error || raw.message || raw.failure_reason || `${recoverableBatch.label} item failed` : void 0
|
|
744
775
|
}));
|
|
745
776
|
return tasks.map((task, index) => ({
|
|
746
777
|
...uniqueResults2[deduplicated.sourceIndexByInput[index]],
|
|
@@ -882,6 +913,7 @@ function companySignalRequestBody(params) {
|
|
|
882
913
|
lookback_days: params.lookbackDays,
|
|
883
914
|
online,
|
|
884
915
|
enable_deep_search: params.enableDeepSearch ?? online,
|
|
916
|
+
include_summary: params.includeSummary,
|
|
885
917
|
enable_outreach_intelligence: params.enableOutreachIntelligence,
|
|
886
918
|
enable_predictive_intelligence: params.enablePredictiveIntelligence,
|
|
887
919
|
skip_cache: params.skipCache,
|
|
@@ -891,6 +923,29 @@ function companySignalRequestBody(params) {
|
|
|
891
923
|
max_wait_ms: params.maxWaitMs
|
|
892
924
|
});
|
|
893
925
|
}
|
|
926
|
+
function companySignalRecoverableBatchOptions(companies, tasks) {
|
|
927
|
+
if (companies.length <= 25 || companies.some((params) => params.waitForResult === false || Boolean(params.signalRunId))) {
|
|
928
|
+
return void 0;
|
|
929
|
+
}
|
|
930
|
+
const maxWaitMs = Math.max(1e3, companies[0].maxWaitMs ?? 20 * 6e4);
|
|
931
|
+
if (companies.some((params) => Math.max(1e3, params.maxWaitMs ?? 20 * 6e4) !== maxWaitMs)) {
|
|
932
|
+
return void 0;
|
|
933
|
+
}
|
|
934
|
+
const configKey = (request) => {
|
|
935
|
+
const {
|
|
936
|
+
company_domain: _companyDomain,
|
|
937
|
+
company_name: _companyName,
|
|
938
|
+
signal_run_id: _signalRunId,
|
|
939
|
+
wait_for_result: _waitForResult,
|
|
940
|
+
max_wait_ms: _maxWaitMs,
|
|
941
|
+
...config
|
|
942
|
+
} = request;
|
|
943
|
+
return JSON.stringify(config);
|
|
944
|
+
};
|
|
945
|
+
const sharedConfig = configKey(tasks[0].request);
|
|
946
|
+
if (tasks.some((task) => configKey(task.request) !== sharedConfig)) return void 0;
|
|
947
|
+
return { label: "Company Signals", pageSize: 25, maxWaitMs };
|
|
948
|
+
}
|
|
894
949
|
function normalizeCompanySignalResult(params, data) {
|
|
895
950
|
return {
|
|
896
951
|
success: data.success ?? true,
|
package/dist/mcp-config.mjs
CHANGED
package/package.json
CHANGED