@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 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; Company Signal Enrichment uses 25-row REST chunks. Signal to Copy batches
84
- larger than 25 use one recoverable REST job when they can reuse available signal
85
- data; fresh or deep-search requests keep the 25-row path. Signal to Copy also
86
- shares company research across copy recipients.
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
- let pending = companies.map((params, index) => ({
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("api/v1/company-signals", pending, options);
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 maxWaitMs = 20 * 6e4;
584
- const submission = await this.client.post(path, {
585
- requests,
586
- idempotency_key: newBatchIdempotencyKey(label)
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: 500
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: 500
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 remainingPages = await Promise.all(Array.from(
611
- { length: totalPages - 1 },
612
- (_, index) => this.client.post(path, {
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: index + 2,
615
- page_size: 500
616
- })
617
- ));
618
- for (const page of remainingPages) pages.push(Array.isArray(page.results) ? page.results : []);
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
- if ((path === "api/v1/find-email" || path === "api/v1/verify-email") && uniqueTasks.length > 25) {
702
- const label = path === "api/v1/find-email" ? "Find Email" : "Verify Email";
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
- let pending = companies.map((params, index) => ({
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("api/v1/company-signals", pending, options);
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 maxWaitMs = 20 * 6e4;
611
- const submission = await this.client.post(path, {
612
- requests,
613
- idempotency_key: newBatchIdempotencyKey(label)
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: 500
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: 500
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 remainingPages = await Promise.all(Array.from(
638
- { length: totalPages - 1 },
639
- (_, index) => this.client.post(path, {
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: index + 2,
642
- page_size: 500
643
- })
644
- ));
645
- for (const page of remainingPages) pages.push(Array.isArray(page.results) ? page.results : []);
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
- if ((path === "api/v1/find-email" || path === "api/v1/verify-email") && uniqueTasks.length > 25) {
729
- const label = path === "api/v1/find-email" ? "Find Email" : "Verify Email";
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
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  Signaliz,
3
3
  SignalizError
4
- } from "./chunk-BGVNI5E2.mjs";
4
+ } from "./chunk-WKE2PNXT.mjs";
5
5
  export {
6
6
  Signaliz,
7
7
  SignalizError
@@ -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
- let pending = companies.map((params, index) => ({
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("api/v1/company-signals", pending, options);
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 maxWaitMs = 20 * 6e4;
615
- const submission = await this.client.post(path2, {
616
- requests,
617
- idempotency_key: newBatchIdempotencyKey(label)
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: 500
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: 500
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 remainingPages = await Promise.all(Array.from(
642
- { length: totalPages - 1 },
643
- (_, index) => this.client.post(path2, {
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: index + 2,
646
- page_size: 500
647
- })
648
- ));
649
- for (const page of remainingPages) pages.push(Array.isArray(page.results) ? page.results : []);
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
- if ((path2 === "api/v1/find-email" || path2 === "api/v1/verify-email") && uniqueTasks.length > 25) {
733
- const label = path2 === "api/v1/find-email" ? "Find Email" : "Verify Email";
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,
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  Signaliz
4
- } from "./chunk-BGVNI5E2.mjs";
4
+ } from "./chunk-WKE2PNXT.mjs";
5
5
 
6
6
  // src/mcp-config.ts
7
7
  import * as fs from "fs";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@signaliz/sdk",
3
- "version": "1.0.34",
3
+ "version": "1.0.36",
4
4
  "description": "Signaliz SDK for Find Email, Verify Email, Company Signal Enrichment, and Signal to Copy AI.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",