@signaliz/sdk 1.0.33 → 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 +11 -6
- package/dist/{chunk-H6HOY73X.mjs → chunk-WKE2PNXT.mjs} +82 -26
- package/dist/index.d.mts +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +82 -26
- package/dist/index.mjs +1 -1
- package/dist/mcp-config.js +82 -26
- package/dist/mcp-config.mjs +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -72,18 +72,23 @@ 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
|
|
78
81
|
server's adaptive polling hints by default and still honors `pollIntervalMs`
|
|
79
82
|
when a caller explicitly overrides the cadence.
|
|
80
83
|
Exact duplicate tasks are single-flighted across each full batch without
|
|
81
|
-
changing input order or result cardinality.
|
|
82
|
-
use one cache-aware recoverable REST job with 500-row result
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
84
|
+
changing input order or result cardinality. Find Email and Verify Email batches
|
|
85
|
+
larger than 25 use one cache-aware recoverable REST job with 500-row result
|
|
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,21 +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
|
-
|
|
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) {
|
|
702
731
|
const rows = await this.runRecoverableCoreBatchJob(
|
|
703
732
|
path,
|
|
704
733
|
uniqueTasks.map((task) => task.request),
|
|
705
|
-
|
|
734
|
+
recoverableBatch.label,
|
|
735
|
+
recoverableBatch.pageSize,
|
|
736
|
+
recoverableBatch.maxWaitMs,
|
|
737
|
+
options?.idempotencyKey
|
|
706
738
|
);
|
|
707
739
|
const uniqueResults2 = rows.map((raw) => ({
|
|
708
740
|
index: uniqueTasks[Number(raw.index)].index,
|
|
709
741
|
success: !recoverableJobRowFailed(raw),
|
|
710
742
|
raw,
|
|
711
|
-
error: recoverableJobRowFailed(raw) ? raw.error || raw._error || raw.message || raw.failure_reason ||
|
|
743
|
+
error: recoverableJobRowFailed(raw) ? raw.error || raw._error || raw.message || raw.failure_reason || `${recoverableBatch.label} item failed` : void 0
|
|
712
744
|
}));
|
|
713
745
|
return tasks.map((task, index) => ({
|
|
714
746
|
...uniqueResults2[deduplicated.sourceIndexByInput[index]],
|
|
@@ -773,7 +805,7 @@ var Signaliz = class {
|
|
|
773
805
|
}
|
|
774
806
|
};
|
|
775
807
|
function recoverableJobRowFailed(row) {
|
|
776
|
-
return row.success === false || String(row._status || "").toLowerCase()
|
|
808
|
+
return row.success === false || ["failed", "skipped"].includes(String(row._status || "").toLowerCase());
|
|
777
809
|
}
|
|
778
810
|
function newBatchIdempotencyKey(label) {
|
|
779
811
|
const nonce = globalThis.crypto?.randomUUID?.() || `${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
|
@@ -850,6 +882,7 @@ function companySignalRequestBody(params) {
|
|
|
850
882
|
lookback_days: params.lookbackDays,
|
|
851
883
|
online,
|
|
852
884
|
enable_deep_search: params.enableDeepSearch ?? online,
|
|
885
|
+
include_summary: params.includeSummary,
|
|
853
886
|
enable_outreach_intelligence: params.enableOutreachIntelligence,
|
|
854
887
|
enable_predictive_intelligence: params.enablePredictiveIntelligence,
|
|
855
888
|
skip_cache: params.skipCache,
|
|
@@ -859,6 +892,29 @@ function companySignalRequestBody(params) {
|
|
|
859
892
|
max_wait_ms: params.maxWaitMs
|
|
860
893
|
});
|
|
861
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
|
+
}
|
|
862
918
|
function normalizeCompanySignalResult(params, data) {
|
|
863
919
|
return {
|
|
864
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,21 +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
|
-
|
|
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) {
|
|
729
758
|
const rows = await this.runRecoverableCoreBatchJob(
|
|
730
759
|
path,
|
|
731
760
|
uniqueTasks.map((task) => task.request),
|
|
732
|
-
|
|
761
|
+
recoverableBatch.label,
|
|
762
|
+
recoverableBatch.pageSize,
|
|
763
|
+
recoverableBatch.maxWaitMs,
|
|
764
|
+
options?.idempotencyKey
|
|
733
765
|
);
|
|
734
766
|
const uniqueResults2 = rows.map((raw) => ({
|
|
735
767
|
index: uniqueTasks[Number(raw.index)].index,
|
|
736
768
|
success: !recoverableJobRowFailed(raw),
|
|
737
769
|
raw,
|
|
738
|
-
error: recoverableJobRowFailed(raw) ? raw.error || raw._error || raw.message || raw.failure_reason ||
|
|
770
|
+
error: recoverableJobRowFailed(raw) ? raw.error || raw._error || raw.message || raw.failure_reason || `${recoverableBatch.label} item failed` : void 0
|
|
739
771
|
}));
|
|
740
772
|
return tasks.map((task, index) => ({
|
|
741
773
|
...uniqueResults2[deduplicated.sourceIndexByInput[index]],
|
|
@@ -800,7 +832,7 @@ var Signaliz = class {
|
|
|
800
832
|
}
|
|
801
833
|
};
|
|
802
834
|
function recoverableJobRowFailed(row) {
|
|
803
|
-
return row.success === false || String(row._status || "").toLowerCase()
|
|
835
|
+
return row.success === false || ["failed", "skipped"].includes(String(row._status || "").toLowerCase());
|
|
804
836
|
}
|
|
805
837
|
function newBatchIdempotencyKey(label) {
|
|
806
838
|
const nonce = globalThis.crypto?.randomUUID?.() || `${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
|
@@ -877,6 +909,7 @@ function companySignalRequestBody(params) {
|
|
|
877
909
|
lookback_days: params.lookbackDays,
|
|
878
910
|
online,
|
|
879
911
|
enable_deep_search: params.enableDeepSearch ?? online,
|
|
912
|
+
include_summary: params.includeSummary,
|
|
880
913
|
enable_outreach_intelligence: params.enableOutreachIntelligence,
|
|
881
914
|
enable_predictive_intelligence: params.enablePredictiveIntelligence,
|
|
882
915
|
skip_cache: params.skipCache,
|
|
@@ -886,6 +919,29 @@ function companySignalRequestBody(params) {
|
|
|
886
919
|
max_wait_ms: params.maxWaitMs
|
|
887
920
|
});
|
|
888
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
|
+
}
|
|
889
945
|
function normalizeCompanySignalResult(params, data) {
|
|
890
946
|
return {
|
|
891
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,21 +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
|
-
|
|
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) {
|
|
733
762
|
const rows = await this.runRecoverableCoreBatchJob(
|
|
734
763
|
path2,
|
|
735
764
|
uniqueTasks.map((task) => task.request),
|
|
736
|
-
|
|
765
|
+
recoverableBatch.label,
|
|
766
|
+
recoverableBatch.pageSize,
|
|
767
|
+
recoverableBatch.maxWaitMs,
|
|
768
|
+
options?.idempotencyKey
|
|
737
769
|
);
|
|
738
770
|
const uniqueResults2 = rows.map((raw) => ({
|
|
739
771
|
index: uniqueTasks[Number(raw.index)].index,
|
|
740
772
|
success: !recoverableJobRowFailed(raw),
|
|
741
773
|
raw,
|
|
742
|
-
error: recoverableJobRowFailed(raw) ? raw.error || raw._error || raw.message || raw.failure_reason ||
|
|
774
|
+
error: recoverableJobRowFailed(raw) ? raw.error || raw._error || raw.message || raw.failure_reason || `${recoverableBatch.label} item failed` : void 0
|
|
743
775
|
}));
|
|
744
776
|
return tasks.map((task, index) => ({
|
|
745
777
|
...uniqueResults2[deduplicated.sourceIndexByInput[index]],
|
|
@@ -804,7 +836,7 @@ var Signaliz = class {
|
|
|
804
836
|
}
|
|
805
837
|
};
|
|
806
838
|
function recoverableJobRowFailed(row) {
|
|
807
|
-
return row.success === false || String(row._status || "").toLowerCase()
|
|
839
|
+
return row.success === false || ["failed", "skipped"].includes(String(row._status || "").toLowerCase());
|
|
808
840
|
}
|
|
809
841
|
function newBatchIdempotencyKey(label) {
|
|
810
842
|
const nonce = globalThis.crypto?.randomUUID?.() || `${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
|
@@ -881,6 +913,7 @@ function companySignalRequestBody(params) {
|
|
|
881
913
|
lookback_days: params.lookbackDays,
|
|
882
914
|
online,
|
|
883
915
|
enable_deep_search: params.enableDeepSearch ?? online,
|
|
916
|
+
include_summary: params.includeSummary,
|
|
884
917
|
enable_outreach_intelligence: params.enableOutreachIntelligence,
|
|
885
918
|
enable_predictive_intelligence: params.enablePredictiveIntelligence,
|
|
886
919
|
skip_cache: params.skipCache,
|
|
@@ -890,6 +923,29 @@ function companySignalRequestBody(params) {
|
|
|
890
923
|
max_wait_ms: params.maxWaitMs
|
|
891
924
|
});
|
|
892
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
|
+
}
|
|
893
949
|
function normalizeCompanySignalResult(params, data) {
|
|
894
950
|
return {
|
|
895
951
|
success: data.success ?? true,
|
package/dist/mcp-config.mjs
CHANGED
package/package.json
CHANGED