@signaliz/sdk 1.0.55 → 1.0.57
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 +7 -2
- package/dist/{chunk-QXMVHZIQ.mjs → chunk-SZ3H5W5L.mjs} +139 -17
- package/dist/index.d.mts +34 -2
- package/dist/index.d.ts +34 -2
- package/dist/index.js +139 -17
- package/dist/index.mjs +1 -1
- package/dist/mcp-config.js +139 -17
- package/dist/mcp-config.mjs +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -29,6 +29,8 @@ const plan = await signaliz.findEmail({
|
|
|
29
29
|
dryRun: true,
|
|
30
30
|
});
|
|
31
31
|
console.log(plan.estimatedCredits.max, plan.creditsCharged); // 2, 0
|
|
32
|
+
// Batch plans price only semantic unique work and expose duplicate suppression.
|
|
33
|
+
console.log(plan.inputCount, plan.uniqueFreshInputCount, plan.inputDuplicatesSuppressed);
|
|
32
34
|
|
|
33
35
|
const found = await signaliz.findEmail({
|
|
34
36
|
companyDomain: 'example.com',
|
|
@@ -111,8 +113,11 @@ when the REST API supplies them, including after automatic row retries are
|
|
|
111
113
|
exhausted.
|
|
112
114
|
|
|
113
115
|
For an ambiguous single request or batch submission failure, retry with the
|
|
114
|
-
same `idempotencyKey` to recover the original request
|
|
115
|
-
work.
|
|
116
|
+
same `idempotencyKey` to recover the original request, durable job, or provider
|
|
117
|
+
run without duplicate work or spend. The key is portable across REST API, MCP,
|
|
118
|
+
SDK, and CLI calls for the same workspace and logical input. Chunked batches
|
|
119
|
+
derive a stable key from each row's original input index, including after exact
|
|
120
|
+
duplicate compaction and rate-limit retries.
|
|
116
121
|
|
|
117
122
|
Each batch accepts up to 5,000 items. For the four row-oriented core products, batches larger
|
|
118
123
|
than 25 use one recoverable REST job; the SDK waits for completion and retrieves
|
|
@@ -532,11 +532,16 @@ var Signaliz = class {
|
|
|
532
532
|
);
|
|
533
533
|
}
|
|
534
534
|
const startedAt = Date.now();
|
|
535
|
-
const deduplicated = dedupeExactBatchItems(
|
|
536
|
-
|
|
537
|
-
|
|
535
|
+
const deduplicated = dedupeExactBatchItems(
|
|
536
|
+
requests,
|
|
537
|
+
(params) => coreProductBatchRequestKey("api/v1/signal-to-copy", signalToCopyRequestBody(params))
|
|
538
|
+
);
|
|
538
539
|
const uniqueRequests = deduplicated.uniqueItems;
|
|
539
|
-
const
|
|
540
|
+
const uniqueInputIndices = new Array(uniqueRequests.length);
|
|
541
|
+
deduplicated.sourceIndexByInput.forEach((uniqueIndex, inputIndex) => {
|
|
542
|
+
if (uniqueInputIndices[uniqueIndex] === void 0) uniqueInputIndices[uniqueIndex] = inputIndex;
|
|
543
|
+
});
|
|
544
|
+
const uniqueResults = await this.runSignalCopyBatchChunks(uniqueRequests, options, uniqueInputIndices);
|
|
540
545
|
const results = requests.map((_, index) => ({
|
|
541
546
|
...uniqueResults[deduplicated.sourceIndexByInput[index]],
|
|
542
547
|
index
|
|
@@ -562,15 +567,25 @@ var Signaliz = class {
|
|
|
562
567
|
}
|
|
563
568
|
return normalizeCoreProductDryRunResult(data);
|
|
564
569
|
}
|
|
565
|
-
async runSignalCopyBatchChunks(uniqueRequests, options) {
|
|
570
|
+
async runSignalCopyBatchChunks(uniqueRequests, options, idempotencyItemIndices = uniqueRequests.map((_, index) => index)) {
|
|
566
571
|
const chunks = [];
|
|
567
572
|
for (let offset = 0; offset < uniqueRequests.length; offset += 25) {
|
|
568
|
-
chunks.push({
|
|
573
|
+
chunks.push({
|
|
574
|
+
offset,
|
|
575
|
+
requests: uniqueRequests.slice(offset, offset + 25),
|
|
576
|
+
idempotencyItemIndices: idempotencyItemIndices.slice(offset, offset + 25)
|
|
577
|
+
});
|
|
569
578
|
}
|
|
570
579
|
const { serverConcurrency, chunkConcurrency } = batchConcurrencyPlan(options, 25, 25);
|
|
571
580
|
const chunkBatch = await runBatch(
|
|
572
581
|
chunks,
|
|
573
|
-
(chunk) => this.runSignalCopyBatchChunk(
|
|
582
|
+
(chunk) => this.runSignalCopyBatchChunk(
|
|
583
|
+
chunk.requests,
|
|
584
|
+
serverConcurrency,
|
|
585
|
+
0,
|
|
586
|
+
options?.idempotencyKey,
|
|
587
|
+
chunk.idempotencyItemIndices
|
|
588
|
+
),
|
|
574
589
|
{ concurrency: chunkConcurrency }
|
|
575
590
|
);
|
|
576
591
|
const uniqueResults = new Array(uniqueRequests.length);
|
|
@@ -592,14 +607,15 @@ var Signaliz = class {
|
|
|
592
607
|
}
|
|
593
608
|
return uniqueResults;
|
|
594
609
|
}
|
|
595
|
-
async runRecoverableCoreBatchJob(path, requests, label, pageSize = 500, maxWaitMs = 20 * 6e4, idempotencyKey) {
|
|
610
|
+
async runRecoverableCoreBatchJob(path, requests, label, pageSize = 500, maxWaitMs = 20 * 6e4, idempotencyKey, idempotencyItemIndices) {
|
|
596
611
|
const startedAt = Date.now();
|
|
597
612
|
const submissionIdempotencyKey = idempotencyKey?.trim() || newBatchIdempotencyKey(label);
|
|
598
613
|
let submission;
|
|
599
614
|
try {
|
|
600
615
|
submission = await this.client.post(path, {
|
|
601
616
|
requests,
|
|
602
|
-
idempotency_key: submissionIdempotencyKey
|
|
617
|
+
idempotency_key: submissionIdempotencyKey,
|
|
618
|
+
batch_item_indices: idempotencyItemIndices
|
|
603
619
|
});
|
|
604
620
|
} catch (error) {
|
|
605
621
|
if (error instanceof SignalizError && !error.isRetryable) throw error;
|
|
@@ -662,12 +678,14 @@ var Signaliz = class {
|
|
|
662
678
|
if (seen.size !== requests.length) throw new Error(`${label} batch returned incomplete indexed results`);
|
|
663
679
|
return rows;
|
|
664
680
|
}
|
|
665
|
-
async runSignalCopyBatchChunk(requests, concurrency, rowRateLimitAttempt = 0) {
|
|
681
|
+
async runSignalCopyBatchChunk(requests, concurrency, rowRateLimitAttempt = 0, idempotencyKey, idempotencyItemIndices = requests.map((_, index) => index)) {
|
|
666
682
|
const results = new Array(requests.length);
|
|
667
683
|
const rateLimited = [];
|
|
668
684
|
const data = await this.client.post("api/v1/signal-to-copy", {
|
|
669
685
|
requests: requests.map(signalToCopyRequestBody),
|
|
670
|
-
concurrency
|
|
686
|
+
concurrency,
|
|
687
|
+
idempotency_key: idempotencyKey,
|
|
688
|
+
batch_item_indices: idempotencyItemIndices
|
|
671
689
|
});
|
|
672
690
|
if (!Array.isArray(data.results) || data.results.length !== requests.length) {
|
|
673
691
|
throw new Error("Signal to Copy batch returned an invalid result count");
|
|
@@ -702,7 +720,9 @@ var Signaliz = class {
|
|
|
702
720
|
const retried = await this.runSignalCopyBatchChunk(
|
|
703
721
|
rateLimited.map((item) => item.params),
|
|
704
722
|
concurrency,
|
|
705
|
-
rowRateLimitAttempt + 1
|
|
723
|
+
rowRateLimitAttempt + 1,
|
|
724
|
+
idempotencyKey,
|
|
725
|
+
rateLimited.map((item) => idempotencyItemIndices[item.index])
|
|
706
726
|
);
|
|
707
727
|
for (let index = 0; index < rateLimited.length; index += 1) {
|
|
708
728
|
results[rateLimited[index].index] = { ...retried[index], index: rateLimited[index].index };
|
|
@@ -712,7 +732,10 @@ var Signaliz = class {
|
|
|
712
732
|
}
|
|
713
733
|
async runCoreBatchRound(path, tasks, options, rowRateLimitAttempt = 0) {
|
|
714
734
|
const { serverConcurrency, chunkConcurrency } = path === "api/v1/company-signals" ? batchConcurrencyPlan(options, 25, 25) : batchConcurrencyPlan(options);
|
|
715
|
-
const deduplicated = dedupeExactBatchItems(
|
|
735
|
+
const deduplicated = dedupeExactBatchItems(
|
|
736
|
+
tasks,
|
|
737
|
+
(task) => coreProductBatchRequestKey(path, task.request)
|
|
738
|
+
);
|
|
716
739
|
const uniqueTasks = deduplicated.uniqueItems;
|
|
717
740
|
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 } : void 0;
|
|
718
741
|
if (path !== "api/v1/company-signals" && recoverableBatch && uniqueTasks.length > 25) {
|
|
@@ -722,7 +745,8 @@ var Signaliz = class {
|
|
|
722
745
|
recoverableBatch.label,
|
|
723
746
|
recoverableBatch.pageSize,
|
|
724
747
|
recoverableBatch.maxWaitMs,
|
|
725
|
-
options?.idempotencyKey
|
|
748
|
+
options?.idempotencyKey,
|
|
749
|
+
uniqueTasks.map((task) => task.index)
|
|
726
750
|
);
|
|
727
751
|
const uniqueResults2 = new Array(uniqueTasks.length);
|
|
728
752
|
for (const raw of rows) {
|
|
@@ -746,7 +770,9 @@ var Signaliz = class {
|
|
|
746
770
|
const chunkBatch = await runBatch(chunks, async (chunk) => {
|
|
747
771
|
const data = await this.client.post(path, {
|
|
748
772
|
requests: chunk.tasks.map((task) => task.request),
|
|
749
|
-
concurrency: serverConcurrency
|
|
773
|
+
concurrency: serverConcurrency,
|
|
774
|
+
idempotency_key: options?.idempotencyKey,
|
|
775
|
+
batch_item_indices: chunk.tasks.map((task) => task.index)
|
|
750
776
|
});
|
|
751
777
|
if (!Array.isArray(data.results) || data.results.length !== chunk.tasks.length) {
|
|
752
778
|
throw new Error(`${path} batch returned an invalid result count`);
|
|
@@ -881,7 +907,11 @@ function normalizeCoreProductDryRunResult(data) {
|
|
|
881
907
|
status: "planned",
|
|
882
908
|
capability: String(data.capability || ""),
|
|
883
909
|
inputCount: Number(data.input_count || 0),
|
|
910
|
+
freshInputCount: Number(data.fresh_input_count ?? data.input_count ?? 0),
|
|
911
|
+
uniqueFreshInputCount: Number(data.unique_fresh_input_count ?? data.fresh_input_count ?? data.input_count ?? 0),
|
|
912
|
+
inputDuplicatesSuppressed: Number(data.input_duplicates_suppressed || 0),
|
|
884
913
|
recoveryReadCount: Number(data.recovery_read_count || 0),
|
|
914
|
+
uniqueRecoveryReadCount: Number(data.unique_recovery_read_count ?? data.recovery_read_count ?? 0),
|
|
885
915
|
plan: data.plan && typeof data.plan === "object" ? data.plan : {},
|
|
886
916
|
estimate: data.estimate && typeof data.estimate === "object" ? data.estimate : {},
|
|
887
917
|
estimatedCredits: {
|
|
@@ -896,12 +926,12 @@ function normalizeCoreProductDryRunResult(data) {
|
|
|
896
926
|
function normalizeFindEmailResult(data) {
|
|
897
927
|
const email = typeof data.email === "string" && data.email.trim() ? data.email.trim() : null;
|
|
898
928
|
const found = Boolean(email) && data.found !== false;
|
|
899
|
-
const verificationStatus = data.verification_status ?? data.status ?? "unknown";
|
|
929
|
+
const verificationStatus = data.verification_status ?? (data.is_valid === true ? "valid" : data.status ?? "unknown");
|
|
900
930
|
const rawFreshness = String(data.verification_freshness ?? "").toLowerCase();
|
|
901
931
|
const verificationFreshness = rawFreshness === "fresh" || rawFreshness === "stale" ? rawFreshness : "unknown";
|
|
902
932
|
const needsReverification = data.needs_reverification === true || verificationFreshness === "stale";
|
|
903
933
|
const explicitlyNotSendSafe = data.verified_for_sending === false || needsReverification;
|
|
904
|
-
const verified = !explicitlyNotSendSafe && (data.email_verified === true || data.verified_for_sending === true || ["valid", "deliverable"].includes(String(verificationStatus).toLowerCase()));
|
|
934
|
+
const verified = !explicitlyNotSendSafe && (data.is_valid === true || data.isValid === true || data.email_verified === true || data.verified_for_sending === true || ["valid", "deliverable"].includes(String(verificationStatus).toLowerCase()));
|
|
905
935
|
const verificationAgeDays = typeof data.verification_age_days === "number" && Number.isFinite(data.verification_age_days) ? data.verification_age_days : null;
|
|
906
936
|
return {
|
|
907
937
|
success: data.success !== false && found,
|
|
@@ -924,6 +954,9 @@ function normalizeFindEmailResult(data) {
|
|
|
924
954
|
billingMetadata: data.billing_metadata,
|
|
925
955
|
resultReturnedFrom: data.result_returned_from,
|
|
926
956
|
cacheHit: data.cache_hit,
|
|
957
|
+
negativeCacheHit: data.negative_cache_hit,
|
|
958
|
+
negativeCacheTtlSeconds: data.negative_cache_ttl_seconds,
|
|
959
|
+
cacheTier: data.cache_tier,
|
|
927
960
|
freshEnrichmentUsed: data.fresh_enrichment_used,
|
|
928
961
|
liveProviderCalled: data.live_provider_called,
|
|
929
962
|
openrouterCalled: data.openrouter_called,
|
|
@@ -945,6 +978,7 @@ function normalizeVerifyEmailResult(email, data) {
|
|
|
945
978
|
nextPollAfterSeconds: data.next_poll_after_seconds,
|
|
946
979
|
isValid: data.is_valid ?? data.valid ?? verified,
|
|
947
980
|
isDeliverable: data.is_deliverable ?? data.deliverable ?? verified,
|
|
981
|
+
isMalformed: data.is_malformed === true || data.verification_verdict === "malformed" || data.verification_status === "malformed" || data.deliverability_status === "malformed",
|
|
948
982
|
isCatchAll: data.email_is_catch_all ?? data.is_catch_all ?? data.catch_all ?? data.diagnostics?.is_catch_all ?? false,
|
|
949
983
|
verifiedForSending: data.verified_for_sending === true,
|
|
950
984
|
verificationVerdict: data.verification_verdict ?? data.verification_status ?? "unknown",
|
|
@@ -956,6 +990,9 @@ function normalizeVerifyEmailResult(email, data) {
|
|
|
956
990
|
billingMetadata: data.billing_metadata,
|
|
957
991
|
resultReturnedFrom: data.result_returned_from,
|
|
958
992
|
cacheHit: data.cache_hit,
|
|
993
|
+
negativeCacheHit: data.negative_cache_hit,
|
|
994
|
+
negativeCacheTtlSeconds: data.negative_cache_ttl_seconds,
|
|
995
|
+
cacheTier: data.cache_tier,
|
|
959
996
|
freshEnrichmentUsed: data.fresh_enrichment_used,
|
|
960
997
|
liveProviderCalled: data.live_provider_called,
|
|
961
998
|
openrouterCalled: data.openrouter_called,
|
|
@@ -1025,6 +1062,14 @@ function normalizeCompanySignalResult(params, data) {
|
|
|
1025
1062
|
evidenceCountTotal: data.evidence_count_total,
|
|
1026
1063
|
evidenceSourcesOmitted: data.evidence_sources_omitted,
|
|
1027
1064
|
evidenceScope: data.evidence_scope,
|
|
1065
|
+
billingMetadata: data.billing_metadata,
|
|
1066
|
+
resultReturnedFrom: data.result_returned_from,
|
|
1067
|
+
cacheHit: data.cache_hit,
|
|
1068
|
+
freshEnrichmentUsed: data.fresh_enrichment_used,
|
|
1069
|
+
creditsUsed: data.credits_used,
|
|
1070
|
+
liveProviderCalled: data.live_provider_called,
|
|
1071
|
+
aiProviderCalled: data.ai_provider_called,
|
|
1072
|
+
byoAiUsed: data.byo_ai_used,
|
|
1028
1073
|
raw: data
|
|
1029
1074
|
};
|
|
1030
1075
|
}
|
|
@@ -1206,6 +1251,75 @@ function dedupeExactBatchItems(items, keyForItem) {
|
|
|
1206
1251
|
}
|
|
1207
1252
|
return { uniqueItems, sourceIndexByInput };
|
|
1208
1253
|
}
|
|
1254
|
+
function normalizedBatchText(value) {
|
|
1255
|
+
return typeof value === "string" ? value.trim().toLowerCase().replace(/\s+/g, " ") : "";
|
|
1256
|
+
}
|
|
1257
|
+
function normalizedBatchDomain(value) {
|
|
1258
|
+
return normalizedBatchText(value).replace(/^https?:\/\//, "").replace(/^www\./, "").split(/[/?#]/, 1)[0].replace(/\.+$/, "");
|
|
1259
|
+
}
|
|
1260
|
+
function normalizedBatchLinkedIn(value) {
|
|
1261
|
+
return normalizedBatchText(value).replace(/\/+$/, "");
|
|
1262
|
+
}
|
|
1263
|
+
function normalizedBatchList(value) {
|
|
1264
|
+
return Array.isArray(value) ? value.map(normalizedBatchText).filter(Boolean).sort() : [];
|
|
1265
|
+
}
|
|
1266
|
+
function normalizedFindEmailBatchName(request) {
|
|
1267
|
+
const fullName = normalizedBatchText(request.full_name);
|
|
1268
|
+
const componentName = [normalizedBatchText(request.first_name), normalizedBatchText(request.last_name)].filter(Boolean).join(" ");
|
|
1269
|
+
if (!fullName || !componentName || fullName === componentName) return fullName || componentName;
|
|
1270
|
+
return JSON.stringify([fullName, componentName]);
|
|
1271
|
+
}
|
|
1272
|
+
function coreProductBatchRequestKey(path, request) {
|
|
1273
|
+
if (path === "api/v1/find-email") {
|
|
1274
|
+
return JSON.stringify([
|
|
1275
|
+
normalizedFindEmailBatchName(request),
|
|
1276
|
+
normalizedBatchDomain(request.company_domain),
|
|
1277
|
+
normalizedBatchLinkedIn(request.linkedin_url),
|
|
1278
|
+
normalizedBatchText(request.company_name),
|
|
1279
|
+
request.skip_cache === true || request.bypass_cache === true
|
|
1280
|
+
]);
|
|
1281
|
+
}
|
|
1282
|
+
if (path === "api/v1/verify-email") {
|
|
1283
|
+
return JSON.stringify([
|
|
1284
|
+
normalizedBatchText(request.email),
|
|
1285
|
+
request.skip_cache === true || request.bypass_cache === true,
|
|
1286
|
+
request.skip_catch_all_verification === true,
|
|
1287
|
+
request.skip_esp_check === true
|
|
1288
|
+
]);
|
|
1289
|
+
}
|
|
1290
|
+
if (path === "api/v1/company-signals") {
|
|
1291
|
+
const online = request.online !== false;
|
|
1292
|
+
const deepSearch = request.enable_deep_search === void 0 ? online : request.enable_deep_search !== false;
|
|
1293
|
+
return JSON.stringify([
|
|
1294
|
+
normalizedBatchText(request.signal_run_id),
|
|
1295
|
+
normalizedBatchDomain(request.company_domain || request.domain),
|
|
1296
|
+
normalizedBatchText(request.company_name),
|
|
1297
|
+
normalizedBatchText(request.company_id),
|
|
1298
|
+
normalizedBatchLinkedIn(request.linkedin_url),
|
|
1299
|
+
normalizedBatchText(request.industry),
|
|
1300
|
+
typeof request.research_prompt === "string" ? request.research_prompt.trim() : "",
|
|
1301
|
+
normalizedBatchList(request.signal_types),
|
|
1302
|
+
Number(request.target_signal_count) || null,
|
|
1303
|
+
Number(request.lookback_days) || null,
|
|
1304
|
+
online,
|
|
1305
|
+
deepSearch,
|
|
1306
|
+
normalizedBatchText(request.search_mode || "balanced"),
|
|
1307
|
+
request.enable_outreach_intelligence === true,
|
|
1308
|
+
request.enable_predictive_intelligence === true,
|
|
1309
|
+
request.skip_cache === true || request.bypass_cache === true
|
|
1310
|
+
]);
|
|
1311
|
+
}
|
|
1312
|
+
return JSON.stringify([
|
|
1313
|
+
normalizedBatchDomain(request.company_domain),
|
|
1314
|
+
typeof request.person_name === "string" ? request.person_name.trim() : "",
|
|
1315
|
+
typeof request.title === "string" ? request.title.trim() : "",
|
|
1316
|
+
typeof request.campaign_offer === "string" ? request.campaign_offer.trim() : "",
|
|
1317
|
+
typeof request.research_prompt === "string" ? request.research_prompt.trim() : "",
|
|
1318
|
+
typeof request.signal_run_id === "string" ? request.signal_run_id.trim() : "",
|
|
1319
|
+
request.skip_cache === true,
|
|
1320
|
+
request.enable_deep_search === true
|
|
1321
|
+
]);
|
|
1322
|
+
}
|
|
1209
1323
|
function finalizeCoreBatch(total, startedAt, round, normalize, options) {
|
|
1210
1324
|
const results = new Array(total);
|
|
1211
1325
|
for (const item of round) {
|
|
@@ -1302,6 +1416,14 @@ function normalizeSignalToCopyResult(data) {
|
|
|
1302
1416
|
empty: data.empty,
|
|
1303
1417
|
emptyReason: data.empty_reason,
|
|
1304
1418
|
researchMetadata: data.research_metadata,
|
|
1419
|
+
billingMetadata: data.billing_metadata,
|
|
1420
|
+
resultReturnedFrom: data.result_returned_from,
|
|
1421
|
+
cacheHit: data.cache_hit,
|
|
1422
|
+
freshEnrichmentUsed: data.fresh_enrichment_used,
|
|
1423
|
+
creditsUsed: data.credits_used,
|
|
1424
|
+
liveProviderCalled: data.live_provider_called,
|
|
1425
|
+
aiProviderCalled: data.ai_provider_called,
|
|
1426
|
+
byoAiUsed: data.byo_ai_used,
|
|
1305
1427
|
variations: (data.variations ?? []).map((variation) => ({
|
|
1306
1428
|
style: variation.style,
|
|
1307
1429
|
subjectLine: variation.subject_line ?? "",
|
package/dist/index.d.mts
CHANGED
|
@@ -9,7 +9,7 @@ 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
|
|
12
|
+
/** Stable cross-surface key. Retries reuse the same provider work; chunked batches derive one collision-free key per original row. */
|
|
13
13
|
idempotencyKey?: string;
|
|
14
14
|
/** Return exact repeated successes as duplicateOf references. Defaults to false. */
|
|
15
15
|
compactDuplicates?: boolean;
|
|
@@ -51,7 +51,11 @@ interface CoreProductDryRunResult {
|
|
|
51
51
|
status: 'planned';
|
|
52
52
|
capability: string;
|
|
53
53
|
inputCount: number;
|
|
54
|
+
freshInputCount: number;
|
|
55
|
+
uniqueFreshInputCount: number;
|
|
56
|
+
inputDuplicatesSuppressed: number;
|
|
54
57
|
recoveryReadCount: number;
|
|
58
|
+
uniqueRecoveryReadCount: number;
|
|
55
59
|
plan: Record<string, unknown>;
|
|
56
60
|
estimate: Record<string, unknown>;
|
|
57
61
|
estimatedCredits: {
|
|
@@ -97,6 +101,11 @@ interface FindEmailResult {
|
|
|
97
101
|
billingMetadata?: Record<string, unknown>;
|
|
98
102
|
resultReturnedFrom?: string;
|
|
99
103
|
cacheHit?: boolean;
|
|
104
|
+
/** True when a definitive no-email result came from the short-lived negative cache. */
|
|
105
|
+
negativeCacheHit?: boolean;
|
|
106
|
+
/** Remaining lifetime of the negative cache entry. */
|
|
107
|
+
negativeCacheTtlSeconds?: number;
|
|
108
|
+
cacheTier?: string;
|
|
100
109
|
freshEnrichmentUsed?: boolean;
|
|
101
110
|
liveProviderCalled?: boolean;
|
|
102
111
|
openrouterCalled?: boolean;
|
|
@@ -116,6 +125,8 @@ interface VerifyEmailResult {
|
|
|
116
125
|
nextPollAfterSeconds?: number;
|
|
117
126
|
isValid: boolean;
|
|
118
127
|
isDeliverable: boolean;
|
|
128
|
+
/** True when the address failed syntax validation before any provider call. */
|
|
129
|
+
isMalformed: boolean;
|
|
119
130
|
isCatchAll: boolean;
|
|
120
131
|
/** Explicit send-safety verdict. Missing or false never becomes true by inference. */
|
|
121
132
|
verifiedForSending: boolean;
|
|
@@ -128,6 +139,11 @@ interface VerifyEmailResult {
|
|
|
128
139
|
billingMetadata?: Record<string, unknown>;
|
|
129
140
|
resultReturnedFrom?: string;
|
|
130
141
|
cacheHit?: boolean;
|
|
142
|
+
/** True when a conclusive send-blocking verdict came from the short-lived rejection cache. */
|
|
143
|
+
negativeCacheHit?: boolean;
|
|
144
|
+
/** Remaining lifetime of the rejection cache entry. */
|
|
145
|
+
negativeCacheTtlSeconds?: number;
|
|
146
|
+
cacheTier?: string;
|
|
131
147
|
freshEnrichmentUsed?: boolean;
|
|
132
148
|
liveProviderCalled?: boolean;
|
|
133
149
|
openrouterCalled?: boolean;
|
|
@@ -217,6 +233,14 @@ interface CompanySignalEnrichmentResult {
|
|
|
217
233
|
evidenceCountTotal?: number;
|
|
218
234
|
evidenceSourcesOmitted?: number;
|
|
219
235
|
evidenceScope?: 'selected_signals' | 'all_candidates';
|
|
236
|
+
billingMetadata?: Record<string, unknown>;
|
|
237
|
+
resultReturnedFrom?: string;
|
|
238
|
+
cacheHit?: boolean;
|
|
239
|
+
freshEnrichmentUsed?: boolean;
|
|
240
|
+
creditsUsed?: number;
|
|
241
|
+
liveProviderCalled?: boolean;
|
|
242
|
+
aiProviderCalled?: boolean;
|
|
243
|
+
byoAiUsed?: boolean;
|
|
220
244
|
raw: Record<string, unknown>;
|
|
221
245
|
}
|
|
222
246
|
type SignalDiscoverySource = 'web' | 'news';
|
|
@@ -311,7 +335,7 @@ interface SignalToCopyParams {
|
|
|
311
335
|
signalRunId?: string;
|
|
312
336
|
/** Bypass cached company signals and start fresh research. */
|
|
313
337
|
skipCache?: boolean;
|
|
314
|
-
/**
|
|
338
|
+
/** Use the full deep-research provider portfolio. The default uses bounded credit-free first-party and Bing grounding. */
|
|
315
339
|
enableDeepSearch?: boolean;
|
|
316
340
|
/** @deprecated Signal to Copy is always synchronous; this option is ignored. */
|
|
317
341
|
waitForResult?: boolean;
|
|
@@ -346,6 +370,14 @@ interface SignalToCopyResult {
|
|
|
346
370
|
empty?: boolean;
|
|
347
371
|
emptyReason?: string;
|
|
348
372
|
researchMetadata?: Record<string, unknown>;
|
|
373
|
+
billingMetadata?: Record<string, unknown>;
|
|
374
|
+
resultReturnedFrom?: string;
|
|
375
|
+
cacheHit?: boolean;
|
|
376
|
+
freshEnrichmentUsed?: boolean;
|
|
377
|
+
creditsUsed?: number;
|
|
378
|
+
liveProviderCalled?: boolean;
|
|
379
|
+
aiProviderCalled?: boolean;
|
|
380
|
+
byoAiUsed?: boolean;
|
|
349
381
|
variations: SignalToCopyVariation[];
|
|
350
382
|
raw: Record<string, unknown>;
|
|
351
383
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -9,7 +9,7 @@ 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
|
|
12
|
+
/** Stable cross-surface key. Retries reuse the same provider work; chunked batches derive one collision-free key per original row. */
|
|
13
13
|
idempotencyKey?: string;
|
|
14
14
|
/** Return exact repeated successes as duplicateOf references. Defaults to false. */
|
|
15
15
|
compactDuplicates?: boolean;
|
|
@@ -51,7 +51,11 @@ interface CoreProductDryRunResult {
|
|
|
51
51
|
status: 'planned';
|
|
52
52
|
capability: string;
|
|
53
53
|
inputCount: number;
|
|
54
|
+
freshInputCount: number;
|
|
55
|
+
uniqueFreshInputCount: number;
|
|
56
|
+
inputDuplicatesSuppressed: number;
|
|
54
57
|
recoveryReadCount: number;
|
|
58
|
+
uniqueRecoveryReadCount: number;
|
|
55
59
|
plan: Record<string, unknown>;
|
|
56
60
|
estimate: Record<string, unknown>;
|
|
57
61
|
estimatedCredits: {
|
|
@@ -97,6 +101,11 @@ interface FindEmailResult {
|
|
|
97
101
|
billingMetadata?: Record<string, unknown>;
|
|
98
102
|
resultReturnedFrom?: string;
|
|
99
103
|
cacheHit?: boolean;
|
|
104
|
+
/** True when a definitive no-email result came from the short-lived negative cache. */
|
|
105
|
+
negativeCacheHit?: boolean;
|
|
106
|
+
/** Remaining lifetime of the negative cache entry. */
|
|
107
|
+
negativeCacheTtlSeconds?: number;
|
|
108
|
+
cacheTier?: string;
|
|
100
109
|
freshEnrichmentUsed?: boolean;
|
|
101
110
|
liveProviderCalled?: boolean;
|
|
102
111
|
openrouterCalled?: boolean;
|
|
@@ -116,6 +125,8 @@ interface VerifyEmailResult {
|
|
|
116
125
|
nextPollAfterSeconds?: number;
|
|
117
126
|
isValid: boolean;
|
|
118
127
|
isDeliverable: boolean;
|
|
128
|
+
/** True when the address failed syntax validation before any provider call. */
|
|
129
|
+
isMalformed: boolean;
|
|
119
130
|
isCatchAll: boolean;
|
|
120
131
|
/** Explicit send-safety verdict. Missing or false never becomes true by inference. */
|
|
121
132
|
verifiedForSending: boolean;
|
|
@@ -128,6 +139,11 @@ interface VerifyEmailResult {
|
|
|
128
139
|
billingMetadata?: Record<string, unknown>;
|
|
129
140
|
resultReturnedFrom?: string;
|
|
130
141
|
cacheHit?: boolean;
|
|
142
|
+
/** True when a conclusive send-blocking verdict came from the short-lived rejection cache. */
|
|
143
|
+
negativeCacheHit?: boolean;
|
|
144
|
+
/** Remaining lifetime of the rejection cache entry. */
|
|
145
|
+
negativeCacheTtlSeconds?: number;
|
|
146
|
+
cacheTier?: string;
|
|
131
147
|
freshEnrichmentUsed?: boolean;
|
|
132
148
|
liveProviderCalled?: boolean;
|
|
133
149
|
openrouterCalled?: boolean;
|
|
@@ -217,6 +233,14 @@ interface CompanySignalEnrichmentResult {
|
|
|
217
233
|
evidenceCountTotal?: number;
|
|
218
234
|
evidenceSourcesOmitted?: number;
|
|
219
235
|
evidenceScope?: 'selected_signals' | 'all_candidates';
|
|
236
|
+
billingMetadata?: Record<string, unknown>;
|
|
237
|
+
resultReturnedFrom?: string;
|
|
238
|
+
cacheHit?: boolean;
|
|
239
|
+
freshEnrichmentUsed?: boolean;
|
|
240
|
+
creditsUsed?: number;
|
|
241
|
+
liveProviderCalled?: boolean;
|
|
242
|
+
aiProviderCalled?: boolean;
|
|
243
|
+
byoAiUsed?: boolean;
|
|
220
244
|
raw: Record<string, unknown>;
|
|
221
245
|
}
|
|
222
246
|
type SignalDiscoverySource = 'web' | 'news';
|
|
@@ -311,7 +335,7 @@ interface SignalToCopyParams {
|
|
|
311
335
|
signalRunId?: string;
|
|
312
336
|
/** Bypass cached company signals and start fresh research. */
|
|
313
337
|
skipCache?: boolean;
|
|
314
|
-
/**
|
|
338
|
+
/** Use the full deep-research provider portfolio. The default uses bounded credit-free first-party and Bing grounding. */
|
|
315
339
|
enableDeepSearch?: boolean;
|
|
316
340
|
/** @deprecated Signal to Copy is always synchronous; this option is ignored. */
|
|
317
341
|
waitForResult?: boolean;
|
|
@@ -346,6 +370,14 @@ interface SignalToCopyResult {
|
|
|
346
370
|
empty?: boolean;
|
|
347
371
|
emptyReason?: string;
|
|
348
372
|
researchMetadata?: Record<string, unknown>;
|
|
373
|
+
billingMetadata?: Record<string, unknown>;
|
|
374
|
+
resultReturnedFrom?: string;
|
|
375
|
+
cacheHit?: boolean;
|
|
376
|
+
freshEnrichmentUsed?: boolean;
|
|
377
|
+
creditsUsed?: number;
|
|
378
|
+
liveProviderCalled?: boolean;
|
|
379
|
+
aiProviderCalled?: boolean;
|
|
380
|
+
byoAiUsed?: boolean;
|
|
349
381
|
variations: SignalToCopyVariation[];
|
|
350
382
|
raw: Record<string, unknown>;
|
|
351
383
|
}
|
package/dist/index.js
CHANGED
|
@@ -559,11 +559,16 @@ var Signaliz = class {
|
|
|
559
559
|
);
|
|
560
560
|
}
|
|
561
561
|
const startedAt = Date.now();
|
|
562
|
-
const deduplicated = dedupeExactBatchItems(
|
|
563
|
-
|
|
564
|
-
|
|
562
|
+
const deduplicated = dedupeExactBatchItems(
|
|
563
|
+
requests,
|
|
564
|
+
(params) => coreProductBatchRequestKey("api/v1/signal-to-copy", signalToCopyRequestBody(params))
|
|
565
|
+
);
|
|
565
566
|
const uniqueRequests = deduplicated.uniqueItems;
|
|
566
|
-
const
|
|
567
|
+
const uniqueInputIndices = new Array(uniqueRequests.length);
|
|
568
|
+
deduplicated.sourceIndexByInput.forEach((uniqueIndex, inputIndex) => {
|
|
569
|
+
if (uniqueInputIndices[uniqueIndex] === void 0) uniqueInputIndices[uniqueIndex] = inputIndex;
|
|
570
|
+
});
|
|
571
|
+
const uniqueResults = await this.runSignalCopyBatchChunks(uniqueRequests, options, uniqueInputIndices);
|
|
567
572
|
const results = requests.map((_, index) => ({
|
|
568
573
|
...uniqueResults[deduplicated.sourceIndexByInput[index]],
|
|
569
574
|
index
|
|
@@ -589,15 +594,25 @@ var Signaliz = class {
|
|
|
589
594
|
}
|
|
590
595
|
return normalizeCoreProductDryRunResult(data);
|
|
591
596
|
}
|
|
592
|
-
async runSignalCopyBatchChunks(uniqueRequests, options) {
|
|
597
|
+
async runSignalCopyBatchChunks(uniqueRequests, options, idempotencyItemIndices = uniqueRequests.map((_, index) => index)) {
|
|
593
598
|
const chunks = [];
|
|
594
599
|
for (let offset = 0; offset < uniqueRequests.length; offset += 25) {
|
|
595
|
-
chunks.push({
|
|
600
|
+
chunks.push({
|
|
601
|
+
offset,
|
|
602
|
+
requests: uniqueRequests.slice(offset, offset + 25),
|
|
603
|
+
idempotencyItemIndices: idempotencyItemIndices.slice(offset, offset + 25)
|
|
604
|
+
});
|
|
596
605
|
}
|
|
597
606
|
const { serverConcurrency, chunkConcurrency } = batchConcurrencyPlan(options, 25, 25);
|
|
598
607
|
const chunkBatch = await runBatch(
|
|
599
608
|
chunks,
|
|
600
|
-
(chunk) => this.runSignalCopyBatchChunk(
|
|
609
|
+
(chunk) => this.runSignalCopyBatchChunk(
|
|
610
|
+
chunk.requests,
|
|
611
|
+
serverConcurrency,
|
|
612
|
+
0,
|
|
613
|
+
options?.idempotencyKey,
|
|
614
|
+
chunk.idempotencyItemIndices
|
|
615
|
+
),
|
|
601
616
|
{ concurrency: chunkConcurrency }
|
|
602
617
|
);
|
|
603
618
|
const uniqueResults = new Array(uniqueRequests.length);
|
|
@@ -619,14 +634,15 @@ var Signaliz = class {
|
|
|
619
634
|
}
|
|
620
635
|
return uniqueResults;
|
|
621
636
|
}
|
|
622
|
-
async runRecoverableCoreBatchJob(path, requests, label, pageSize = 500, maxWaitMs = 20 * 6e4, idempotencyKey) {
|
|
637
|
+
async runRecoverableCoreBatchJob(path, requests, label, pageSize = 500, maxWaitMs = 20 * 6e4, idempotencyKey, idempotencyItemIndices) {
|
|
623
638
|
const startedAt = Date.now();
|
|
624
639
|
const submissionIdempotencyKey = idempotencyKey?.trim() || newBatchIdempotencyKey(label);
|
|
625
640
|
let submission;
|
|
626
641
|
try {
|
|
627
642
|
submission = await this.client.post(path, {
|
|
628
643
|
requests,
|
|
629
|
-
idempotency_key: submissionIdempotencyKey
|
|
644
|
+
idempotency_key: submissionIdempotencyKey,
|
|
645
|
+
batch_item_indices: idempotencyItemIndices
|
|
630
646
|
});
|
|
631
647
|
} catch (error) {
|
|
632
648
|
if (error instanceof SignalizError && !error.isRetryable) throw error;
|
|
@@ -689,12 +705,14 @@ var Signaliz = class {
|
|
|
689
705
|
if (seen.size !== requests.length) throw new Error(`${label} batch returned incomplete indexed results`);
|
|
690
706
|
return rows;
|
|
691
707
|
}
|
|
692
|
-
async runSignalCopyBatchChunk(requests, concurrency, rowRateLimitAttempt = 0) {
|
|
708
|
+
async runSignalCopyBatchChunk(requests, concurrency, rowRateLimitAttempt = 0, idempotencyKey, idempotencyItemIndices = requests.map((_, index) => index)) {
|
|
693
709
|
const results = new Array(requests.length);
|
|
694
710
|
const rateLimited = [];
|
|
695
711
|
const data = await this.client.post("api/v1/signal-to-copy", {
|
|
696
712
|
requests: requests.map(signalToCopyRequestBody),
|
|
697
|
-
concurrency
|
|
713
|
+
concurrency,
|
|
714
|
+
idempotency_key: idempotencyKey,
|
|
715
|
+
batch_item_indices: idempotencyItemIndices
|
|
698
716
|
});
|
|
699
717
|
if (!Array.isArray(data.results) || data.results.length !== requests.length) {
|
|
700
718
|
throw new Error("Signal to Copy batch returned an invalid result count");
|
|
@@ -729,7 +747,9 @@ var Signaliz = class {
|
|
|
729
747
|
const retried = await this.runSignalCopyBatchChunk(
|
|
730
748
|
rateLimited.map((item) => item.params),
|
|
731
749
|
concurrency,
|
|
732
|
-
rowRateLimitAttempt + 1
|
|
750
|
+
rowRateLimitAttempt + 1,
|
|
751
|
+
idempotencyKey,
|
|
752
|
+
rateLimited.map((item) => idempotencyItemIndices[item.index])
|
|
733
753
|
);
|
|
734
754
|
for (let index = 0; index < rateLimited.length; index += 1) {
|
|
735
755
|
results[rateLimited[index].index] = { ...retried[index], index: rateLimited[index].index };
|
|
@@ -739,7 +759,10 @@ var Signaliz = class {
|
|
|
739
759
|
}
|
|
740
760
|
async runCoreBatchRound(path, tasks, options, rowRateLimitAttempt = 0) {
|
|
741
761
|
const { serverConcurrency, chunkConcurrency } = path === "api/v1/company-signals" ? batchConcurrencyPlan(options, 25, 25) : batchConcurrencyPlan(options);
|
|
742
|
-
const deduplicated = dedupeExactBatchItems(
|
|
762
|
+
const deduplicated = dedupeExactBatchItems(
|
|
763
|
+
tasks,
|
|
764
|
+
(task) => coreProductBatchRequestKey(path, task.request)
|
|
765
|
+
);
|
|
743
766
|
const uniqueTasks = deduplicated.uniqueItems;
|
|
744
767
|
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 } : void 0;
|
|
745
768
|
if (path !== "api/v1/company-signals" && recoverableBatch && uniqueTasks.length > 25) {
|
|
@@ -749,7 +772,8 @@ var Signaliz = class {
|
|
|
749
772
|
recoverableBatch.label,
|
|
750
773
|
recoverableBatch.pageSize,
|
|
751
774
|
recoverableBatch.maxWaitMs,
|
|
752
|
-
options?.idempotencyKey
|
|
775
|
+
options?.idempotencyKey,
|
|
776
|
+
uniqueTasks.map((task) => task.index)
|
|
753
777
|
);
|
|
754
778
|
const uniqueResults2 = new Array(uniqueTasks.length);
|
|
755
779
|
for (const raw of rows) {
|
|
@@ -773,7 +797,9 @@ var Signaliz = class {
|
|
|
773
797
|
const chunkBatch = await runBatch(chunks, async (chunk) => {
|
|
774
798
|
const data = await this.client.post(path, {
|
|
775
799
|
requests: chunk.tasks.map((task) => task.request),
|
|
776
|
-
concurrency: serverConcurrency
|
|
800
|
+
concurrency: serverConcurrency,
|
|
801
|
+
idempotency_key: options?.idempotencyKey,
|
|
802
|
+
batch_item_indices: chunk.tasks.map((task) => task.index)
|
|
777
803
|
});
|
|
778
804
|
if (!Array.isArray(data.results) || data.results.length !== chunk.tasks.length) {
|
|
779
805
|
throw new Error(`${path} batch returned an invalid result count`);
|
|
@@ -908,7 +934,11 @@ function normalizeCoreProductDryRunResult(data) {
|
|
|
908
934
|
status: "planned",
|
|
909
935
|
capability: String(data.capability || ""),
|
|
910
936
|
inputCount: Number(data.input_count || 0),
|
|
937
|
+
freshInputCount: Number(data.fresh_input_count ?? data.input_count ?? 0),
|
|
938
|
+
uniqueFreshInputCount: Number(data.unique_fresh_input_count ?? data.fresh_input_count ?? data.input_count ?? 0),
|
|
939
|
+
inputDuplicatesSuppressed: Number(data.input_duplicates_suppressed || 0),
|
|
911
940
|
recoveryReadCount: Number(data.recovery_read_count || 0),
|
|
941
|
+
uniqueRecoveryReadCount: Number(data.unique_recovery_read_count ?? data.recovery_read_count ?? 0),
|
|
912
942
|
plan: data.plan && typeof data.plan === "object" ? data.plan : {},
|
|
913
943
|
estimate: data.estimate && typeof data.estimate === "object" ? data.estimate : {},
|
|
914
944
|
estimatedCredits: {
|
|
@@ -923,12 +953,12 @@ function normalizeCoreProductDryRunResult(data) {
|
|
|
923
953
|
function normalizeFindEmailResult(data) {
|
|
924
954
|
const email = typeof data.email === "string" && data.email.trim() ? data.email.trim() : null;
|
|
925
955
|
const found = Boolean(email) && data.found !== false;
|
|
926
|
-
const verificationStatus = data.verification_status ?? data.status ?? "unknown";
|
|
956
|
+
const verificationStatus = data.verification_status ?? (data.is_valid === true ? "valid" : data.status ?? "unknown");
|
|
927
957
|
const rawFreshness = String(data.verification_freshness ?? "").toLowerCase();
|
|
928
958
|
const verificationFreshness = rawFreshness === "fresh" || rawFreshness === "stale" ? rawFreshness : "unknown";
|
|
929
959
|
const needsReverification = data.needs_reverification === true || verificationFreshness === "stale";
|
|
930
960
|
const explicitlyNotSendSafe = data.verified_for_sending === false || needsReverification;
|
|
931
|
-
const verified = !explicitlyNotSendSafe && (data.email_verified === true || data.verified_for_sending === true || ["valid", "deliverable"].includes(String(verificationStatus).toLowerCase()));
|
|
961
|
+
const verified = !explicitlyNotSendSafe && (data.is_valid === true || data.isValid === true || data.email_verified === true || data.verified_for_sending === true || ["valid", "deliverable"].includes(String(verificationStatus).toLowerCase()));
|
|
932
962
|
const verificationAgeDays = typeof data.verification_age_days === "number" && Number.isFinite(data.verification_age_days) ? data.verification_age_days : null;
|
|
933
963
|
return {
|
|
934
964
|
success: data.success !== false && found,
|
|
@@ -951,6 +981,9 @@ function normalizeFindEmailResult(data) {
|
|
|
951
981
|
billingMetadata: data.billing_metadata,
|
|
952
982
|
resultReturnedFrom: data.result_returned_from,
|
|
953
983
|
cacheHit: data.cache_hit,
|
|
984
|
+
negativeCacheHit: data.negative_cache_hit,
|
|
985
|
+
negativeCacheTtlSeconds: data.negative_cache_ttl_seconds,
|
|
986
|
+
cacheTier: data.cache_tier,
|
|
954
987
|
freshEnrichmentUsed: data.fresh_enrichment_used,
|
|
955
988
|
liveProviderCalled: data.live_provider_called,
|
|
956
989
|
openrouterCalled: data.openrouter_called,
|
|
@@ -972,6 +1005,7 @@ function normalizeVerifyEmailResult(email, data) {
|
|
|
972
1005
|
nextPollAfterSeconds: data.next_poll_after_seconds,
|
|
973
1006
|
isValid: data.is_valid ?? data.valid ?? verified,
|
|
974
1007
|
isDeliverable: data.is_deliverable ?? data.deliverable ?? verified,
|
|
1008
|
+
isMalformed: data.is_malformed === true || data.verification_verdict === "malformed" || data.verification_status === "malformed" || data.deliverability_status === "malformed",
|
|
975
1009
|
isCatchAll: data.email_is_catch_all ?? data.is_catch_all ?? data.catch_all ?? data.diagnostics?.is_catch_all ?? false,
|
|
976
1010
|
verifiedForSending: data.verified_for_sending === true,
|
|
977
1011
|
verificationVerdict: data.verification_verdict ?? data.verification_status ?? "unknown",
|
|
@@ -983,6 +1017,9 @@ function normalizeVerifyEmailResult(email, data) {
|
|
|
983
1017
|
billingMetadata: data.billing_metadata,
|
|
984
1018
|
resultReturnedFrom: data.result_returned_from,
|
|
985
1019
|
cacheHit: data.cache_hit,
|
|
1020
|
+
negativeCacheHit: data.negative_cache_hit,
|
|
1021
|
+
negativeCacheTtlSeconds: data.negative_cache_ttl_seconds,
|
|
1022
|
+
cacheTier: data.cache_tier,
|
|
986
1023
|
freshEnrichmentUsed: data.fresh_enrichment_used,
|
|
987
1024
|
liveProviderCalled: data.live_provider_called,
|
|
988
1025
|
openrouterCalled: data.openrouter_called,
|
|
@@ -1052,6 +1089,14 @@ function normalizeCompanySignalResult(params, data) {
|
|
|
1052
1089
|
evidenceCountTotal: data.evidence_count_total,
|
|
1053
1090
|
evidenceSourcesOmitted: data.evidence_sources_omitted,
|
|
1054
1091
|
evidenceScope: data.evidence_scope,
|
|
1092
|
+
billingMetadata: data.billing_metadata,
|
|
1093
|
+
resultReturnedFrom: data.result_returned_from,
|
|
1094
|
+
cacheHit: data.cache_hit,
|
|
1095
|
+
freshEnrichmentUsed: data.fresh_enrichment_used,
|
|
1096
|
+
creditsUsed: data.credits_used,
|
|
1097
|
+
liveProviderCalled: data.live_provider_called,
|
|
1098
|
+
aiProviderCalled: data.ai_provider_called,
|
|
1099
|
+
byoAiUsed: data.byo_ai_used,
|
|
1055
1100
|
raw: data
|
|
1056
1101
|
};
|
|
1057
1102
|
}
|
|
@@ -1233,6 +1278,75 @@ function dedupeExactBatchItems(items, keyForItem) {
|
|
|
1233
1278
|
}
|
|
1234
1279
|
return { uniqueItems, sourceIndexByInput };
|
|
1235
1280
|
}
|
|
1281
|
+
function normalizedBatchText(value) {
|
|
1282
|
+
return typeof value === "string" ? value.trim().toLowerCase().replace(/\s+/g, " ") : "";
|
|
1283
|
+
}
|
|
1284
|
+
function normalizedBatchDomain(value) {
|
|
1285
|
+
return normalizedBatchText(value).replace(/^https?:\/\//, "").replace(/^www\./, "").split(/[/?#]/, 1)[0].replace(/\.+$/, "");
|
|
1286
|
+
}
|
|
1287
|
+
function normalizedBatchLinkedIn(value) {
|
|
1288
|
+
return normalizedBatchText(value).replace(/\/+$/, "");
|
|
1289
|
+
}
|
|
1290
|
+
function normalizedBatchList(value) {
|
|
1291
|
+
return Array.isArray(value) ? value.map(normalizedBatchText).filter(Boolean).sort() : [];
|
|
1292
|
+
}
|
|
1293
|
+
function normalizedFindEmailBatchName(request) {
|
|
1294
|
+
const fullName = normalizedBatchText(request.full_name);
|
|
1295
|
+
const componentName = [normalizedBatchText(request.first_name), normalizedBatchText(request.last_name)].filter(Boolean).join(" ");
|
|
1296
|
+
if (!fullName || !componentName || fullName === componentName) return fullName || componentName;
|
|
1297
|
+
return JSON.stringify([fullName, componentName]);
|
|
1298
|
+
}
|
|
1299
|
+
function coreProductBatchRequestKey(path, request) {
|
|
1300
|
+
if (path === "api/v1/find-email") {
|
|
1301
|
+
return JSON.stringify([
|
|
1302
|
+
normalizedFindEmailBatchName(request),
|
|
1303
|
+
normalizedBatchDomain(request.company_domain),
|
|
1304
|
+
normalizedBatchLinkedIn(request.linkedin_url),
|
|
1305
|
+
normalizedBatchText(request.company_name),
|
|
1306
|
+
request.skip_cache === true || request.bypass_cache === true
|
|
1307
|
+
]);
|
|
1308
|
+
}
|
|
1309
|
+
if (path === "api/v1/verify-email") {
|
|
1310
|
+
return JSON.stringify([
|
|
1311
|
+
normalizedBatchText(request.email),
|
|
1312
|
+
request.skip_cache === true || request.bypass_cache === true,
|
|
1313
|
+
request.skip_catch_all_verification === true,
|
|
1314
|
+
request.skip_esp_check === true
|
|
1315
|
+
]);
|
|
1316
|
+
}
|
|
1317
|
+
if (path === "api/v1/company-signals") {
|
|
1318
|
+
const online = request.online !== false;
|
|
1319
|
+
const deepSearch = request.enable_deep_search === void 0 ? online : request.enable_deep_search !== false;
|
|
1320
|
+
return JSON.stringify([
|
|
1321
|
+
normalizedBatchText(request.signal_run_id),
|
|
1322
|
+
normalizedBatchDomain(request.company_domain || request.domain),
|
|
1323
|
+
normalizedBatchText(request.company_name),
|
|
1324
|
+
normalizedBatchText(request.company_id),
|
|
1325
|
+
normalizedBatchLinkedIn(request.linkedin_url),
|
|
1326
|
+
normalizedBatchText(request.industry),
|
|
1327
|
+
typeof request.research_prompt === "string" ? request.research_prompt.trim() : "",
|
|
1328
|
+
normalizedBatchList(request.signal_types),
|
|
1329
|
+
Number(request.target_signal_count) || null,
|
|
1330
|
+
Number(request.lookback_days) || null,
|
|
1331
|
+
online,
|
|
1332
|
+
deepSearch,
|
|
1333
|
+
normalizedBatchText(request.search_mode || "balanced"),
|
|
1334
|
+
request.enable_outreach_intelligence === true,
|
|
1335
|
+
request.enable_predictive_intelligence === true,
|
|
1336
|
+
request.skip_cache === true || request.bypass_cache === true
|
|
1337
|
+
]);
|
|
1338
|
+
}
|
|
1339
|
+
return JSON.stringify([
|
|
1340
|
+
normalizedBatchDomain(request.company_domain),
|
|
1341
|
+
typeof request.person_name === "string" ? request.person_name.trim() : "",
|
|
1342
|
+
typeof request.title === "string" ? request.title.trim() : "",
|
|
1343
|
+
typeof request.campaign_offer === "string" ? request.campaign_offer.trim() : "",
|
|
1344
|
+
typeof request.research_prompt === "string" ? request.research_prompt.trim() : "",
|
|
1345
|
+
typeof request.signal_run_id === "string" ? request.signal_run_id.trim() : "",
|
|
1346
|
+
request.skip_cache === true,
|
|
1347
|
+
request.enable_deep_search === true
|
|
1348
|
+
]);
|
|
1349
|
+
}
|
|
1236
1350
|
function finalizeCoreBatch(total, startedAt, round, normalize, options) {
|
|
1237
1351
|
const results = new Array(total);
|
|
1238
1352
|
for (const item of round) {
|
|
@@ -1329,6 +1443,14 @@ function normalizeSignalToCopyResult(data) {
|
|
|
1329
1443
|
empty: data.empty,
|
|
1330
1444
|
emptyReason: data.empty_reason,
|
|
1331
1445
|
researchMetadata: data.research_metadata,
|
|
1446
|
+
billingMetadata: data.billing_metadata,
|
|
1447
|
+
resultReturnedFrom: data.result_returned_from,
|
|
1448
|
+
cacheHit: data.cache_hit,
|
|
1449
|
+
freshEnrichmentUsed: data.fresh_enrichment_used,
|
|
1450
|
+
creditsUsed: data.credits_used,
|
|
1451
|
+
liveProviderCalled: data.live_provider_called,
|
|
1452
|
+
aiProviderCalled: data.ai_provider_called,
|
|
1453
|
+
byoAiUsed: data.byo_ai_used,
|
|
1332
1454
|
variations: (data.variations ?? []).map((variation) => ({
|
|
1333
1455
|
style: variation.style,
|
|
1334
1456
|
subjectLine: variation.subject_line ?? "",
|
package/dist/index.mjs
CHANGED
package/dist/mcp-config.js
CHANGED
|
@@ -563,11 +563,16 @@ var Signaliz = class {
|
|
|
563
563
|
);
|
|
564
564
|
}
|
|
565
565
|
const startedAt = Date.now();
|
|
566
|
-
const deduplicated = dedupeExactBatchItems(
|
|
567
|
-
|
|
568
|
-
|
|
566
|
+
const deduplicated = dedupeExactBatchItems(
|
|
567
|
+
requests,
|
|
568
|
+
(params) => coreProductBatchRequestKey("api/v1/signal-to-copy", signalToCopyRequestBody(params))
|
|
569
|
+
);
|
|
569
570
|
const uniqueRequests = deduplicated.uniqueItems;
|
|
570
|
-
const
|
|
571
|
+
const uniqueInputIndices = new Array(uniqueRequests.length);
|
|
572
|
+
deduplicated.sourceIndexByInput.forEach((uniqueIndex, inputIndex) => {
|
|
573
|
+
if (uniqueInputIndices[uniqueIndex] === void 0) uniqueInputIndices[uniqueIndex] = inputIndex;
|
|
574
|
+
});
|
|
575
|
+
const uniqueResults = await this.runSignalCopyBatchChunks(uniqueRequests, options, uniqueInputIndices);
|
|
571
576
|
const results = requests.map((_, index) => ({
|
|
572
577
|
...uniqueResults[deduplicated.sourceIndexByInput[index]],
|
|
573
578
|
index
|
|
@@ -593,15 +598,25 @@ var Signaliz = class {
|
|
|
593
598
|
}
|
|
594
599
|
return normalizeCoreProductDryRunResult(data);
|
|
595
600
|
}
|
|
596
|
-
async runSignalCopyBatchChunks(uniqueRequests, options) {
|
|
601
|
+
async runSignalCopyBatchChunks(uniqueRequests, options, idempotencyItemIndices = uniqueRequests.map((_, index) => index)) {
|
|
597
602
|
const chunks = [];
|
|
598
603
|
for (let offset = 0; offset < uniqueRequests.length; offset += 25) {
|
|
599
|
-
chunks.push({
|
|
604
|
+
chunks.push({
|
|
605
|
+
offset,
|
|
606
|
+
requests: uniqueRequests.slice(offset, offset + 25),
|
|
607
|
+
idempotencyItemIndices: idempotencyItemIndices.slice(offset, offset + 25)
|
|
608
|
+
});
|
|
600
609
|
}
|
|
601
610
|
const { serverConcurrency, chunkConcurrency } = batchConcurrencyPlan(options, 25, 25);
|
|
602
611
|
const chunkBatch = await runBatch(
|
|
603
612
|
chunks,
|
|
604
|
-
(chunk) => this.runSignalCopyBatchChunk(
|
|
613
|
+
(chunk) => this.runSignalCopyBatchChunk(
|
|
614
|
+
chunk.requests,
|
|
615
|
+
serverConcurrency,
|
|
616
|
+
0,
|
|
617
|
+
options?.idempotencyKey,
|
|
618
|
+
chunk.idempotencyItemIndices
|
|
619
|
+
),
|
|
605
620
|
{ concurrency: chunkConcurrency }
|
|
606
621
|
);
|
|
607
622
|
const uniqueResults = new Array(uniqueRequests.length);
|
|
@@ -623,14 +638,15 @@ var Signaliz = class {
|
|
|
623
638
|
}
|
|
624
639
|
return uniqueResults;
|
|
625
640
|
}
|
|
626
|
-
async runRecoverableCoreBatchJob(path2, requests, label, pageSize = 500, maxWaitMs = 20 * 6e4, idempotencyKey) {
|
|
641
|
+
async runRecoverableCoreBatchJob(path2, requests, label, pageSize = 500, maxWaitMs = 20 * 6e4, idempotencyKey, idempotencyItemIndices) {
|
|
627
642
|
const startedAt = Date.now();
|
|
628
643
|
const submissionIdempotencyKey = idempotencyKey?.trim() || newBatchIdempotencyKey(label);
|
|
629
644
|
let submission;
|
|
630
645
|
try {
|
|
631
646
|
submission = await this.client.post(path2, {
|
|
632
647
|
requests,
|
|
633
|
-
idempotency_key: submissionIdempotencyKey
|
|
648
|
+
idempotency_key: submissionIdempotencyKey,
|
|
649
|
+
batch_item_indices: idempotencyItemIndices
|
|
634
650
|
});
|
|
635
651
|
} catch (error) {
|
|
636
652
|
if (error instanceof SignalizError && !error.isRetryable) throw error;
|
|
@@ -693,12 +709,14 @@ var Signaliz = class {
|
|
|
693
709
|
if (seen.size !== requests.length) throw new Error(`${label} batch returned incomplete indexed results`);
|
|
694
710
|
return rows;
|
|
695
711
|
}
|
|
696
|
-
async runSignalCopyBatchChunk(requests, concurrency, rowRateLimitAttempt = 0) {
|
|
712
|
+
async runSignalCopyBatchChunk(requests, concurrency, rowRateLimitAttempt = 0, idempotencyKey, idempotencyItemIndices = requests.map((_, index) => index)) {
|
|
697
713
|
const results = new Array(requests.length);
|
|
698
714
|
const rateLimited = [];
|
|
699
715
|
const data = await this.client.post("api/v1/signal-to-copy", {
|
|
700
716
|
requests: requests.map(signalToCopyRequestBody),
|
|
701
|
-
concurrency
|
|
717
|
+
concurrency,
|
|
718
|
+
idempotency_key: idempotencyKey,
|
|
719
|
+
batch_item_indices: idempotencyItemIndices
|
|
702
720
|
});
|
|
703
721
|
if (!Array.isArray(data.results) || data.results.length !== requests.length) {
|
|
704
722
|
throw new Error("Signal to Copy batch returned an invalid result count");
|
|
@@ -733,7 +751,9 @@ var Signaliz = class {
|
|
|
733
751
|
const retried = await this.runSignalCopyBatchChunk(
|
|
734
752
|
rateLimited.map((item) => item.params),
|
|
735
753
|
concurrency,
|
|
736
|
-
rowRateLimitAttempt + 1
|
|
754
|
+
rowRateLimitAttempt + 1,
|
|
755
|
+
idempotencyKey,
|
|
756
|
+
rateLimited.map((item) => idempotencyItemIndices[item.index])
|
|
737
757
|
);
|
|
738
758
|
for (let index = 0; index < rateLimited.length; index += 1) {
|
|
739
759
|
results[rateLimited[index].index] = { ...retried[index], index: rateLimited[index].index };
|
|
@@ -743,7 +763,10 @@ var Signaliz = class {
|
|
|
743
763
|
}
|
|
744
764
|
async runCoreBatchRound(path2, tasks, options, rowRateLimitAttempt = 0) {
|
|
745
765
|
const { serverConcurrency, chunkConcurrency } = path2 === "api/v1/company-signals" ? batchConcurrencyPlan(options, 25, 25) : batchConcurrencyPlan(options);
|
|
746
|
-
const deduplicated = dedupeExactBatchItems(
|
|
766
|
+
const deduplicated = dedupeExactBatchItems(
|
|
767
|
+
tasks,
|
|
768
|
+
(task) => coreProductBatchRequestKey(path2, task.request)
|
|
769
|
+
);
|
|
747
770
|
const uniqueTasks = deduplicated.uniqueItems;
|
|
748
771
|
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 } : void 0;
|
|
749
772
|
if (path2 !== "api/v1/company-signals" && recoverableBatch && uniqueTasks.length > 25) {
|
|
@@ -753,7 +776,8 @@ var Signaliz = class {
|
|
|
753
776
|
recoverableBatch.label,
|
|
754
777
|
recoverableBatch.pageSize,
|
|
755
778
|
recoverableBatch.maxWaitMs,
|
|
756
|
-
options?.idempotencyKey
|
|
779
|
+
options?.idempotencyKey,
|
|
780
|
+
uniqueTasks.map((task) => task.index)
|
|
757
781
|
);
|
|
758
782
|
const uniqueResults2 = new Array(uniqueTasks.length);
|
|
759
783
|
for (const raw of rows) {
|
|
@@ -777,7 +801,9 @@ var Signaliz = class {
|
|
|
777
801
|
const chunkBatch = await runBatch(chunks, async (chunk) => {
|
|
778
802
|
const data = await this.client.post(path2, {
|
|
779
803
|
requests: chunk.tasks.map((task) => task.request),
|
|
780
|
-
concurrency: serverConcurrency
|
|
804
|
+
concurrency: serverConcurrency,
|
|
805
|
+
idempotency_key: options?.idempotencyKey,
|
|
806
|
+
batch_item_indices: chunk.tasks.map((task) => task.index)
|
|
781
807
|
});
|
|
782
808
|
if (!Array.isArray(data.results) || data.results.length !== chunk.tasks.length) {
|
|
783
809
|
throw new Error(`${path2} batch returned an invalid result count`);
|
|
@@ -912,7 +938,11 @@ function normalizeCoreProductDryRunResult(data) {
|
|
|
912
938
|
status: "planned",
|
|
913
939
|
capability: String(data.capability || ""),
|
|
914
940
|
inputCount: Number(data.input_count || 0),
|
|
941
|
+
freshInputCount: Number(data.fresh_input_count ?? data.input_count ?? 0),
|
|
942
|
+
uniqueFreshInputCount: Number(data.unique_fresh_input_count ?? data.fresh_input_count ?? data.input_count ?? 0),
|
|
943
|
+
inputDuplicatesSuppressed: Number(data.input_duplicates_suppressed || 0),
|
|
915
944
|
recoveryReadCount: Number(data.recovery_read_count || 0),
|
|
945
|
+
uniqueRecoveryReadCount: Number(data.unique_recovery_read_count ?? data.recovery_read_count ?? 0),
|
|
916
946
|
plan: data.plan && typeof data.plan === "object" ? data.plan : {},
|
|
917
947
|
estimate: data.estimate && typeof data.estimate === "object" ? data.estimate : {},
|
|
918
948
|
estimatedCredits: {
|
|
@@ -927,12 +957,12 @@ function normalizeCoreProductDryRunResult(data) {
|
|
|
927
957
|
function normalizeFindEmailResult(data) {
|
|
928
958
|
const email = typeof data.email === "string" && data.email.trim() ? data.email.trim() : null;
|
|
929
959
|
const found = Boolean(email) && data.found !== false;
|
|
930
|
-
const verificationStatus = data.verification_status ?? data.status ?? "unknown";
|
|
960
|
+
const verificationStatus = data.verification_status ?? (data.is_valid === true ? "valid" : data.status ?? "unknown");
|
|
931
961
|
const rawFreshness = String(data.verification_freshness ?? "").toLowerCase();
|
|
932
962
|
const verificationFreshness = rawFreshness === "fresh" || rawFreshness === "stale" ? rawFreshness : "unknown";
|
|
933
963
|
const needsReverification = data.needs_reverification === true || verificationFreshness === "stale";
|
|
934
964
|
const explicitlyNotSendSafe = data.verified_for_sending === false || needsReverification;
|
|
935
|
-
const verified = !explicitlyNotSendSafe && (data.email_verified === true || data.verified_for_sending === true || ["valid", "deliverable"].includes(String(verificationStatus).toLowerCase()));
|
|
965
|
+
const verified = !explicitlyNotSendSafe && (data.is_valid === true || data.isValid === true || data.email_verified === true || data.verified_for_sending === true || ["valid", "deliverable"].includes(String(verificationStatus).toLowerCase()));
|
|
936
966
|
const verificationAgeDays = typeof data.verification_age_days === "number" && Number.isFinite(data.verification_age_days) ? data.verification_age_days : null;
|
|
937
967
|
return {
|
|
938
968
|
success: data.success !== false && found,
|
|
@@ -955,6 +985,9 @@ function normalizeFindEmailResult(data) {
|
|
|
955
985
|
billingMetadata: data.billing_metadata,
|
|
956
986
|
resultReturnedFrom: data.result_returned_from,
|
|
957
987
|
cacheHit: data.cache_hit,
|
|
988
|
+
negativeCacheHit: data.negative_cache_hit,
|
|
989
|
+
negativeCacheTtlSeconds: data.negative_cache_ttl_seconds,
|
|
990
|
+
cacheTier: data.cache_tier,
|
|
958
991
|
freshEnrichmentUsed: data.fresh_enrichment_used,
|
|
959
992
|
liveProviderCalled: data.live_provider_called,
|
|
960
993
|
openrouterCalled: data.openrouter_called,
|
|
@@ -976,6 +1009,7 @@ function normalizeVerifyEmailResult(email, data) {
|
|
|
976
1009
|
nextPollAfterSeconds: data.next_poll_after_seconds,
|
|
977
1010
|
isValid: data.is_valid ?? data.valid ?? verified,
|
|
978
1011
|
isDeliverable: data.is_deliverable ?? data.deliverable ?? verified,
|
|
1012
|
+
isMalformed: data.is_malformed === true || data.verification_verdict === "malformed" || data.verification_status === "malformed" || data.deliverability_status === "malformed",
|
|
979
1013
|
isCatchAll: data.email_is_catch_all ?? data.is_catch_all ?? data.catch_all ?? data.diagnostics?.is_catch_all ?? false,
|
|
980
1014
|
verifiedForSending: data.verified_for_sending === true,
|
|
981
1015
|
verificationVerdict: data.verification_verdict ?? data.verification_status ?? "unknown",
|
|
@@ -987,6 +1021,9 @@ function normalizeVerifyEmailResult(email, data) {
|
|
|
987
1021
|
billingMetadata: data.billing_metadata,
|
|
988
1022
|
resultReturnedFrom: data.result_returned_from,
|
|
989
1023
|
cacheHit: data.cache_hit,
|
|
1024
|
+
negativeCacheHit: data.negative_cache_hit,
|
|
1025
|
+
negativeCacheTtlSeconds: data.negative_cache_ttl_seconds,
|
|
1026
|
+
cacheTier: data.cache_tier,
|
|
990
1027
|
freshEnrichmentUsed: data.fresh_enrichment_used,
|
|
991
1028
|
liveProviderCalled: data.live_provider_called,
|
|
992
1029
|
openrouterCalled: data.openrouter_called,
|
|
@@ -1056,6 +1093,14 @@ function normalizeCompanySignalResult(params, data) {
|
|
|
1056
1093
|
evidenceCountTotal: data.evidence_count_total,
|
|
1057
1094
|
evidenceSourcesOmitted: data.evidence_sources_omitted,
|
|
1058
1095
|
evidenceScope: data.evidence_scope,
|
|
1096
|
+
billingMetadata: data.billing_metadata,
|
|
1097
|
+
resultReturnedFrom: data.result_returned_from,
|
|
1098
|
+
cacheHit: data.cache_hit,
|
|
1099
|
+
freshEnrichmentUsed: data.fresh_enrichment_used,
|
|
1100
|
+
creditsUsed: data.credits_used,
|
|
1101
|
+
liveProviderCalled: data.live_provider_called,
|
|
1102
|
+
aiProviderCalled: data.ai_provider_called,
|
|
1103
|
+
byoAiUsed: data.byo_ai_used,
|
|
1059
1104
|
raw: data
|
|
1060
1105
|
};
|
|
1061
1106
|
}
|
|
@@ -1237,6 +1282,75 @@ function dedupeExactBatchItems(items, keyForItem) {
|
|
|
1237
1282
|
}
|
|
1238
1283
|
return { uniqueItems, sourceIndexByInput };
|
|
1239
1284
|
}
|
|
1285
|
+
function normalizedBatchText(value) {
|
|
1286
|
+
return typeof value === "string" ? value.trim().toLowerCase().replace(/\s+/g, " ") : "";
|
|
1287
|
+
}
|
|
1288
|
+
function normalizedBatchDomain(value) {
|
|
1289
|
+
return normalizedBatchText(value).replace(/^https?:\/\//, "").replace(/^www\./, "").split(/[/?#]/, 1)[0].replace(/\.+$/, "");
|
|
1290
|
+
}
|
|
1291
|
+
function normalizedBatchLinkedIn(value) {
|
|
1292
|
+
return normalizedBatchText(value).replace(/\/+$/, "");
|
|
1293
|
+
}
|
|
1294
|
+
function normalizedBatchList(value) {
|
|
1295
|
+
return Array.isArray(value) ? value.map(normalizedBatchText).filter(Boolean).sort() : [];
|
|
1296
|
+
}
|
|
1297
|
+
function normalizedFindEmailBatchName(request) {
|
|
1298
|
+
const fullName = normalizedBatchText(request.full_name);
|
|
1299
|
+
const componentName = [normalizedBatchText(request.first_name), normalizedBatchText(request.last_name)].filter(Boolean).join(" ");
|
|
1300
|
+
if (!fullName || !componentName || fullName === componentName) return fullName || componentName;
|
|
1301
|
+
return JSON.stringify([fullName, componentName]);
|
|
1302
|
+
}
|
|
1303
|
+
function coreProductBatchRequestKey(path2, request) {
|
|
1304
|
+
if (path2 === "api/v1/find-email") {
|
|
1305
|
+
return JSON.stringify([
|
|
1306
|
+
normalizedFindEmailBatchName(request),
|
|
1307
|
+
normalizedBatchDomain(request.company_domain),
|
|
1308
|
+
normalizedBatchLinkedIn(request.linkedin_url),
|
|
1309
|
+
normalizedBatchText(request.company_name),
|
|
1310
|
+
request.skip_cache === true || request.bypass_cache === true
|
|
1311
|
+
]);
|
|
1312
|
+
}
|
|
1313
|
+
if (path2 === "api/v1/verify-email") {
|
|
1314
|
+
return JSON.stringify([
|
|
1315
|
+
normalizedBatchText(request.email),
|
|
1316
|
+
request.skip_cache === true || request.bypass_cache === true,
|
|
1317
|
+
request.skip_catch_all_verification === true,
|
|
1318
|
+
request.skip_esp_check === true
|
|
1319
|
+
]);
|
|
1320
|
+
}
|
|
1321
|
+
if (path2 === "api/v1/company-signals") {
|
|
1322
|
+
const online = request.online !== false;
|
|
1323
|
+
const deepSearch = request.enable_deep_search === void 0 ? online : request.enable_deep_search !== false;
|
|
1324
|
+
return JSON.stringify([
|
|
1325
|
+
normalizedBatchText(request.signal_run_id),
|
|
1326
|
+
normalizedBatchDomain(request.company_domain || request.domain),
|
|
1327
|
+
normalizedBatchText(request.company_name),
|
|
1328
|
+
normalizedBatchText(request.company_id),
|
|
1329
|
+
normalizedBatchLinkedIn(request.linkedin_url),
|
|
1330
|
+
normalizedBatchText(request.industry),
|
|
1331
|
+
typeof request.research_prompt === "string" ? request.research_prompt.trim() : "",
|
|
1332
|
+
normalizedBatchList(request.signal_types),
|
|
1333
|
+
Number(request.target_signal_count) || null,
|
|
1334
|
+
Number(request.lookback_days) || null,
|
|
1335
|
+
online,
|
|
1336
|
+
deepSearch,
|
|
1337
|
+
normalizedBatchText(request.search_mode || "balanced"),
|
|
1338
|
+
request.enable_outreach_intelligence === true,
|
|
1339
|
+
request.enable_predictive_intelligence === true,
|
|
1340
|
+
request.skip_cache === true || request.bypass_cache === true
|
|
1341
|
+
]);
|
|
1342
|
+
}
|
|
1343
|
+
return JSON.stringify([
|
|
1344
|
+
normalizedBatchDomain(request.company_domain),
|
|
1345
|
+
typeof request.person_name === "string" ? request.person_name.trim() : "",
|
|
1346
|
+
typeof request.title === "string" ? request.title.trim() : "",
|
|
1347
|
+
typeof request.campaign_offer === "string" ? request.campaign_offer.trim() : "",
|
|
1348
|
+
typeof request.research_prompt === "string" ? request.research_prompt.trim() : "",
|
|
1349
|
+
typeof request.signal_run_id === "string" ? request.signal_run_id.trim() : "",
|
|
1350
|
+
request.skip_cache === true,
|
|
1351
|
+
request.enable_deep_search === true
|
|
1352
|
+
]);
|
|
1353
|
+
}
|
|
1240
1354
|
function finalizeCoreBatch(total, startedAt, round, normalize, options) {
|
|
1241
1355
|
const results = new Array(total);
|
|
1242
1356
|
for (const item of round) {
|
|
@@ -1333,6 +1447,14 @@ function normalizeSignalToCopyResult(data) {
|
|
|
1333
1447
|
empty: data.empty,
|
|
1334
1448
|
emptyReason: data.empty_reason,
|
|
1335
1449
|
researchMetadata: data.research_metadata,
|
|
1450
|
+
billingMetadata: data.billing_metadata,
|
|
1451
|
+
resultReturnedFrom: data.result_returned_from,
|
|
1452
|
+
cacheHit: data.cache_hit,
|
|
1453
|
+
freshEnrichmentUsed: data.fresh_enrichment_used,
|
|
1454
|
+
creditsUsed: data.credits_used,
|
|
1455
|
+
liveProviderCalled: data.live_provider_called,
|
|
1456
|
+
aiProviderCalled: data.ai_provider_called,
|
|
1457
|
+
byoAiUsed: data.byo_ai_used,
|
|
1336
1458
|
variations: (data.variations ?? []).map((variation) => ({
|
|
1337
1459
|
style: variation.style,
|
|
1338
1460
|
subjectLine: variation.subject_line ?? "",
|
package/dist/mcp-config.mjs
CHANGED
package/package.json
CHANGED