@signaliz/sdk 1.0.32 → 1.0.34

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
@@ -78,11 +78,12 @@ callers never need an internal Trigger.dev status URL. The SDK follows the
78
78
  server's adaptive polling hints by default and still honors `pollIntervalMs`
79
79
  when a caller explicitly overrides the cadence.
80
80
  Exact duplicate tasks are single-flighted across each full batch without
81
- changing input order or result cardinality. Find Email, Verify Email, and
82
- Company Signal Enrichment use 25-row REST chunks. Signal to Copy batches larger
83
- than 25 use one recoverable REST job when they can reuse available signal data;
84
- fresh or deep-search requests keep the 25-row path. Signal to Copy also shares
85
- company research across copy recipients.
81
+ changing input order or result cardinality. Find Email and Verify Email batches
82
+ 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
87
 
87
88
  Company Signal Enrichment defaults `online` and `enableDeepSearch` to `true`
88
89
  unless the caller explicitly disables them. Signal to Copy AI returns three
@@ -566,20 +566,39 @@ var Signaliz = class {
566
566
  return uniqueResults;
567
567
  }
568
568
  async runAvailableSignalCopyBatchJob(requests) {
569
+ const rows = await this.runRecoverableCoreBatchJob(
570
+ "api/v1/signal-to-copy",
571
+ requests.map(signalToCopyRequestBody),
572
+ "Signal to Copy"
573
+ );
574
+ const results = new Array(requests.length);
575
+ for (const row of rows) {
576
+ const index = Number(row.index);
577
+ results[index] = recoverableJobRowFailed(row) ? { index, success: false, error: row.error || row.message || "Signal to Copy failed" } : { index, success: true, data: normalizeSignalToCopyResult(row) };
578
+ }
579
+ return results;
580
+ }
581
+ async runRecoverableCoreBatchJob(path, requests, label) {
569
582
  const startedAt = Date.now();
570
583
  const maxWaitMs = 20 * 6e4;
571
- let status = await this.client.post("api/v1/signal-to-copy", {
572
- requests: requests.map(signalToCopyRequestBody)
584
+ const submission = await this.client.post(path, {
585
+ requests,
586
+ idempotency_key: newBatchIdempotencyKey(label)
587
+ });
588
+ const jobId = String(submission.job_id || "").trim();
589
+ if (!jobId) throw new Error(`${label} batch did not return a recoverable job_id`);
590
+ let status = await this.client.post(path, {
591
+ job_id: jobId,
592
+ page: 1,
593
+ page_size: 500
573
594
  });
574
- const jobId = String(status.job_id || "").trim();
575
- if (!jobId) throw new Error("Signal to Copy batch did not return a recoverable job_id");
576
595
  while (!["completed", "partial", "failed"].includes(String(status.status || "").toLowerCase())) {
577
596
  if (Date.now() - startedAt >= maxWaitMs) {
578
- throw new Error(`Signal to Copy batch ${jobId} did not complete within ${maxWaitMs}ms`);
597
+ throw new Error(`${label} batch ${jobId} did not complete within ${maxWaitMs}ms`);
579
598
  }
580
599
  const retryAfterMs = Number(status.next_poll_after_seconds || 2) * 1e3;
581
600
  await sleep2(Math.min(Math.max(250, retryAfterMs), maxWaitMs - (Date.now() - startedAt)));
582
- status = await this.client.post("api/v1/signal-to-copy", {
601
+ status = await this.client.post(path, {
583
602
  job_id: jobId,
584
603
  page: 1,
585
604
  page_size: 500
@@ -590,7 +609,7 @@ var Signaliz = class {
590
609
  if (totalPages > 1) {
591
610
  const remainingPages = await Promise.all(Array.from(
592
611
  { length: totalPages - 1 },
593
- (_, index) => this.client.post("api/v1/signal-to-copy", {
612
+ (_, index) => this.client.post(path, {
594
613
  job_id: jobId,
595
614
  page: index + 2,
596
615
  page_size: 500
@@ -600,18 +619,18 @@ var Signaliz = class {
600
619
  }
601
620
  const rows = pages.flat();
602
621
  if (rows.length !== requests.length) {
603
- throw new Error(`Signal to Copy batch returned ${rows.length} results for ${requests.length} requests`);
622
+ throw new Error(`${label} batch returned ${rows.length} results for ${requests.length} requests`);
604
623
  }
605
- const results = new Array(requests.length);
624
+ const seen = /* @__PURE__ */ new Set();
606
625
  for (const row of rows) {
607
626
  const index = Number(row.index);
608
- if (!Number.isInteger(index) || index < 0 || index >= requests.length) {
609
- throw new Error("Signal to Copy batch returned an invalid result index");
627
+ if (!Number.isInteger(index) || index < 0 || index >= requests.length || seen.has(index)) {
628
+ throw new Error(`${label} batch returned an invalid result index`);
610
629
  }
611
- results[index] = row.success === false ? { index, success: false, error: row.error || row.message || "Signal to Copy failed" } : { index, success: true, data: normalizeSignalToCopyResult(row) };
630
+ seen.add(index);
612
631
  }
613
- if (results.some((item) => !item)) throw new Error("Signal to Copy batch returned incomplete indexed results");
614
- return results;
632
+ if (seen.size !== requests.length) throw new Error(`${label} batch returned incomplete indexed results`);
633
+ return rows;
615
634
  }
616
635
  async runSignalCopyBatchChunk(requests, concurrency) {
617
636
  const startedAt = Date.now();
@@ -679,6 +698,24 @@ var Signaliz = class {
679
698
  const { serverConcurrency, chunkConcurrency } = batchConcurrencyPlan(options);
680
699
  const deduplicated = dedupeExactBatchItems(tasks, (task) => JSON.stringify(task.request));
681
700
  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";
703
+ const rows = await this.runRecoverableCoreBatchJob(
704
+ path,
705
+ uniqueTasks.map((task) => task.request),
706
+ label
707
+ );
708
+ const uniqueResults2 = rows.map((raw) => ({
709
+ index: uniqueTasks[Number(raw.index)].index,
710
+ success: !recoverableJobRowFailed(raw),
711
+ raw,
712
+ error: recoverableJobRowFailed(raw) ? raw.error || raw._error || raw.message || raw.failure_reason || `${label} item failed` : void 0
713
+ }));
714
+ return tasks.map((task, index) => ({
715
+ ...uniqueResults2[deduplicated.sourceIndexByInput[index]],
716
+ index: task.index
717
+ }));
718
+ }
682
719
  const chunks = [];
683
720
  for (let offset = 0; offset < uniqueTasks.length; offset += 25) {
684
721
  chunks.push({ offset, tasks: uniqueTasks.slice(offset, offset + 25) });
@@ -736,6 +773,13 @@ var Signaliz = class {
736
773
  };
737
774
  }
738
775
  };
776
+ function recoverableJobRowFailed(row) {
777
+ return row.success === false || ["failed", "skipped"].includes(String(row._status || "").toLowerCase());
778
+ }
779
+ function newBatchIdempotencyKey(label) {
780
+ const nonce = globalThis.crypto?.randomUUID?.() || `${Date.now()}-${Math.random().toString(16).slice(2)}`;
781
+ return `signaliz-${label.toLowerCase().replace(/[^a-z0-9]+/g, "-")}-${nonce}`;
782
+ }
739
783
  function compact(input) {
740
784
  return Object.fromEntries(Object.entries(input).filter(([, value]) => value !== void 0));
741
785
  }
package/dist/index.d.mts CHANGED
@@ -205,6 +205,7 @@ declare class Signaliz {
205
205
  createSignalCopyBatch(requests: SignalToCopyParams[], options?: BatchOptions): Promise<BatchResult<SignalToCopyResult>>;
206
206
  private runSignalCopyBatchChunks;
207
207
  private runAvailableSignalCopyBatchJob;
208
+ private runRecoverableCoreBatchJob;
208
209
  private runSignalCopyBatchChunk;
209
210
  private runCoreBatchRound;
210
211
  listTools(): Promise<MCPToolInfo[]>;
package/dist/index.d.ts CHANGED
@@ -205,6 +205,7 @@ declare class Signaliz {
205
205
  createSignalCopyBatch(requests: SignalToCopyParams[], options?: BatchOptions): Promise<BatchResult<SignalToCopyResult>>;
206
206
  private runSignalCopyBatchChunks;
207
207
  private runAvailableSignalCopyBatchJob;
208
+ private runRecoverableCoreBatchJob;
208
209
  private runSignalCopyBatchChunk;
209
210
  private runCoreBatchRound;
210
211
  listTools(): Promise<MCPToolInfo[]>;
package/dist/index.js CHANGED
@@ -593,20 +593,39 @@ var Signaliz = class {
593
593
  return uniqueResults;
594
594
  }
595
595
  async runAvailableSignalCopyBatchJob(requests) {
596
+ const rows = await this.runRecoverableCoreBatchJob(
597
+ "api/v1/signal-to-copy",
598
+ requests.map(signalToCopyRequestBody),
599
+ "Signal to Copy"
600
+ );
601
+ const results = new Array(requests.length);
602
+ for (const row of rows) {
603
+ const index = Number(row.index);
604
+ results[index] = recoverableJobRowFailed(row) ? { index, success: false, error: row.error || row.message || "Signal to Copy failed" } : { index, success: true, data: normalizeSignalToCopyResult(row) };
605
+ }
606
+ return results;
607
+ }
608
+ async runRecoverableCoreBatchJob(path, requests, label) {
596
609
  const startedAt = Date.now();
597
610
  const maxWaitMs = 20 * 6e4;
598
- let status = await this.client.post("api/v1/signal-to-copy", {
599
- requests: requests.map(signalToCopyRequestBody)
611
+ const submission = await this.client.post(path, {
612
+ requests,
613
+ idempotency_key: newBatchIdempotencyKey(label)
614
+ });
615
+ const jobId = String(submission.job_id || "").trim();
616
+ if (!jobId) throw new Error(`${label} batch did not return a recoverable job_id`);
617
+ let status = await this.client.post(path, {
618
+ job_id: jobId,
619
+ page: 1,
620
+ page_size: 500
600
621
  });
601
- const jobId = String(status.job_id || "").trim();
602
- if (!jobId) throw new Error("Signal to Copy batch did not return a recoverable job_id");
603
622
  while (!["completed", "partial", "failed"].includes(String(status.status || "").toLowerCase())) {
604
623
  if (Date.now() - startedAt >= maxWaitMs) {
605
- throw new Error(`Signal to Copy batch ${jobId} did not complete within ${maxWaitMs}ms`);
624
+ throw new Error(`${label} batch ${jobId} did not complete within ${maxWaitMs}ms`);
606
625
  }
607
626
  const retryAfterMs = Number(status.next_poll_after_seconds || 2) * 1e3;
608
627
  await sleep2(Math.min(Math.max(250, retryAfterMs), maxWaitMs - (Date.now() - startedAt)));
609
- status = await this.client.post("api/v1/signal-to-copy", {
628
+ status = await this.client.post(path, {
610
629
  job_id: jobId,
611
630
  page: 1,
612
631
  page_size: 500
@@ -617,7 +636,7 @@ var Signaliz = class {
617
636
  if (totalPages > 1) {
618
637
  const remainingPages = await Promise.all(Array.from(
619
638
  { length: totalPages - 1 },
620
- (_, index) => this.client.post("api/v1/signal-to-copy", {
639
+ (_, index) => this.client.post(path, {
621
640
  job_id: jobId,
622
641
  page: index + 2,
623
642
  page_size: 500
@@ -627,18 +646,18 @@ var Signaliz = class {
627
646
  }
628
647
  const rows = pages.flat();
629
648
  if (rows.length !== requests.length) {
630
- throw new Error(`Signal to Copy batch returned ${rows.length} results for ${requests.length} requests`);
649
+ throw new Error(`${label} batch returned ${rows.length} results for ${requests.length} requests`);
631
650
  }
632
- const results = new Array(requests.length);
651
+ const seen = /* @__PURE__ */ new Set();
633
652
  for (const row of rows) {
634
653
  const index = Number(row.index);
635
- if (!Number.isInteger(index) || index < 0 || index >= requests.length) {
636
- throw new Error("Signal to Copy batch returned an invalid result index");
654
+ if (!Number.isInteger(index) || index < 0 || index >= requests.length || seen.has(index)) {
655
+ throw new Error(`${label} batch returned an invalid result index`);
637
656
  }
638
- results[index] = row.success === false ? { index, success: false, error: row.error || row.message || "Signal to Copy failed" } : { index, success: true, data: normalizeSignalToCopyResult(row) };
657
+ seen.add(index);
639
658
  }
640
- if (results.some((item) => !item)) throw new Error("Signal to Copy batch returned incomplete indexed results");
641
- return results;
659
+ if (seen.size !== requests.length) throw new Error(`${label} batch returned incomplete indexed results`);
660
+ return rows;
642
661
  }
643
662
  async runSignalCopyBatchChunk(requests, concurrency) {
644
663
  const startedAt = Date.now();
@@ -706,6 +725,24 @@ var Signaliz = class {
706
725
  const { serverConcurrency, chunkConcurrency } = batchConcurrencyPlan(options);
707
726
  const deduplicated = dedupeExactBatchItems(tasks, (task) => JSON.stringify(task.request));
708
727
  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";
730
+ const rows = await this.runRecoverableCoreBatchJob(
731
+ path,
732
+ uniqueTasks.map((task) => task.request),
733
+ label
734
+ );
735
+ const uniqueResults2 = rows.map((raw) => ({
736
+ index: uniqueTasks[Number(raw.index)].index,
737
+ success: !recoverableJobRowFailed(raw),
738
+ raw,
739
+ error: recoverableJobRowFailed(raw) ? raw.error || raw._error || raw.message || raw.failure_reason || `${label} item failed` : void 0
740
+ }));
741
+ return tasks.map((task, index) => ({
742
+ ...uniqueResults2[deduplicated.sourceIndexByInput[index]],
743
+ index: task.index
744
+ }));
745
+ }
709
746
  const chunks = [];
710
747
  for (let offset = 0; offset < uniqueTasks.length; offset += 25) {
711
748
  chunks.push({ offset, tasks: uniqueTasks.slice(offset, offset + 25) });
@@ -763,6 +800,13 @@ var Signaliz = class {
763
800
  };
764
801
  }
765
802
  };
803
+ function recoverableJobRowFailed(row) {
804
+ return row.success === false || ["failed", "skipped"].includes(String(row._status || "").toLowerCase());
805
+ }
806
+ function newBatchIdempotencyKey(label) {
807
+ const nonce = globalThis.crypto?.randomUUID?.() || `${Date.now()}-${Math.random().toString(16).slice(2)}`;
808
+ return `signaliz-${label.toLowerCase().replace(/[^a-z0-9]+/g, "-")}-${nonce}`;
809
+ }
766
810
  function compact(input) {
767
811
  return Object.fromEntries(Object.entries(input).filter(([, value]) => value !== void 0));
768
812
  }
package/dist/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  Signaliz,
3
3
  SignalizError
4
- } from "./chunk-JO4MYKSK.mjs";
4
+ } from "./chunk-BGVNI5E2.mjs";
5
5
  export {
6
6
  Signaliz,
7
7
  SignalizError
@@ -597,20 +597,39 @@ var Signaliz = class {
597
597
  return uniqueResults;
598
598
  }
599
599
  async runAvailableSignalCopyBatchJob(requests) {
600
+ const rows = await this.runRecoverableCoreBatchJob(
601
+ "api/v1/signal-to-copy",
602
+ requests.map(signalToCopyRequestBody),
603
+ "Signal to Copy"
604
+ );
605
+ const results = new Array(requests.length);
606
+ for (const row of rows) {
607
+ const index = Number(row.index);
608
+ results[index] = recoverableJobRowFailed(row) ? { index, success: false, error: row.error || row.message || "Signal to Copy failed" } : { index, success: true, data: normalizeSignalToCopyResult(row) };
609
+ }
610
+ return results;
611
+ }
612
+ async runRecoverableCoreBatchJob(path2, requests, label) {
600
613
  const startedAt = Date.now();
601
614
  const maxWaitMs = 20 * 6e4;
602
- let status = await this.client.post("api/v1/signal-to-copy", {
603
- requests: requests.map(signalToCopyRequestBody)
615
+ const submission = await this.client.post(path2, {
616
+ requests,
617
+ idempotency_key: newBatchIdempotencyKey(label)
618
+ });
619
+ const jobId = String(submission.job_id || "").trim();
620
+ if (!jobId) throw new Error(`${label} batch did not return a recoverable job_id`);
621
+ let status = await this.client.post(path2, {
622
+ job_id: jobId,
623
+ page: 1,
624
+ page_size: 500
604
625
  });
605
- const jobId = String(status.job_id || "").trim();
606
- if (!jobId) throw new Error("Signal to Copy batch did not return a recoverable job_id");
607
626
  while (!["completed", "partial", "failed"].includes(String(status.status || "").toLowerCase())) {
608
627
  if (Date.now() - startedAt >= maxWaitMs) {
609
- throw new Error(`Signal to Copy batch ${jobId} did not complete within ${maxWaitMs}ms`);
628
+ throw new Error(`${label} batch ${jobId} did not complete within ${maxWaitMs}ms`);
610
629
  }
611
630
  const retryAfterMs = Number(status.next_poll_after_seconds || 2) * 1e3;
612
631
  await sleep2(Math.min(Math.max(250, retryAfterMs), maxWaitMs - (Date.now() - startedAt)));
613
- status = await this.client.post("api/v1/signal-to-copy", {
632
+ status = await this.client.post(path2, {
614
633
  job_id: jobId,
615
634
  page: 1,
616
635
  page_size: 500
@@ -621,7 +640,7 @@ var Signaliz = class {
621
640
  if (totalPages > 1) {
622
641
  const remainingPages = await Promise.all(Array.from(
623
642
  { length: totalPages - 1 },
624
- (_, index) => this.client.post("api/v1/signal-to-copy", {
643
+ (_, index) => this.client.post(path2, {
625
644
  job_id: jobId,
626
645
  page: index + 2,
627
646
  page_size: 500
@@ -631,18 +650,18 @@ var Signaliz = class {
631
650
  }
632
651
  const rows = pages.flat();
633
652
  if (rows.length !== requests.length) {
634
- throw new Error(`Signal to Copy batch returned ${rows.length} results for ${requests.length} requests`);
653
+ throw new Error(`${label} batch returned ${rows.length} results for ${requests.length} requests`);
635
654
  }
636
- const results = new Array(requests.length);
655
+ const seen = /* @__PURE__ */ new Set();
637
656
  for (const row of rows) {
638
657
  const index = Number(row.index);
639
- if (!Number.isInteger(index) || index < 0 || index >= requests.length) {
640
- throw new Error("Signal to Copy batch returned an invalid result index");
658
+ if (!Number.isInteger(index) || index < 0 || index >= requests.length || seen.has(index)) {
659
+ throw new Error(`${label} batch returned an invalid result index`);
641
660
  }
642
- results[index] = row.success === false ? { index, success: false, error: row.error || row.message || "Signal to Copy failed" } : { index, success: true, data: normalizeSignalToCopyResult(row) };
661
+ seen.add(index);
643
662
  }
644
- if (results.some((item) => !item)) throw new Error("Signal to Copy batch returned incomplete indexed results");
645
- return results;
663
+ if (seen.size !== requests.length) throw new Error(`${label} batch returned incomplete indexed results`);
664
+ return rows;
646
665
  }
647
666
  async runSignalCopyBatchChunk(requests, concurrency) {
648
667
  const startedAt = Date.now();
@@ -710,6 +729,24 @@ var Signaliz = class {
710
729
  const { serverConcurrency, chunkConcurrency } = batchConcurrencyPlan(options);
711
730
  const deduplicated = dedupeExactBatchItems(tasks, (task) => JSON.stringify(task.request));
712
731
  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";
734
+ const rows = await this.runRecoverableCoreBatchJob(
735
+ path2,
736
+ uniqueTasks.map((task) => task.request),
737
+ label
738
+ );
739
+ const uniqueResults2 = rows.map((raw) => ({
740
+ index: uniqueTasks[Number(raw.index)].index,
741
+ success: !recoverableJobRowFailed(raw),
742
+ raw,
743
+ error: recoverableJobRowFailed(raw) ? raw.error || raw._error || raw.message || raw.failure_reason || `${label} item failed` : void 0
744
+ }));
745
+ return tasks.map((task, index) => ({
746
+ ...uniqueResults2[deduplicated.sourceIndexByInput[index]],
747
+ index: task.index
748
+ }));
749
+ }
713
750
  const chunks = [];
714
751
  for (let offset = 0; offset < uniqueTasks.length; offset += 25) {
715
752
  chunks.push({ offset, tasks: uniqueTasks.slice(offset, offset + 25) });
@@ -767,6 +804,13 @@ var Signaliz = class {
767
804
  };
768
805
  }
769
806
  };
807
+ function recoverableJobRowFailed(row) {
808
+ return row.success === false || ["failed", "skipped"].includes(String(row._status || "").toLowerCase());
809
+ }
810
+ function newBatchIdempotencyKey(label) {
811
+ const nonce = globalThis.crypto?.randomUUID?.() || `${Date.now()}-${Math.random().toString(16).slice(2)}`;
812
+ return `signaliz-${label.toLowerCase().replace(/[^a-z0-9]+/g, "-")}-${nonce}`;
813
+ }
770
814
  function compact(input) {
771
815
  return Object.fromEntries(Object.entries(input).filter(([, value]) => value !== void 0));
772
816
  }
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  Signaliz
4
- } from "./chunk-JO4MYKSK.mjs";
4
+ } from "./chunk-BGVNI5E2.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.32",
3
+ "version": "1.0.34",
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",