@signaliz/sdk 1.0.45 → 1.0.47
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 +16 -2
- package/dist/{chunk-NAJO2CAD.mjs → chunk-KTWDTUCI.mjs} +126 -19
- package/dist/index.d.mts +66 -0
- package/dist/index.d.ts +66 -0
- package/dist/index.js +126 -19
- package/dist/index.mjs +1 -1
- package/dist/mcp-config.js +126 -19
- package/dist/mcp-config.mjs +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -20,6 +20,15 @@ const signaliz = new Signaliz({
|
|
|
20
20
|
apiKey: process.env.SIGNALIZ_API_KEY,
|
|
21
21
|
});
|
|
22
22
|
|
|
23
|
+
// Every product supports a strict no-spend preflight. Batch methods accept the
|
|
24
|
+
// same option and return one plan for the full input.
|
|
25
|
+
const plan = await signaliz.findEmail({
|
|
26
|
+
companyDomain: 'example.com',
|
|
27
|
+
fullName: 'Jane Doe',
|
|
28
|
+
dryRun: true,
|
|
29
|
+
});
|
|
30
|
+
console.log(plan.estimatedCredits.max, plan.creditsCharged); // 2, 0
|
|
31
|
+
|
|
23
32
|
const found = await signaliz.findEmail({
|
|
24
33
|
companyDomain: 'example.com',
|
|
25
34
|
fullName: 'Jane Doe',
|
|
@@ -100,8 +109,13 @@ const compactSignals = await signaliz.enrichCompanies(companies, {
|
|
|
100
109
|
});
|
|
101
110
|
```
|
|
102
111
|
|
|
103
|
-
|
|
104
|
-
|
|
112
|
+
Failed rows preserve `errorCode`, `retryEligible`, and `retryAfterSeconds`
|
|
113
|
+
when the REST API supplies them, including after automatic row retries are
|
|
114
|
+
exhausted.
|
|
115
|
+
|
|
116
|
+
For an ambiguous single request or batch submission failure, retry with the
|
|
117
|
+
same `idempotencyKey` to recover the original request or job without duplicate
|
|
118
|
+
work.
|
|
105
119
|
|
|
106
120
|
Each batch accepts up to 5,000 items. Company Signal Enrichment transparently
|
|
107
121
|
resumes long-running research through the same `/company-signals` endpoint;
|
|
@@ -378,10 +378,17 @@ var Signaliz = class {
|
|
|
378
378
|
}
|
|
379
379
|
async findEmail(params) {
|
|
380
380
|
const data = await this.client.post("api/v1/find-email", findEmailRequestBody(params));
|
|
381
|
-
return normalizeFindEmailResult(data);
|
|
381
|
+
return isCoreProductDryRun(data) ? normalizeCoreProductDryRunResult(data) : normalizeFindEmailResult(data);
|
|
382
382
|
}
|
|
383
383
|
async findEmails(params, options) {
|
|
384
384
|
validateBatchSize(params);
|
|
385
|
+
if (options?.dryRun === true) {
|
|
386
|
+
return this.runCoreProductDryRun(
|
|
387
|
+
"api/v1/find-email",
|
|
388
|
+
params.map(findEmailRequestBody),
|
|
389
|
+
options.idempotencyKey
|
|
390
|
+
);
|
|
391
|
+
}
|
|
385
392
|
const startedAt = Date.now();
|
|
386
393
|
const round = await this.runCoreBatchRound(
|
|
387
394
|
"api/v1/find-email",
|
|
@@ -400,9 +407,12 @@ var Signaliz = class {
|
|
|
400
407
|
const request = compact({
|
|
401
408
|
email: email || void 0,
|
|
402
409
|
verification_run_id: options?.verificationRunId,
|
|
403
|
-
skip_cache: options?.skipCache
|
|
410
|
+
skip_cache: options?.skipCache,
|
|
411
|
+
dry_run: options?.dryRun,
|
|
412
|
+
idempotency_key: options?.idempotencyKey
|
|
404
413
|
});
|
|
405
414
|
let data = await this.client.post("api/v1/verify-email", request);
|
|
415
|
+
if (isCoreProductDryRun(data)) return normalizeCoreProductDryRunResult(data);
|
|
406
416
|
if (options?.waitForResult !== false && isPendingSignalResponse(data) && data.verification_run_id) {
|
|
407
417
|
const maxWaitMs = Math.max(1e3, options?.maxWaitMs ?? 10 * 6e4);
|
|
408
418
|
const startedAt = Date.now();
|
|
@@ -422,6 +432,13 @@ var Signaliz = class {
|
|
|
422
432
|
}
|
|
423
433
|
async verifyEmails(emails, options) {
|
|
424
434
|
validateBatchSize(emails);
|
|
435
|
+
if (options?.dryRun === true) {
|
|
436
|
+
return this.runCoreProductDryRun(
|
|
437
|
+
"api/v1/verify-email",
|
|
438
|
+
emails.map((email) => ({ email, skip_cache: options.skipCache })),
|
|
439
|
+
options.idempotencyKey
|
|
440
|
+
);
|
|
441
|
+
}
|
|
425
442
|
const startedAt = Date.now();
|
|
426
443
|
const round = await this.runCoreBatchRound(
|
|
427
444
|
"api/v1/verify-email",
|
|
@@ -442,6 +459,7 @@ var Signaliz = class {
|
|
|
442
459
|
async enrichCompanySignals(params) {
|
|
443
460
|
const request = companySignalRequestBody(params);
|
|
444
461
|
let data = await this.client.post("api/v1/company-signals", request);
|
|
462
|
+
if (isCoreProductDryRun(data)) return normalizeCoreProductDryRunResult(data);
|
|
445
463
|
if (params.waitForResult !== false && data.run_id && isPendingSignalResponse(data)) {
|
|
446
464
|
const maxWaitMs = Math.max(1e3, params.maxWaitMs ?? 20 * 6e4);
|
|
447
465
|
const startedAt = Date.now();
|
|
@@ -472,6 +490,13 @@ var Signaliz = class {
|
|
|
472
490
|
}
|
|
473
491
|
async enrichCompanies(companies, options) {
|
|
474
492
|
validateBatchSize(companies);
|
|
493
|
+
if (options?.dryRun === true) {
|
|
494
|
+
return this.runCoreProductDryRun(
|
|
495
|
+
"api/v1/company-signals",
|
|
496
|
+
companies.map(companySignalRequestBody),
|
|
497
|
+
options.idempotencyKey
|
|
498
|
+
);
|
|
499
|
+
}
|
|
475
500
|
const startedAt = Date.now();
|
|
476
501
|
const results = new Array(companies.length);
|
|
477
502
|
const initialTasks = companies.map((params, index) => ({
|
|
@@ -496,7 +521,11 @@ var Signaliz = class {
|
|
|
496
521
|
const params = companies[item.index];
|
|
497
522
|
const task = taskByIndex.get(item.index);
|
|
498
523
|
if (!item.success || !item.raw) {
|
|
499
|
-
results[item.index] =
|
|
524
|
+
results[item.index] = batchItemFailure(
|
|
525
|
+
item.index,
|
|
526
|
+
item.error || "Company Signal batch request failed",
|
|
527
|
+
item.raw ?? item
|
|
528
|
+
);
|
|
500
529
|
continue;
|
|
501
530
|
}
|
|
502
531
|
if (!isPendingSignalResponse(item.raw) || params.waitForResult === false) {
|
|
@@ -545,6 +574,7 @@ var Signaliz = class {
|
|
|
545
574
|
validateSignalToCopyParams(params);
|
|
546
575
|
const request = signalToCopyRequestBody(params);
|
|
547
576
|
let data = await this.client.post("api/v1/signal-to-copy", request);
|
|
577
|
+
if (isCoreProductDryRun(data)) return normalizeCoreProductDryRunResult(data);
|
|
548
578
|
if (params.waitForResult !== false && (data.signal_run_id || data.run_id) && isPendingSignalResponse(data)) {
|
|
549
579
|
const maxWaitMs = Math.max(1e3, params.maxWaitMs ?? 20 * 6e4);
|
|
550
580
|
const startedAt = Date.now();
|
|
@@ -563,6 +593,13 @@ var Signaliz = class {
|
|
|
563
593
|
async createSignalCopyBatch(requests, options) {
|
|
564
594
|
validateBatchSize(requests);
|
|
565
595
|
requests.forEach(validateSignalToCopyParams);
|
|
596
|
+
if (options?.dryRun === true) {
|
|
597
|
+
return this.runCoreProductDryRun(
|
|
598
|
+
"api/v1/signal-to-copy",
|
|
599
|
+
requests.map(signalToCopyRequestBody),
|
|
600
|
+
options.idempotencyKey
|
|
601
|
+
);
|
|
602
|
+
}
|
|
566
603
|
const startedAt = Date.now();
|
|
567
604
|
const deduplicated = dedupeExactBatchItems(requests, (params) => JSON.stringify({
|
|
568
605
|
request: signalToCopyRequestBody(params),
|
|
@@ -589,6 +626,17 @@ var Signaliz = class {
|
|
|
589
626
|
results: compactedResults
|
|
590
627
|
};
|
|
591
628
|
}
|
|
629
|
+
async runCoreProductDryRun(path, requests, idempotencyKey) {
|
|
630
|
+
const data = await this.client.post(path, compact({
|
|
631
|
+
requests,
|
|
632
|
+
dry_run: true,
|
|
633
|
+
idempotency_key: idempotencyKey
|
|
634
|
+
}));
|
|
635
|
+
if (!isCoreProductDryRun(data)) {
|
|
636
|
+
throw new Error(`${path} dry run returned a live-result contract`);
|
|
637
|
+
}
|
|
638
|
+
return normalizeCoreProductDryRunResult(data);
|
|
639
|
+
}
|
|
592
640
|
async runSignalCopyBatchChunks(uniqueRequests, options) {
|
|
593
641
|
const chunks = [];
|
|
594
642
|
for (let offset = 0; offset < uniqueRequests.length; offset += 25) {
|
|
@@ -605,11 +653,11 @@ var Signaliz = class {
|
|
|
605
653
|
const chunk = chunks[chunkResult.index];
|
|
606
654
|
if (!chunkResult.success || !chunkResult.data) {
|
|
607
655
|
for (let index = 0; index < chunk.requests.length; index += 1) {
|
|
608
|
-
uniqueResults[chunk.offset + index] =
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
656
|
+
uniqueResults[chunk.offset + index] = batchItemFailure(
|
|
657
|
+
chunk.offset + index,
|
|
658
|
+
chunkResult.error || "Signal to Copy batch request failed",
|
|
659
|
+
chunkResult
|
|
660
|
+
);
|
|
613
661
|
}
|
|
614
662
|
continue;
|
|
615
663
|
}
|
|
@@ -631,7 +679,7 @@ var Signaliz = class {
|
|
|
631
679
|
const results = new Array(requests.length);
|
|
632
680
|
for (const row of rows) {
|
|
633
681
|
const index = Number(row.index);
|
|
634
|
-
results[index] = recoverableJobRowFailed(row) ?
|
|
682
|
+
results[index] = recoverableJobRowFailed(row) ? batchItemFailure(index, row.error || row.message || "Signal to Copy failed", row) : { index, success: true, data: normalizeSignalToCopyResult(row) };
|
|
635
683
|
}
|
|
636
684
|
return results;
|
|
637
685
|
}
|
|
@@ -736,7 +784,11 @@ var Signaliz = class {
|
|
|
736
784
|
rateLimited.push({ index: task.index, params: task.params, raw: item });
|
|
737
785
|
continue;
|
|
738
786
|
}
|
|
739
|
-
results[task.index] =
|
|
787
|
+
results[task.index] = batchItemFailure(
|
|
788
|
+
task.index,
|
|
789
|
+
item.error || item.message || "Signal to Copy failed",
|
|
790
|
+
item
|
|
791
|
+
);
|
|
740
792
|
continue;
|
|
741
793
|
}
|
|
742
794
|
if (!isPendingSignalResponse(item)) {
|
|
@@ -841,7 +893,10 @@ var Signaliz = class {
|
|
|
841
893
|
uniqueResults[chunk.offset + index] = {
|
|
842
894
|
index: chunk.tasks[index].index,
|
|
843
895
|
success: false,
|
|
844
|
-
error: chunkResult.error || `${path} batch request failed
|
|
896
|
+
error: chunkResult.error || `${path} batch request failed`,
|
|
897
|
+
errorCode: chunkResult.errorCode,
|
|
898
|
+
retryEligible: chunkResult.retryEligible,
|
|
899
|
+
retryAfterSeconds: chunkResult.retryAfterSeconds
|
|
845
900
|
};
|
|
846
901
|
}
|
|
847
902
|
continue;
|
|
@@ -907,6 +962,21 @@ function rowRateLimitDelayMs(items) {
|
|
|
907
962
|
});
|
|
908
963
|
return Math.max(1, Math.min(6e4, Math.max(...delays)));
|
|
909
964
|
}
|
|
965
|
+
function batchItemFailure(index, error, raw) {
|
|
966
|
+
const errorCode = String(raw?.error_code ?? raw?.code ?? raw?.errorCode ?? "").trim();
|
|
967
|
+
const retryAfterSeconds2 = Number(
|
|
968
|
+
raw?.retry_after_seconds ?? raw?.retry_after ?? raw?.retryAfterSeconds ?? (Number.isFinite(Number(raw?.retry_after_ms)) ? Number(raw?.retry_after_ms) / 1e3 : void 0)
|
|
969
|
+
);
|
|
970
|
+
const retryEligible = raw?.retry_eligible ?? raw?.retryEligible;
|
|
971
|
+
return {
|
|
972
|
+
index,
|
|
973
|
+
success: false,
|
|
974
|
+
error,
|
|
975
|
+
...errorCode ? { errorCode } : {},
|
|
976
|
+
...typeof retryEligible === "boolean" ? { retryEligible } : {},
|
|
977
|
+
...Number.isFinite(retryAfterSeconds2) && retryAfterSeconds2 >= 0 ? { retryAfterSeconds: retryAfterSeconds2 } : {}
|
|
978
|
+
};
|
|
979
|
+
}
|
|
910
980
|
function recoverableJobRowFailed(row) {
|
|
911
981
|
return row.success === false || ["failed", "skipped"].includes(String(row._status || "").toLowerCase());
|
|
912
982
|
}
|
|
@@ -925,9 +995,34 @@ function findEmailRequestBody(params) {
|
|
|
925
995
|
last_name: params.lastName,
|
|
926
996
|
linkedin_url: params.linkedinUrl,
|
|
927
997
|
company_name: params.companyName,
|
|
928
|
-
skip_cache: params.skipCache
|
|
998
|
+
skip_cache: params.skipCache,
|
|
999
|
+
dry_run: params.dryRun,
|
|
1000
|
+
idempotency_key: params.idempotencyKey
|
|
929
1001
|
});
|
|
930
1002
|
}
|
|
1003
|
+
function isCoreProductDryRun(data) {
|
|
1004
|
+
return data.dry_run === true && data.status === "planned";
|
|
1005
|
+
}
|
|
1006
|
+
function normalizeCoreProductDryRunResult(data) {
|
|
1007
|
+
const estimatedCredits = data.estimated_credits && typeof data.estimated_credits === "object" ? data.estimated_credits : { min: 0, max: 0 };
|
|
1008
|
+
return {
|
|
1009
|
+
success: true,
|
|
1010
|
+
dryRun: true,
|
|
1011
|
+
status: "planned",
|
|
1012
|
+
capability: String(data.capability || ""),
|
|
1013
|
+
inputCount: Number(data.input_count || 0),
|
|
1014
|
+
recoveryReadCount: Number(data.recovery_read_count || 0),
|
|
1015
|
+
plan: data.plan && typeof data.plan === "object" ? data.plan : {},
|
|
1016
|
+
estimate: data.estimate && typeof data.estimate === "object" ? data.estimate : {},
|
|
1017
|
+
estimatedCredits: {
|
|
1018
|
+
min: Number(estimatedCredits.min || 0),
|
|
1019
|
+
max: Number(estimatedCredits.max || 0)
|
|
1020
|
+
},
|
|
1021
|
+
creditsCharged: 0,
|
|
1022
|
+
sideEffects: Array.isArray(data.side_effects) ? data.side_effects : [],
|
|
1023
|
+
raw: data
|
|
1024
|
+
};
|
|
1025
|
+
}
|
|
931
1026
|
function normalizeFindEmailResult(data) {
|
|
932
1027
|
const email = typeof data.email === "string" && data.email.trim() ? data.email.trim() : null;
|
|
933
1028
|
const found = Boolean(email) && data.found !== false;
|
|
@@ -996,7 +1091,9 @@ function companySignalRequestBody(params) {
|
|
|
996
1091
|
// REST always returns a resumable run instead of holding the connection;
|
|
997
1092
|
// waitForResult controls the SDK polling loop.
|
|
998
1093
|
wait_for_result: false,
|
|
999
|
-
max_wait_ms: params.maxWaitMs
|
|
1094
|
+
max_wait_ms: params.maxWaitMs,
|
|
1095
|
+
dry_run: params.dryRun,
|
|
1096
|
+
idempotency_key: params.idempotencyKey
|
|
1000
1097
|
});
|
|
1001
1098
|
}
|
|
1002
1099
|
function companySignalRecoverableBatchOptions(companies, tasks) {
|
|
@@ -1117,7 +1214,11 @@ function finalizeCoreBatch(total, startedAt, round, normalize, options) {
|
|
|
1117
1214
|
const results = new Array(total);
|
|
1118
1215
|
for (const item of round) {
|
|
1119
1216
|
if (!item.success || !item.raw) {
|
|
1120
|
-
results[item.index] =
|
|
1217
|
+
results[item.index] = batchItemFailure(
|
|
1218
|
+
item.index,
|
|
1219
|
+
item.error || "Signaliz batch item failed",
|
|
1220
|
+
item.raw ?? item
|
|
1221
|
+
);
|
|
1121
1222
|
continue;
|
|
1122
1223
|
}
|
|
1123
1224
|
try {
|
|
@@ -1164,7 +1265,9 @@ function signalToCopyRequestBody(params) {
|
|
|
1164
1265
|
research_prompt: params.researchPrompt,
|
|
1165
1266
|
signal_run_id: params.signalRunId,
|
|
1166
1267
|
skip_cache: params.skipCache,
|
|
1167
|
-
enable_deep_search: params.enableDeepSearch
|
|
1268
|
+
enable_deep_search: params.enableDeepSearch,
|
|
1269
|
+
dry_run: params.dryRun,
|
|
1270
|
+
idempotency_key: params.idempotencyKey
|
|
1168
1271
|
});
|
|
1169
1272
|
}
|
|
1170
1273
|
function validateSignalToCopyParams(params) {
|
|
@@ -1245,11 +1348,15 @@ async function runBatch(items, execute, options) {
|
|
|
1245
1348
|
try {
|
|
1246
1349
|
results[index] = { index, success: true, data: await execute(items[index]) };
|
|
1247
1350
|
} catch (error) {
|
|
1248
|
-
results[index] =
|
|
1351
|
+
results[index] = batchItemFailure(
|
|
1249
1352
|
index,
|
|
1250
|
-
|
|
1251
|
-
error
|
|
1252
|
-
|
|
1353
|
+
error instanceof Error ? error.message : String(error),
|
|
1354
|
+
error instanceof SignalizError ? {
|
|
1355
|
+
error_code: error.code,
|
|
1356
|
+
retry_eligible: error.isRetryable,
|
|
1357
|
+
retry_after_seconds: error.retryAfter
|
|
1358
|
+
} : void 0
|
|
1359
|
+
);
|
|
1253
1360
|
}
|
|
1254
1361
|
}
|
|
1255
1362
|
};
|
package/dist/index.d.mts
CHANGED
|
@@ -13,12 +13,20 @@ interface BatchOptions {
|
|
|
13
13
|
idempotencyKey?: string;
|
|
14
14
|
/** Return exact repeated successes as duplicateOf references. Defaults to false. */
|
|
15
15
|
compactDuplicates?: boolean;
|
|
16
|
+
/** Return one no-spend plan for the full batch without provider calls or jobs. */
|
|
17
|
+
dryRun?: boolean;
|
|
16
18
|
}
|
|
17
19
|
interface BatchItemResult<T> {
|
|
18
20
|
index: number;
|
|
19
21
|
success: boolean;
|
|
20
22
|
data?: T;
|
|
21
23
|
error?: string;
|
|
24
|
+
/** Stable public error code for a failed row. */
|
|
25
|
+
errorCode?: string;
|
|
26
|
+
/** Whether retrying the failed row is safe. */
|
|
27
|
+
retryEligible?: boolean;
|
|
28
|
+
/** Server-provided delay before retrying the failed row. */
|
|
29
|
+
retryAfterSeconds?: number;
|
|
22
30
|
/** Zero-based input index containing the full result for this exact duplicate. */
|
|
23
31
|
duplicateOf?: number;
|
|
24
32
|
}
|
|
@@ -37,6 +45,23 @@ interface SignalizErrorData {
|
|
|
37
45
|
retryAfter?: number;
|
|
38
46
|
details?: Record<string, unknown>;
|
|
39
47
|
}
|
|
48
|
+
interface CoreProductDryRunResult {
|
|
49
|
+
success: true;
|
|
50
|
+
dryRun: true;
|
|
51
|
+
status: 'planned';
|
|
52
|
+
capability: string;
|
|
53
|
+
inputCount: number;
|
|
54
|
+
recoveryReadCount: number;
|
|
55
|
+
plan: Record<string, unknown>;
|
|
56
|
+
estimate: Record<string, unknown>;
|
|
57
|
+
estimatedCredits: {
|
|
58
|
+
min: number;
|
|
59
|
+
max: number;
|
|
60
|
+
};
|
|
61
|
+
creditsCharged: 0;
|
|
62
|
+
sideEffects: unknown[];
|
|
63
|
+
raw: Record<string, unknown>;
|
|
64
|
+
}
|
|
40
65
|
interface FindEmailParams {
|
|
41
66
|
companyDomain: string;
|
|
42
67
|
fullName?: string;
|
|
@@ -46,6 +71,10 @@ interface FindEmailParams {
|
|
|
46
71
|
companyName?: string;
|
|
47
72
|
/** Bypass cached email and verification data for a fresh-provider run. */
|
|
48
73
|
skipCache?: boolean;
|
|
74
|
+
/** Return a no-spend plan without provider calls or jobs. */
|
|
75
|
+
dryRun?: boolean;
|
|
76
|
+
/** Stable retry key for recovering the same request after an ambiguous response. */
|
|
77
|
+
idempotencyKey?: string;
|
|
49
78
|
}
|
|
50
79
|
interface FindEmailResult {
|
|
51
80
|
success: boolean;
|
|
@@ -94,6 +123,10 @@ interface VerifyEmailOptions {
|
|
|
94
123
|
maxWaitMs?: number;
|
|
95
124
|
/** Override the server-provided polling delay. */
|
|
96
125
|
pollIntervalMs?: number;
|
|
126
|
+
/** Return a no-spend plan without provider calls or jobs. */
|
|
127
|
+
dryRun?: boolean;
|
|
128
|
+
/** Stable retry key for recovering the same request after an ambiguous response. */
|
|
129
|
+
idempotencyKey?: string;
|
|
97
130
|
}
|
|
98
131
|
interface VerifyEmailBatchOptions extends BatchOptions, VerifyEmailOptions {
|
|
99
132
|
}
|
|
@@ -118,6 +151,10 @@ interface CompanySignalEnrichmentParams {
|
|
|
118
151
|
maxWaitMs?: number;
|
|
119
152
|
/** Delay between queued-enrichment status checks. Defaults to 2 seconds. */
|
|
120
153
|
pollIntervalMs?: number;
|
|
154
|
+
/** Return a no-spend plan without provider calls or jobs. */
|
|
155
|
+
dryRun?: boolean;
|
|
156
|
+
/** Stable retry key for recovering the same request after an ambiguous response. */
|
|
157
|
+
idempotencyKey?: string;
|
|
121
158
|
}
|
|
122
159
|
interface CompanySignal {
|
|
123
160
|
id?: string;
|
|
@@ -180,6 +217,10 @@ interface SignalToCopyParams {
|
|
|
180
217
|
waitForResult?: boolean;
|
|
181
218
|
maxWaitMs?: number;
|
|
182
219
|
pollIntervalMs?: number;
|
|
220
|
+
/** Return a no-spend plan without provider calls or jobs. */
|
|
221
|
+
dryRun?: boolean;
|
|
222
|
+
/** Stable retry key for recovering the same request after an ambiguous response. */
|
|
223
|
+
idempotencyKey?: string;
|
|
183
224
|
}
|
|
184
225
|
interface SignalToCopyVariation {
|
|
185
226
|
style: 'signal_led' | 'business_case' | 'predictive';
|
|
@@ -231,14 +272,39 @@ declare class SignalizError extends Error {
|
|
|
231
272
|
declare class Signaliz {
|
|
232
273
|
private readonly client;
|
|
233
274
|
constructor(config: SignalizConfig);
|
|
275
|
+
findEmail(params: FindEmailParams & {
|
|
276
|
+
dryRun: true;
|
|
277
|
+
}): Promise<CoreProductDryRunResult>;
|
|
234
278
|
findEmail(params: FindEmailParams): Promise<FindEmailResult>;
|
|
279
|
+
findEmails(params: FindEmailParams[], options: BatchOptions & {
|
|
280
|
+
dryRun: true;
|
|
281
|
+
}): Promise<CoreProductDryRunResult>;
|
|
235
282
|
findEmails(params: FindEmailParams[], options?: BatchOptions): Promise<BatchResult<FindEmailResult>>;
|
|
283
|
+
verifyEmail(email: string | undefined, options: VerifyEmailOptions & {
|
|
284
|
+
dryRun: true;
|
|
285
|
+
}): Promise<CoreProductDryRunResult>;
|
|
236
286
|
verifyEmail(email: string | undefined, options?: VerifyEmailOptions): Promise<VerifyEmailResult>;
|
|
287
|
+
verifyEmails(emails: string[], options: VerifyEmailBatchOptions & {
|
|
288
|
+
dryRun: true;
|
|
289
|
+
}): Promise<CoreProductDryRunResult>;
|
|
237
290
|
verifyEmails(emails: string[], options?: VerifyEmailBatchOptions): Promise<BatchResult<VerifyEmailResult>>;
|
|
291
|
+
enrichCompanySignals(params: CompanySignalEnrichmentParams & {
|
|
292
|
+
dryRun: true;
|
|
293
|
+
}): Promise<CoreProductDryRunResult>;
|
|
238
294
|
enrichCompanySignals(params: CompanySignalEnrichmentParams): Promise<CompanySignalEnrichmentResult>;
|
|
295
|
+
enrichCompanies(companies: CompanySignalEnrichmentParams[], options: BatchOptions & {
|
|
296
|
+
dryRun: true;
|
|
297
|
+
}): Promise<CoreProductDryRunResult>;
|
|
239
298
|
enrichCompanies(companies: CompanySignalEnrichmentParams[], options?: BatchOptions): Promise<BatchResult<CompanySignalEnrichmentResult>>;
|
|
299
|
+
signalToCopy(params: SignalToCopyParams & {
|
|
300
|
+
dryRun: true;
|
|
301
|
+
}): Promise<CoreProductDryRunResult>;
|
|
240
302
|
signalToCopy(params: SignalToCopyParams): Promise<SignalToCopyResult>;
|
|
303
|
+
createSignalCopyBatch(requests: SignalToCopyParams[], options: BatchOptions & {
|
|
304
|
+
dryRun: true;
|
|
305
|
+
}): Promise<CoreProductDryRunResult>;
|
|
241
306
|
createSignalCopyBatch(requests: SignalToCopyParams[], options?: BatchOptions): Promise<BatchResult<SignalToCopyResult>>;
|
|
307
|
+
private runCoreProductDryRun;
|
|
242
308
|
private runSignalCopyBatchChunks;
|
|
243
309
|
private runAvailableSignalCopyBatchJob;
|
|
244
310
|
private runRecoverableCoreBatchJob;
|
package/dist/index.d.ts
CHANGED
|
@@ -13,12 +13,20 @@ interface BatchOptions {
|
|
|
13
13
|
idempotencyKey?: string;
|
|
14
14
|
/** Return exact repeated successes as duplicateOf references. Defaults to false. */
|
|
15
15
|
compactDuplicates?: boolean;
|
|
16
|
+
/** Return one no-spend plan for the full batch without provider calls or jobs. */
|
|
17
|
+
dryRun?: boolean;
|
|
16
18
|
}
|
|
17
19
|
interface BatchItemResult<T> {
|
|
18
20
|
index: number;
|
|
19
21
|
success: boolean;
|
|
20
22
|
data?: T;
|
|
21
23
|
error?: string;
|
|
24
|
+
/** Stable public error code for a failed row. */
|
|
25
|
+
errorCode?: string;
|
|
26
|
+
/** Whether retrying the failed row is safe. */
|
|
27
|
+
retryEligible?: boolean;
|
|
28
|
+
/** Server-provided delay before retrying the failed row. */
|
|
29
|
+
retryAfterSeconds?: number;
|
|
22
30
|
/** Zero-based input index containing the full result for this exact duplicate. */
|
|
23
31
|
duplicateOf?: number;
|
|
24
32
|
}
|
|
@@ -37,6 +45,23 @@ interface SignalizErrorData {
|
|
|
37
45
|
retryAfter?: number;
|
|
38
46
|
details?: Record<string, unknown>;
|
|
39
47
|
}
|
|
48
|
+
interface CoreProductDryRunResult {
|
|
49
|
+
success: true;
|
|
50
|
+
dryRun: true;
|
|
51
|
+
status: 'planned';
|
|
52
|
+
capability: string;
|
|
53
|
+
inputCount: number;
|
|
54
|
+
recoveryReadCount: number;
|
|
55
|
+
plan: Record<string, unknown>;
|
|
56
|
+
estimate: Record<string, unknown>;
|
|
57
|
+
estimatedCredits: {
|
|
58
|
+
min: number;
|
|
59
|
+
max: number;
|
|
60
|
+
};
|
|
61
|
+
creditsCharged: 0;
|
|
62
|
+
sideEffects: unknown[];
|
|
63
|
+
raw: Record<string, unknown>;
|
|
64
|
+
}
|
|
40
65
|
interface FindEmailParams {
|
|
41
66
|
companyDomain: string;
|
|
42
67
|
fullName?: string;
|
|
@@ -46,6 +71,10 @@ interface FindEmailParams {
|
|
|
46
71
|
companyName?: string;
|
|
47
72
|
/** Bypass cached email and verification data for a fresh-provider run. */
|
|
48
73
|
skipCache?: boolean;
|
|
74
|
+
/** Return a no-spend plan without provider calls or jobs. */
|
|
75
|
+
dryRun?: boolean;
|
|
76
|
+
/** Stable retry key for recovering the same request after an ambiguous response. */
|
|
77
|
+
idempotencyKey?: string;
|
|
49
78
|
}
|
|
50
79
|
interface FindEmailResult {
|
|
51
80
|
success: boolean;
|
|
@@ -94,6 +123,10 @@ interface VerifyEmailOptions {
|
|
|
94
123
|
maxWaitMs?: number;
|
|
95
124
|
/** Override the server-provided polling delay. */
|
|
96
125
|
pollIntervalMs?: number;
|
|
126
|
+
/** Return a no-spend plan without provider calls or jobs. */
|
|
127
|
+
dryRun?: boolean;
|
|
128
|
+
/** Stable retry key for recovering the same request after an ambiguous response. */
|
|
129
|
+
idempotencyKey?: string;
|
|
97
130
|
}
|
|
98
131
|
interface VerifyEmailBatchOptions extends BatchOptions, VerifyEmailOptions {
|
|
99
132
|
}
|
|
@@ -118,6 +151,10 @@ interface CompanySignalEnrichmentParams {
|
|
|
118
151
|
maxWaitMs?: number;
|
|
119
152
|
/** Delay between queued-enrichment status checks. Defaults to 2 seconds. */
|
|
120
153
|
pollIntervalMs?: number;
|
|
154
|
+
/** Return a no-spend plan without provider calls or jobs. */
|
|
155
|
+
dryRun?: boolean;
|
|
156
|
+
/** Stable retry key for recovering the same request after an ambiguous response. */
|
|
157
|
+
idempotencyKey?: string;
|
|
121
158
|
}
|
|
122
159
|
interface CompanySignal {
|
|
123
160
|
id?: string;
|
|
@@ -180,6 +217,10 @@ interface SignalToCopyParams {
|
|
|
180
217
|
waitForResult?: boolean;
|
|
181
218
|
maxWaitMs?: number;
|
|
182
219
|
pollIntervalMs?: number;
|
|
220
|
+
/** Return a no-spend plan without provider calls or jobs. */
|
|
221
|
+
dryRun?: boolean;
|
|
222
|
+
/** Stable retry key for recovering the same request after an ambiguous response. */
|
|
223
|
+
idempotencyKey?: string;
|
|
183
224
|
}
|
|
184
225
|
interface SignalToCopyVariation {
|
|
185
226
|
style: 'signal_led' | 'business_case' | 'predictive';
|
|
@@ -231,14 +272,39 @@ declare class SignalizError extends Error {
|
|
|
231
272
|
declare class Signaliz {
|
|
232
273
|
private readonly client;
|
|
233
274
|
constructor(config: SignalizConfig);
|
|
275
|
+
findEmail(params: FindEmailParams & {
|
|
276
|
+
dryRun: true;
|
|
277
|
+
}): Promise<CoreProductDryRunResult>;
|
|
234
278
|
findEmail(params: FindEmailParams): Promise<FindEmailResult>;
|
|
279
|
+
findEmails(params: FindEmailParams[], options: BatchOptions & {
|
|
280
|
+
dryRun: true;
|
|
281
|
+
}): Promise<CoreProductDryRunResult>;
|
|
235
282
|
findEmails(params: FindEmailParams[], options?: BatchOptions): Promise<BatchResult<FindEmailResult>>;
|
|
283
|
+
verifyEmail(email: string | undefined, options: VerifyEmailOptions & {
|
|
284
|
+
dryRun: true;
|
|
285
|
+
}): Promise<CoreProductDryRunResult>;
|
|
236
286
|
verifyEmail(email: string | undefined, options?: VerifyEmailOptions): Promise<VerifyEmailResult>;
|
|
287
|
+
verifyEmails(emails: string[], options: VerifyEmailBatchOptions & {
|
|
288
|
+
dryRun: true;
|
|
289
|
+
}): Promise<CoreProductDryRunResult>;
|
|
237
290
|
verifyEmails(emails: string[], options?: VerifyEmailBatchOptions): Promise<BatchResult<VerifyEmailResult>>;
|
|
291
|
+
enrichCompanySignals(params: CompanySignalEnrichmentParams & {
|
|
292
|
+
dryRun: true;
|
|
293
|
+
}): Promise<CoreProductDryRunResult>;
|
|
238
294
|
enrichCompanySignals(params: CompanySignalEnrichmentParams): Promise<CompanySignalEnrichmentResult>;
|
|
295
|
+
enrichCompanies(companies: CompanySignalEnrichmentParams[], options: BatchOptions & {
|
|
296
|
+
dryRun: true;
|
|
297
|
+
}): Promise<CoreProductDryRunResult>;
|
|
239
298
|
enrichCompanies(companies: CompanySignalEnrichmentParams[], options?: BatchOptions): Promise<BatchResult<CompanySignalEnrichmentResult>>;
|
|
299
|
+
signalToCopy(params: SignalToCopyParams & {
|
|
300
|
+
dryRun: true;
|
|
301
|
+
}): Promise<CoreProductDryRunResult>;
|
|
240
302
|
signalToCopy(params: SignalToCopyParams): Promise<SignalToCopyResult>;
|
|
303
|
+
createSignalCopyBatch(requests: SignalToCopyParams[], options: BatchOptions & {
|
|
304
|
+
dryRun: true;
|
|
305
|
+
}): Promise<CoreProductDryRunResult>;
|
|
241
306
|
createSignalCopyBatch(requests: SignalToCopyParams[], options?: BatchOptions): Promise<BatchResult<SignalToCopyResult>>;
|
|
307
|
+
private runCoreProductDryRun;
|
|
242
308
|
private runSignalCopyBatchChunks;
|
|
243
309
|
private runAvailableSignalCopyBatchJob;
|
|
244
310
|
private runRecoverableCoreBatchJob;
|
package/dist/index.js
CHANGED
|
@@ -405,10 +405,17 @@ var Signaliz = class {
|
|
|
405
405
|
}
|
|
406
406
|
async findEmail(params) {
|
|
407
407
|
const data = await this.client.post("api/v1/find-email", findEmailRequestBody(params));
|
|
408
|
-
return normalizeFindEmailResult(data);
|
|
408
|
+
return isCoreProductDryRun(data) ? normalizeCoreProductDryRunResult(data) : normalizeFindEmailResult(data);
|
|
409
409
|
}
|
|
410
410
|
async findEmails(params, options) {
|
|
411
411
|
validateBatchSize(params);
|
|
412
|
+
if (options?.dryRun === true) {
|
|
413
|
+
return this.runCoreProductDryRun(
|
|
414
|
+
"api/v1/find-email",
|
|
415
|
+
params.map(findEmailRequestBody),
|
|
416
|
+
options.idempotencyKey
|
|
417
|
+
);
|
|
418
|
+
}
|
|
412
419
|
const startedAt = Date.now();
|
|
413
420
|
const round = await this.runCoreBatchRound(
|
|
414
421
|
"api/v1/find-email",
|
|
@@ -427,9 +434,12 @@ var Signaliz = class {
|
|
|
427
434
|
const request = compact({
|
|
428
435
|
email: email || void 0,
|
|
429
436
|
verification_run_id: options?.verificationRunId,
|
|
430
|
-
skip_cache: options?.skipCache
|
|
437
|
+
skip_cache: options?.skipCache,
|
|
438
|
+
dry_run: options?.dryRun,
|
|
439
|
+
idempotency_key: options?.idempotencyKey
|
|
431
440
|
});
|
|
432
441
|
let data = await this.client.post("api/v1/verify-email", request);
|
|
442
|
+
if (isCoreProductDryRun(data)) return normalizeCoreProductDryRunResult(data);
|
|
433
443
|
if (options?.waitForResult !== false && isPendingSignalResponse(data) && data.verification_run_id) {
|
|
434
444
|
const maxWaitMs = Math.max(1e3, options?.maxWaitMs ?? 10 * 6e4);
|
|
435
445
|
const startedAt = Date.now();
|
|
@@ -449,6 +459,13 @@ var Signaliz = class {
|
|
|
449
459
|
}
|
|
450
460
|
async verifyEmails(emails, options) {
|
|
451
461
|
validateBatchSize(emails);
|
|
462
|
+
if (options?.dryRun === true) {
|
|
463
|
+
return this.runCoreProductDryRun(
|
|
464
|
+
"api/v1/verify-email",
|
|
465
|
+
emails.map((email) => ({ email, skip_cache: options.skipCache })),
|
|
466
|
+
options.idempotencyKey
|
|
467
|
+
);
|
|
468
|
+
}
|
|
452
469
|
const startedAt = Date.now();
|
|
453
470
|
const round = await this.runCoreBatchRound(
|
|
454
471
|
"api/v1/verify-email",
|
|
@@ -469,6 +486,7 @@ var Signaliz = class {
|
|
|
469
486
|
async enrichCompanySignals(params) {
|
|
470
487
|
const request = companySignalRequestBody(params);
|
|
471
488
|
let data = await this.client.post("api/v1/company-signals", request);
|
|
489
|
+
if (isCoreProductDryRun(data)) return normalizeCoreProductDryRunResult(data);
|
|
472
490
|
if (params.waitForResult !== false && data.run_id && isPendingSignalResponse(data)) {
|
|
473
491
|
const maxWaitMs = Math.max(1e3, params.maxWaitMs ?? 20 * 6e4);
|
|
474
492
|
const startedAt = Date.now();
|
|
@@ -499,6 +517,13 @@ var Signaliz = class {
|
|
|
499
517
|
}
|
|
500
518
|
async enrichCompanies(companies, options) {
|
|
501
519
|
validateBatchSize(companies);
|
|
520
|
+
if (options?.dryRun === true) {
|
|
521
|
+
return this.runCoreProductDryRun(
|
|
522
|
+
"api/v1/company-signals",
|
|
523
|
+
companies.map(companySignalRequestBody),
|
|
524
|
+
options.idempotencyKey
|
|
525
|
+
);
|
|
526
|
+
}
|
|
502
527
|
const startedAt = Date.now();
|
|
503
528
|
const results = new Array(companies.length);
|
|
504
529
|
const initialTasks = companies.map((params, index) => ({
|
|
@@ -523,7 +548,11 @@ var Signaliz = class {
|
|
|
523
548
|
const params = companies[item.index];
|
|
524
549
|
const task = taskByIndex.get(item.index);
|
|
525
550
|
if (!item.success || !item.raw) {
|
|
526
|
-
results[item.index] =
|
|
551
|
+
results[item.index] = batchItemFailure(
|
|
552
|
+
item.index,
|
|
553
|
+
item.error || "Company Signal batch request failed",
|
|
554
|
+
item.raw ?? item
|
|
555
|
+
);
|
|
527
556
|
continue;
|
|
528
557
|
}
|
|
529
558
|
if (!isPendingSignalResponse(item.raw) || params.waitForResult === false) {
|
|
@@ -572,6 +601,7 @@ var Signaliz = class {
|
|
|
572
601
|
validateSignalToCopyParams(params);
|
|
573
602
|
const request = signalToCopyRequestBody(params);
|
|
574
603
|
let data = await this.client.post("api/v1/signal-to-copy", request);
|
|
604
|
+
if (isCoreProductDryRun(data)) return normalizeCoreProductDryRunResult(data);
|
|
575
605
|
if (params.waitForResult !== false && (data.signal_run_id || data.run_id) && isPendingSignalResponse(data)) {
|
|
576
606
|
const maxWaitMs = Math.max(1e3, params.maxWaitMs ?? 20 * 6e4);
|
|
577
607
|
const startedAt = Date.now();
|
|
@@ -590,6 +620,13 @@ var Signaliz = class {
|
|
|
590
620
|
async createSignalCopyBatch(requests, options) {
|
|
591
621
|
validateBatchSize(requests);
|
|
592
622
|
requests.forEach(validateSignalToCopyParams);
|
|
623
|
+
if (options?.dryRun === true) {
|
|
624
|
+
return this.runCoreProductDryRun(
|
|
625
|
+
"api/v1/signal-to-copy",
|
|
626
|
+
requests.map(signalToCopyRequestBody),
|
|
627
|
+
options.idempotencyKey
|
|
628
|
+
);
|
|
629
|
+
}
|
|
593
630
|
const startedAt = Date.now();
|
|
594
631
|
const deduplicated = dedupeExactBatchItems(requests, (params) => JSON.stringify({
|
|
595
632
|
request: signalToCopyRequestBody(params),
|
|
@@ -616,6 +653,17 @@ var Signaliz = class {
|
|
|
616
653
|
results: compactedResults
|
|
617
654
|
};
|
|
618
655
|
}
|
|
656
|
+
async runCoreProductDryRun(path, requests, idempotencyKey) {
|
|
657
|
+
const data = await this.client.post(path, compact({
|
|
658
|
+
requests,
|
|
659
|
+
dry_run: true,
|
|
660
|
+
idempotency_key: idempotencyKey
|
|
661
|
+
}));
|
|
662
|
+
if (!isCoreProductDryRun(data)) {
|
|
663
|
+
throw new Error(`${path} dry run returned a live-result contract`);
|
|
664
|
+
}
|
|
665
|
+
return normalizeCoreProductDryRunResult(data);
|
|
666
|
+
}
|
|
619
667
|
async runSignalCopyBatchChunks(uniqueRequests, options) {
|
|
620
668
|
const chunks = [];
|
|
621
669
|
for (let offset = 0; offset < uniqueRequests.length; offset += 25) {
|
|
@@ -632,11 +680,11 @@ var Signaliz = class {
|
|
|
632
680
|
const chunk = chunks[chunkResult.index];
|
|
633
681
|
if (!chunkResult.success || !chunkResult.data) {
|
|
634
682
|
for (let index = 0; index < chunk.requests.length; index += 1) {
|
|
635
|
-
uniqueResults[chunk.offset + index] =
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
683
|
+
uniqueResults[chunk.offset + index] = batchItemFailure(
|
|
684
|
+
chunk.offset + index,
|
|
685
|
+
chunkResult.error || "Signal to Copy batch request failed",
|
|
686
|
+
chunkResult
|
|
687
|
+
);
|
|
640
688
|
}
|
|
641
689
|
continue;
|
|
642
690
|
}
|
|
@@ -658,7 +706,7 @@ var Signaliz = class {
|
|
|
658
706
|
const results = new Array(requests.length);
|
|
659
707
|
for (const row of rows) {
|
|
660
708
|
const index = Number(row.index);
|
|
661
|
-
results[index] = recoverableJobRowFailed(row) ?
|
|
709
|
+
results[index] = recoverableJobRowFailed(row) ? batchItemFailure(index, row.error || row.message || "Signal to Copy failed", row) : { index, success: true, data: normalizeSignalToCopyResult(row) };
|
|
662
710
|
}
|
|
663
711
|
return results;
|
|
664
712
|
}
|
|
@@ -763,7 +811,11 @@ var Signaliz = class {
|
|
|
763
811
|
rateLimited.push({ index: task.index, params: task.params, raw: item });
|
|
764
812
|
continue;
|
|
765
813
|
}
|
|
766
|
-
results[task.index] =
|
|
814
|
+
results[task.index] = batchItemFailure(
|
|
815
|
+
task.index,
|
|
816
|
+
item.error || item.message || "Signal to Copy failed",
|
|
817
|
+
item
|
|
818
|
+
);
|
|
767
819
|
continue;
|
|
768
820
|
}
|
|
769
821
|
if (!isPendingSignalResponse(item)) {
|
|
@@ -868,7 +920,10 @@ var Signaliz = class {
|
|
|
868
920
|
uniqueResults[chunk.offset + index] = {
|
|
869
921
|
index: chunk.tasks[index].index,
|
|
870
922
|
success: false,
|
|
871
|
-
error: chunkResult.error || `${path} batch request failed
|
|
923
|
+
error: chunkResult.error || `${path} batch request failed`,
|
|
924
|
+
errorCode: chunkResult.errorCode,
|
|
925
|
+
retryEligible: chunkResult.retryEligible,
|
|
926
|
+
retryAfterSeconds: chunkResult.retryAfterSeconds
|
|
872
927
|
};
|
|
873
928
|
}
|
|
874
929
|
continue;
|
|
@@ -934,6 +989,21 @@ function rowRateLimitDelayMs(items) {
|
|
|
934
989
|
});
|
|
935
990
|
return Math.max(1, Math.min(6e4, Math.max(...delays)));
|
|
936
991
|
}
|
|
992
|
+
function batchItemFailure(index, error, raw) {
|
|
993
|
+
const errorCode = String(raw?.error_code ?? raw?.code ?? raw?.errorCode ?? "").trim();
|
|
994
|
+
const retryAfterSeconds2 = Number(
|
|
995
|
+
raw?.retry_after_seconds ?? raw?.retry_after ?? raw?.retryAfterSeconds ?? (Number.isFinite(Number(raw?.retry_after_ms)) ? Number(raw?.retry_after_ms) / 1e3 : void 0)
|
|
996
|
+
);
|
|
997
|
+
const retryEligible = raw?.retry_eligible ?? raw?.retryEligible;
|
|
998
|
+
return {
|
|
999
|
+
index,
|
|
1000
|
+
success: false,
|
|
1001
|
+
error,
|
|
1002
|
+
...errorCode ? { errorCode } : {},
|
|
1003
|
+
...typeof retryEligible === "boolean" ? { retryEligible } : {},
|
|
1004
|
+
...Number.isFinite(retryAfterSeconds2) && retryAfterSeconds2 >= 0 ? { retryAfterSeconds: retryAfterSeconds2 } : {}
|
|
1005
|
+
};
|
|
1006
|
+
}
|
|
937
1007
|
function recoverableJobRowFailed(row) {
|
|
938
1008
|
return row.success === false || ["failed", "skipped"].includes(String(row._status || "").toLowerCase());
|
|
939
1009
|
}
|
|
@@ -952,9 +1022,34 @@ function findEmailRequestBody(params) {
|
|
|
952
1022
|
last_name: params.lastName,
|
|
953
1023
|
linkedin_url: params.linkedinUrl,
|
|
954
1024
|
company_name: params.companyName,
|
|
955
|
-
skip_cache: params.skipCache
|
|
1025
|
+
skip_cache: params.skipCache,
|
|
1026
|
+
dry_run: params.dryRun,
|
|
1027
|
+
idempotency_key: params.idempotencyKey
|
|
956
1028
|
});
|
|
957
1029
|
}
|
|
1030
|
+
function isCoreProductDryRun(data) {
|
|
1031
|
+
return data.dry_run === true && data.status === "planned";
|
|
1032
|
+
}
|
|
1033
|
+
function normalizeCoreProductDryRunResult(data) {
|
|
1034
|
+
const estimatedCredits = data.estimated_credits && typeof data.estimated_credits === "object" ? data.estimated_credits : { min: 0, max: 0 };
|
|
1035
|
+
return {
|
|
1036
|
+
success: true,
|
|
1037
|
+
dryRun: true,
|
|
1038
|
+
status: "planned",
|
|
1039
|
+
capability: String(data.capability || ""),
|
|
1040
|
+
inputCount: Number(data.input_count || 0),
|
|
1041
|
+
recoveryReadCount: Number(data.recovery_read_count || 0),
|
|
1042
|
+
plan: data.plan && typeof data.plan === "object" ? data.plan : {},
|
|
1043
|
+
estimate: data.estimate && typeof data.estimate === "object" ? data.estimate : {},
|
|
1044
|
+
estimatedCredits: {
|
|
1045
|
+
min: Number(estimatedCredits.min || 0),
|
|
1046
|
+
max: Number(estimatedCredits.max || 0)
|
|
1047
|
+
},
|
|
1048
|
+
creditsCharged: 0,
|
|
1049
|
+
sideEffects: Array.isArray(data.side_effects) ? data.side_effects : [],
|
|
1050
|
+
raw: data
|
|
1051
|
+
};
|
|
1052
|
+
}
|
|
958
1053
|
function normalizeFindEmailResult(data) {
|
|
959
1054
|
const email = typeof data.email === "string" && data.email.trim() ? data.email.trim() : null;
|
|
960
1055
|
const found = Boolean(email) && data.found !== false;
|
|
@@ -1023,7 +1118,9 @@ function companySignalRequestBody(params) {
|
|
|
1023
1118
|
// REST always returns a resumable run instead of holding the connection;
|
|
1024
1119
|
// waitForResult controls the SDK polling loop.
|
|
1025
1120
|
wait_for_result: false,
|
|
1026
|
-
max_wait_ms: params.maxWaitMs
|
|
1121
|
+
max_wait_ms: params.maxWaitMs,
|
|
1122
|
+
dry_run: params.dryRun,
|
|
1123
|
+
idempotency_key: params.idempotencyKey
|
|
1027
1124
|
});
|
|
1028
1125
|
}
|
|
1029
1126
|
function companySignalRecoverableBatchOptions(companies, tasks) {
|
|
@@ -1144,7 +1241,11 @@ function finalizeCoreBatch(total, startedAt, round, normalize, options) {
|
|
|
1144
1241
|
const results = new Array(total);
|
|
1145
1242
|
for (const item of round) {
|
|
1146
1243
|
if (!item.success || !item.raw) {
|
|
1147
|
-
results[item.index] =
|
|
1244
|
+
results[item.index] = batchItemFailure(
|
|
1245
|
+
item.index,
|
|
1246
|
+
item.error || "Signaliz batch item failed",
|
|
1247
|
+
item.raw ?? item
|
|
1248
|
+
);
|
|
1148
1249
|
continue;
|
|
1149
1250
|
}
|
|
1150
1251
|
try {
|
|
@@ -1191,7 +1292,9 @@ function signalToCopyRequestBody(params) {
|
|
|
1191
1292
|
research_prompt: params.researchPrompt,
|
|
1192
1293
|
signal_run_id: params.signalRunId,
|
|
1193
1294
|
skip_cache: params.skipCache,
|
|
1194
|
-
enable_deep_search: params.enableDeepSearch
|
|
1295
|
+
enable_deep_search: params.enableDeepSearch,
|
|
1296
|
+
dry_run: params.dryRun,
|
|
1297
|
+
idempotency_key: params.idempotencyKey
|
|
1195
1298
|
});
|
|
1196
1299
|
}
|
|
1197
1300
|
function validateSignalToCopyParams(params) {
|
|
@@ -1272,11 +1375,15 @@ async function runBatch(items, execute, options) {
|
|
|
1272
1375
|
try {
|
|
1273
1376
|
results[index] = { index, success: true, data: await execute(items[index]) };
|
|
1274
1377
|
} catch (error) {
|
|
1275
|
-
results[index] =
|
|
1378
|
+
results[index] = batchItemFailure(
|
|
1276
1379
|
index,
|
|
1277
|
-
|
|
1278
|
-
error
|
|
1279
|
-
|
|
1380
|
+
error instanceof Error ? error.message : String(error),
|
|
1381
|
+
error instanceof SignalizError ? {
|
|
1382
|
+
error_code: error.code,
|
|
1383
|
+
retry_eligible: error.isRetryable,
|
|
1384
|
+
retry_after_seconds: error.retryAfter
|
|
1385
|
+
} : void 0
|
|
1386
|
+
);
|
|
1280
1387
|
}
|
|
1281
1388
|
}
|
|
1282
1389
|
};
|
package/dist/index.mjs
CHANGED
package/dist/mcp-config.js
CHANGED
|
@@ -409,10 +409,17 @@ var Signaliz = class {
|
|
|
409
409
|
}
|
|
410
410
|
async findEmail(params) {
|
|
411
411
|
const data = await this.client.post("api/v1/find-email", findEmailRequestBody(params));
|
|
412
|
-
return normalizeFindEmailResult(data);
|
|
412
|
+
return isCoreProductDryRun(data) ? normalizeCoreProductDryRunResult(data) : normalizeFindEmailResult(data);
|
|
413
413
|
}
|
|
414
414
|
async findEmails(params, options) {
|
|
415
415
|
validateBatchSize(params);
|
|
416
|
+
if (options?.dryRun === true) {
|
|
417
|
+
return this.runCoreProductDryRun(
|
|
418
|
+
"api/v1/find-email",
|
|
419
|
+
params.map(findEmailRequestBody),
|
|
420
|
+
options.idempotencyKey
|
|
421
|
+
);
|
|
422
|
+
}
|
|
416
423
|
const startedAt = Date.now();
|
|
417
424
|
const round = await this.runCoreBatchRound(
|
|
418
425
|
"api/v1/find-email",
|
|
@@ -431,9 +438,12 @@ var Signaliz = class {
|
|
|
431
438
|
const request = compact({
|
|
432
439
|
email: email || void 0,
|
|
433
440
|
verification_run_id: options?.verificationRunId,
|
|
434
|
-
skip_cache: options?.skipCache
|
|
441
|
+
skip_cache: options?.skipCache,
|
|
442
|
+
dry_run: options?.dryRun,
|
|
443
|
+
idempotency_key: options?.idempotencyKey
|
|
435
444
|
});
|
|
436
445
|
let data = await this.client.post("api/v1/verify-email", request);
|
|
446
|
+
if (isCoreProductDryRun(data)) return normalizeCoreProductDryRunResult(data);
|
|
437
447
|
if (options?.waitForResult !== false && isPendingSignalResponse(data) && data.verification_run_id) {
|
|
438
448
|
const maxWaitMs = Math.max(1e3, options?.maxWaitMs ?? 10 * 6e4);
|
|
439
449
|
const startedAt = Date.now();
|
|
@@ -453,6 +463,13 @@ var Signaliz = class {
|
|
|
453
463
|
}
|
|
454
464
|
async verifyEmails(emails, options) {
|
|
455
465
|
validateBatchSize(emails);
|
|
466
|
+
if (options?.dryRun === true) {
|
|
467
|
+
return this.runCoreProductDryRun(
|
|
468
|
+
"api/v1/verify-email",
|
|
469
|
+
emails.map((email) => ({ email, skip_cache: options.skipCache })),
|
|
470
|
+
options.idempotencyKey
|
|
471
|
+
);
|
|
472
|
+
}
|
|
456
473
|
const startedAt = Date.now();
|
|
457
474
|
const round = await this.runCoreBatchRound(
|
|
458
475
|
"api/v1/verify-email",
|
|
@@ -473,6 +490,7 @@ var Signaliz = class {
|
|
|
473
490
|
async enrichCompanySignals(params) {
|
|
474
491
|
const request = companySignalRequestBody(params);
|
|
475
492
|
let data = await this.client.post("api/v1/company-signals", request);
|
|
493
|
+
if (isCoreProductDryRun(data)) return normalizeCoreProductDryRunResult(data);
|
|
476
494
|
if (params.waitForResult !== false && data.run_id && isPendingSignalResponse(data)) {
|
|
477
495
|
const maxWaitMs = Math.max(1e3, params.maxWaitMs ?? 20 * 6e4);
|
|
478
496
|
const startedAt = Date.now();
|
|
@@ -503,6 +521,13 @@ var Signaliz = class {
|
|
|
503
521
|
}
|
|
504
522
|
async enrichCompanies(companies, options) {
|
|
505
523
|
validateBatchSize(companies);
|
|
524
|
+
if (options?.dryRun === true) {
|
|
525
|
+
return this.runCoreProductDryRun(
|
|
526
|
+
"api/v1/company-signals",
|
|
527
|
+
companies.map(companySignalRequestBody),
|
|
528
|
+
options.idempotencyKey
|
|
529
|
+
);
|
|
530
|
+
}
|
|
506
531
|
const startedAt = Date.now();
|
|
507
532
|
const results = new Array(companies.length);
|
|
508
533
|
const initialTasks = companies.map((params, index) => ({
|
|
@@ -527,7 +552,11 @@ var Signaliz = class {
|
|
|
527
552
|
const params = companies[item.index];
|
|
528
553
|
const task = taskByIndex.get(item.index);
|
|
529
554
|
if (!item.success || !item.raw) {
|
|
530
|
-
results[item.index] =
|
|
555
|
+
results[item.index] = batchItemFailure(
|
|
556
|
+
item.index,
|
|
557
|
+
item.error || "Company Signal batch request failed",
|
|
558
|
+
item.raw ?? item
|
|
559
|
+
);
|
|
531
560
|
continue;
|
|
532
561
|
}
|
|
533
562
|
if (!isPendingSignalResponse(item.raw) || params.waitForResult === false) {
|
|
@@ -576,6 +605,7 @@ var Signaliz = class {
|
|
|
576
605
|
validateSignalToCopyParams(params);
|
|
577
606
|
const request = signalToCopyRequestBody(params);
|
|
578
607
|
let data = await this.client.post("api/v1/signal-to-copy", request);
|
|
608
|
+
if (isCoreProductDryRun(data)) return normalizeCoreProductDryRunResult(data);
|
|
579
609
|
if (params.waitForResult !== false && (data.signal_run_id || data.run_id) && isPendingSignalResponse(data)) {
|
|
580
610
|
const maxWaitMs = Math.max(1e3, params.maxWaitMs ?? 20 * 6e4);
|
|
581
611
|
const startedAt = Date.now();
|
|
@@ -594,6 +624,13 @@ var Signaliz = class {
|
|
|
594
624
|
async createSignalCopyBatch(requests, options) {
|
|
595
625
|
validateBatchSize(requests);
|
|
596
626
|
requests.forEach(validateSignalToCopyParams);
|
|
627
|
+
if (options?.dryRun === true) {
|
|
628
|
+
return this.runCoreProductDryRun(
|
|
629
|
+
"api/v1/signal-to-copy",
|
|
630
|
+
requests.map(signalToCopyRequestBody),
|
|
631
|
+
options.idempotencyKey
|
|
632
|
+
);
|
|
633
|
+
}
|
|
597
634
|
const startedAt = Date.now();
|
|
598
635
|
const deduplicated = dedupeExactBatchItems(requests, (params) => JSON.stringify({
|
|
599
636
|
request: signalToCopyRequestBody(params),
|
|
@@ -620,6 +657,17 @@ var Signaliz = class {
|
|
|
620
657
|
results: compactedResults
|
|
621
658
|
};
|
|
622
659
|
}
|
|
660
|
+
async runCoreProductDryRun(path2, requests, idempotencyKey) {
|
|
661
|
+
const data = await this.client.post(path2, compact({
|
|
662
|
+
requests,
|
|
663
|
+
dry_run: true,
|
|
664
|
+
idempotency_key: idempotencyKey
|
|
665
|
+
}));
|
|
666
|
+
if (!isCoreProductDryRun(data)) {
|
|
667
|
+
throw new Error(`${path2} dry run returned a live-result contract`);
|
|
668
|
+
}
|
|
669
|
+
return normalizeCoreProductDryRunResult(data);
|
|
670
|
+
}
|
|
623
671
|
async runSignalCopyBatchChunks(uniqueRequests, options) {
|
|
624
672
|
const chunks = [];
|
|
625
673
|
for (let offset = 0; offset < uniqueRequests.length; offset += 25) {
|
|
@@ -636,11 +684,11 @@ var Signaliz = class {
|
|
|
636
684
|
const chunk = chunks[chunkResult.index];
|
|
637
685
|
if (!chunkResult.success || !chunkResult.data) {
|
|
638
686
|
for (let index = 0; index < chunk.requests.length; index += 1) {
|
|
639
|
-
uniqueResults[chunk.offset + index] =
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
687
|
+
uniqueResults[chunk.offset + index] = batchItemFailure(
|
|
688
|
+
chunk.offset + index,
|
|
689
|
+
chunkResult.error || "Signal to Copy batch request failed",
|
|
690
|
+
chunkResult
|
|
691
|
+
);
|
|
644
692
|
}
|
|
645
693
|
continue;
|
|
646
694
|
}
|
|
@@ -662,7 +710,7 @@ var Signaliz = class {
|
|
|
662
710
|
const results = new Array(requests.length);
|
|
663
711
|
for (const row of rows) {
|
|
664
712
|
const index = Number(row.index);
|
|
665
|
-
results[index] = recoverableJobRowFailed(row) ?
|
|
713
|
+
results[index] = recoverableJobRowFailed(row) ? batchItemFailure(index, row.error || row.message || "Signal to Copy failed", row) : { index, success: true, data: normalizeSignalToCopyResult(row) };
|
|
666
714
|
}
|
|
667
715
|
return results;
|
|
668
716
|
}
|
|
@@ -767,7 +815,11 @@ var Signaliz = class {
|
|
|
767
815
|
rateLimited.push({ index: task.index, params: task.params, raw: item });
|
|
768
816
|
continue;
|
|
769
817
|
}
|
|
770
|
-
results[task.index] =
|
|
818
|
+
results[task.index] = batchItemFailure(
|
|
819
|
+
task.index,
|
|
820
|
+
item.error || item.message || "Signal to Copy failed",
|
|
821
|
+
item
|
|
822
|
+
);
|
|
771
823
|
continue;
|
|
772
824
|
}
|
|
773
825
|
if (!isPendingSignalResponse(item)) {
|
|
@@ -872,7 +924,10 @@ var Signaliz = class {
|
|
|
872
924
|
uniqueResults[chunk.offset + index] = {
|
|
873
925
|
index: chunk.tasks[index].index,
|
|
874
926
|
success: false,
|
|
875
|
-
error: chunkResult.error || `${path2} batch request failed
|
|
927
|
+
error: chunkResult.error || `${path2} batch request failed`,
|
|
928
|
+
errorCode: chunkResult.errorCode,
|
|
929
|
+
retryEligible: chunkResult.retryEligible,
|
|
930
|
+
retryAfterSeconds: chunkResult.retryAfterSeconds
|
|
876
931
|
};
|
|
877
932
|
}
|
|
878
933
|
continue;
|
|
@@ -938,6 +993,21 @@ function rowRateLimitDelayMs(items) {
|
|
|
938
993
|
});
|
|
939
994
|
return Math.max(1, Math.min(6e4, Math.max(...delays)));
|
|
940
995
|
}
|
|
996
|
+
function batchItemFailure(index, error, raw) {
|
|
997
|
+
const errorCode = String(raw?.error_code ?? raw?.code ?? raw?.errorCode ?? "").trim();
|
|
998
|
+
const retryAfterSeconds2 = Number(
|
|
999
|
+
raw?.retry_after_seconds ?? raw?.retry_after ?? raw?.retryAfterSeconds ?? (Number.isFinite(Number(raw?.retry_after_ms)) ? Number(raw?.retry_after_ms) / 1e3 : void 0)
|
|
1000
|
+
);
|
|
1001
|
+
const retryEligible = raw?.retry_eligible ?? raw?.retryEligible;
|
|
1002
|
+
return {
|
|
1003
|
+
index,
|
|
1004
|
+
success: false,
|
|
1005
|
+
error,
|
|
1006
|
+
...errorCode ? { errorCode } : {},
|
|
1007
|
+
...typeof retryEligible === "boolean" ? { retryEligible } : {},
|
|
1008
|
+
...Number.isFinite(retryAfterSeconds2) && retryAfterSeconds2 >= 0 ? { retryAfterSeconds: retryAfterSeconds2 } : {}
|
|
1009
|
+
};
|
|
1010
|
+
}
|
|
941
1011
|
function recoverableJobRowFailed(row) {
|
|
942
1012
|
return row.success === false || ["failed", "skipped"].includes(String(row._status || "").toLowerCase());
|
|
943
1013
|
}
|
|
@@ -956,9 +1026,34 @@ function findEmailRequestBody(params) {
|
|
|
956
1026
|
last_name: params.lastName,
|
|
957
1027
|
linkedin_url: params.linkedinUrl,
|
|
958
1028
|
company_name: params.companyName,
|
|
959
|
-
skip_cache: params.skipCache
|
|
1029
|
+
skip_cache: params.skipCache,
|
|
1030
|
+
dry_run: params.dryRun,
|
|
1031
|
+
idempotency_key: params.idempotencyKey
|
|
960
1032
|
});
|
|
961
1033
|
}
|
|
1034
|
+
function isCoreProductDryRun(data) {
|
|
1035
|
+
return data.dry_run === true && data.status === "planned";
|
|
1036
|
+
}
|
|
1037
|
+
function normalizeCoreProductDryRunResult(data) {
|
|
1038
|
+
const estimatedCredits = data.estimated_credits && typeof data.estimated_credits === "object" ? data.estimated_credits : { min: 0, max: 0 };
|
|
1039
|
+
return {
|
|
1040
|
+
success: true,
|
|
1041
|
+
dryRun: true,
|
|
1042
|
+
status: "planned",
|
|
1043
|
+
capability: String(data.capability || ""),
|
|
1044
|
+
inputCount: Number(data.input_count || 0),
|
|
1045
|
+
recoveryReadCount: Number(data.recovery_read_count || 0),
|
|
1046
|
+
plan: data.plan && typeof data.plan === "object" ? data.plan : {},
|
|
1047
|
+
estimate: data.estimate && typeof data.estimate === "object" ? data.estimate : {},
|
|
1048
|
+
estimatedCredits: {
|
|
1049
|
+
min: Number(estimatedCredits.min || 0),
|
|
1050
|
+
max: Number(estimatedCredits.max || 0)
|
|
1051
|
+
},
|
|
1052
|
+
creditsCharged: 0,
|
|
1053
|
+
sideEffects: Array.isArray(data.side_effects) ? data.side_effects : [],
|
|
1054
|
+
raw: data
|
|
1055
|
+
};
|
|
1056
|
+
}
|
|
962
1057
|
function normalizeFindEmailResult(data) {
|
|
963
1058
|
const email = typeof data.email === "string" && data.email.trim() ? data.email.trim() : null;
|
|
964
1059
|
const found = Boolean(email) && data.found !== false;
|
|
@@ -1027,7 +1122,9 @@ function companySignalRequestBody(params) {
|
|
|
1027
1122
|
// REST always returns a resumable run instead of holding the connection;
|
|
1028
1123
|
// waitForResult controls the SDK polling loop.
|
|
1029
1124
|
wait_for_result: false,
|
|
1030
|
-
max_wait_ms: params.maxWaitMs
|
|
1125
|
+
max_wait_ms: params.maxWaitMs,
|
|
1126
|
+
dry_run: params.dryRun,
|
|
1127
|
+
idempotency_key: params.idempotencyKey
|
|
1031
1128
|
});
|
|
1032
1129
|
}
|
|
1033
1130
|
function companySignalRecoverableBatchOptions(companies, tasks) {
|
|
@@ -1148,7 +1245,11 @@ function finalizeCoreBatch(total, startedAt, round, normalize, options) {
|
|
|
1148
1245
|
const results = new Array(total);
|
|
1149
1246
|
for (const item of round) {
|
|
1150
1247
|
if (!item.success || !item.raw) {
|
|
1151
|
-
results[item.index] =
|
|
1248
|
+
results[item.index] = batchItemFailure(
|
|
1249
|
+
item.index,
|
|
1250
|
+
item.error || "Signaliz batch item failed",
|
|
1251
|
+
item.raw ?? item
|
|
1252
|
+
);
|
|
1152
1253
|
continue;
|
|
1153
1254
|
}
|
|
1154
1255
|
try {
|
|
@@ -1195,7 +1296,9 @@ function signalToCopyRequestBody(params) {
|
|
|
1195
1296
|
research_prompt: params.researchPrompt,
|
|
1196
1297
|
signal_run_id: params.signalRunId,
|
|
1197
1298
|
skip_cache: params.skipCache,
|
|
1198
|
-
enable_deep_search: params.enableDeepSearch
|
|
1299
|
+
enable_deep_search: params.enableDeepSearch,
|
|
1300
|
+
dry_run: params.dryRun,
|
|
1301
|
+
idempotency_key: params.idempotencyKey
|
|
1199
1302
|
});
|
|
1200
1303
|
}
|
|
1201
1304
|
function validateSignalToCopyParams(params) {
|
|
@@ -1276,11 +1379,15 @@ async function runBatch(items, execute, options) {
|
|
|
1276
1379
|
try {
|
|
1277
1380
|
results[index] = { index, success: true, data: await execute(items[index]) };
|
|
1278
1381
|
} catch (error) {
|
|
1279
|
-
results[index] =
|
|
1382
|
+
results[index] = batchItemFailure(
|
|
1280
1383
|
index,
|
|
1281
|
-
|
|
1282
|
-
error
|
|
1283
|
-
|
|
1384
|
+
error instanceof Error ? error.message : String(error),
|
|
1385
|
+
error instanceof SignalizError ? {
|
|
1386
|
+
error_code: error.code,
|
|
1387
|
+
retry_eligible: error.isRetryable,
|
|
1388
|
+
retry_after_seconds: error.retryAfter
|
|
1389
|
+
} : void 0
|
|
1390
|
+
);
|
|
1284
1391
|
}
|
|
1285
1392
|
}
|
|
1286
1393
|
};
|
package/dist/mcp-config.mjs
CHANGED
package/package.json
CHANGED