mailery 0.18.0 → 0.19.0
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/dist/admin/spa/index-B4zQqLvr.js.map +1 -1
- package/dist/index.cjs +51 -25
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +51 -25
- package/dist/index.js.map +1 -1
- package/dist/{null-BFipn5tF.d.cts → null-DxTHQSrJ.d.cts} +18 -5
- package/dist/{null-BFipn5tF.d.ts → null-DxTHQSrJ.d.ts} +18 -5
- package/dist/testing.cjs +48 -22
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.d.cts +2 -2
- package/dist/testing.d.ts +2 -2
- package/dist/testing.js +48 -22
- package/dist/testing.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { R as RunnerContext, N as NormalizedEvent, C as ContactAdapter, a as Contact, A as AdapterFilter, b as AdapterSort, M as MailProvider, S as SendArgs, c as SendResult, d as MailTesterFeedback, e as Mailer, T as TemplateDoc, F as FlowStep, f as Collections, g as FlowDoc, h as SuppressionScope, D as DeliveryWindow, i as SegmentFilter, P as Predicate } from './null-
|
|
2
|
-
export { j as AuditLogDoc, B as BotFilterConfig, k as BroadcastDoc, l as BroadcastOrder, m as BroadcastPauseReason, n as BroadcastStatus, o as CircuitBreakerThresholds, p as ContactTagDoc, E as EventDoc, q as FlowGoal, r as FlowRunDoc, s as FlowRunStatus, t as FlowVersionDoc, H as HealthDoc, u as HealthStatus, L as LeadDoc, v as MailerConfig, w as NullProvider, O as OutboxDoc, x as RESERVED_VAR_KEYS, y as RedisOptions, z as SegmentDefinition, G as SendDoc, I as SendStatus, J as SenderDomainConfig, K as SenderDomainRegistry, Q as SenderDomainValidation, U as SubscriptionDoc, V as SubscriptionStatus, W as SuppressionDoc, X as SuppressionReason, Y as TemplateKind, Z as TemplateVersionDoc, _ as VarsAdapter, $ as VarsResolveInfo, a0 as WebhookEventDoc, a1 as defineVars, a2 as ensureIndexes, a3 as getCollections, a4 as validateSenderDomain, a5 as varsJsonSchema } from './null-
|
|
1
|
+
import { R as RunnerContext, N as NormalizedEvent, C as ContactAdapter, a as Contact, A as AdapterFilter, b as AdapterSort, M as MailProvider, S as SendArgs, c as SendResult, d as MailTesterFeedback, e as Mailer, T as TemplateDoc, F as FlowStep, f as Collections, g as FlowDoc, h as SuppressionScope, D as DeliveryWindow, i as SegmentFilter, P as Predicate } from './null-DxTHQSrJ.cjs';
|
|
2
|
+
export { j as AuditLogDoc, B as BotFilterConfig, k as BroadcastDoc, l as BroadcastOrder, m as BroadcastPauseReason, n as BroadcastStatus, o as CircuitBreakerThresholds, p as ContactTagDoc, E as EventDoc, q as FlowGoal, r as FlowRunDoc, s as FlowRunStatus, t as FlowVersionDoc, H as HealthDoc, u as HealthStatus, L as LeadDoc, v as MailerConfig, w as NullProvider, O as OutboxDoc, x as RESERVED_VAR_KEYS, y as RedisOptions, z as SegmentDefinition, G as SendDoc, I as SendStatus, J as SenderDomainConfig, K as SenderDomainRegistry, Q as SenderDomainValidation, U as SubscriptionDoc, V as SubscriptionStatus, W as SuppressionDoc, X as SuppressionReason, Y as TemplateKind, Z as TemplateVersionDoc, _ as VarsAdapter, $ as VarsResolveInfo, a0 as WebhookEventDoc, a1 as defineVars, a2 as ensureIndexes, a3 as getCollections, a4 as validateSenderDomain, a5 as varsJsonSchema } from './null-DxTHQSrJ.cjs';
|
|
3
3
|
import { ObjectId, Db, Filter } from 'mongodb';
|
|
4
4
|
import { Request, Router } from 'express';
|
|
5
5
|
import Handlebars from 'handlebars';
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { R as RunnerContext, N as NormalizedEvent, C as ContactAdapter, a as Contact, A as AdapterFilter, b as AdapterSort, M as MailProvider, S as SendArgs, c as SendResult, d as MailTesterFeedback, e as Mailer, T as TemplateDoc, F as FlowStep, f as Collections, g as FlowDoc, h as SuppressionScope, D as DeliveryWindow, i as SegmentFilter, P as Predicate } from './null-
|
|
2
|
-
export { j as AuditLogDoc, B as BotFilterConfig, k as BroadcastDoc, l as BroadcastOrder, m as BroadcastPauseReason, n as BroadcastStatus, o as CircuitBreakerThresholds, p as ContactTagDoc, E as EventDoc, q as FlowGoal, r as FlowRunDoc, s as FlowRunStatus, t as FlowVersionDoc, H as HealthDoc, u as HealthStatus, L as LeadDoc, v as MailerConfig, w as NullProvider, O as OutboxDoc, x as RESERVED_VAR_KEYS, y as RedisOptions, z as SegmentDefinition, G as SendDoc, I as SendStatus, J as SenderDomainConfig, K as SenderDomainRegistry, Q as SenderDomainValidation, U as SubscriptionDoc, V as SubscriptionStatus, W as SuppressionDoc, X as SuppressionReason, Y as TemplateKind, Z as TemplateVersionDoc, _ as VarsAdapter, $ as VarsResolveInfo, a0 as WebhookEventDoc, a1 as defineVars, a2 as ensureIndexes, a3 as getCollections, a4 as validateSenderDomain, a5 as varsJsonSchema } from './null-
|
|
1
|
+
import { R as RunnerContext, N as NormalizedEvent, C as ContactAdapter, a as Contact, A as AdapterFilter, b as AdapterSort, M as MailProvider, S as SendArgs, c as SendResult, d as MailTesterFeedback, e as Mailer, T as TemplateDoc, F as FlowStep, f as Collections, g as FlowDoc, h as SuppressionScope, D as DeliveryWindow, i as SegmentFilter, P as Predicate } from './null-DxTHQSrJ.js';
|
|
2
|
+
export { j as AuditLogDoc, B as BotFilterConfig, k as BroadcastDoc, l as BroadcastOrder, m as BroadcastPauseReason, n as BroadcastStatus, o as CircuitBreakerThresholds, p as ContactTagDoc, E as EventDoc, q as FlowGoal, r as FlowRunDoc, s as FlowRunStatus, t as FlowVersionDoc, H as HealthDoc, u as HealthStatus, L as LeadDoc, v as MailerConfig, w as NullProvider, O as OutboxDoc, x as RESERVED_VAR_KEYS, y as RedisOptions, z as SegmentDefinition, G as SendDoc, I as SendStatus, J as SenderDomainConfig, K as SenderDomainRegistry, Q as SenderDomainValidation, U as SubscriptionDoc, V as SubscriptionStatus, W as SuppressionDoc, X as SuppressionReason, Y as TemplateKind, Z as TemplateVersionDoc, _ as VarsAdapter, $ as VarsResolveInfo, a0 as WebhookEventDoc, a1 as defineVars, a2 as ensureIndexes, a3 as getCollections, a4 as validateSenderDomain, a5 as varsJsonSchema } from './null-DxTHQSrJ.js';
|
|
3
3
|
import { ObjectId, Db, Filter } from 'mongodb';
|
|
4
4
|
import { Request, Router } from 'express';
|
|
5
5
|
import Handlebars from 'handlebars';
|
package/dist/index.js
CHANGED
|
@@ -1699,11 +1699,11 @@ var ZERO_COUNTERS = {
|
|
|
1699
1699
|
complained: 0,
|
|
1700
1700
|
failedToSend: 0
|
|
1701
1701
|
};
|
|
1702
|
-
var
|
|
1703
|
-
bounceRate:
|
|
1704
|
-
hardBounceRate:
|
|
1705
|
-
complaintRate:
|
|
1706
|
-
failureRate:
|
|
1702
|
+
var EMPTY_RATES = {
|
|
1703
|
+
bounceRate: null,
|
|
1704
|
+
hardBounceRate: null,
|
|
1705
|
+
complaintRate: null,
|
|
1706
|
+
failureRate: null
|
|
1707
1707
|
};
|
|
1708
1708
|
async function recordHealthCounter(ctx, counter2, dims, by = 1) {
|
|
1709
1709
|
const windowMs = ctx.config.circuitBreaker.windowMinutes * 60 * 1e3;
|
|
@@ -1733,7 +1733,7 @@ async function upsertCounter(ctx, _id, senderDomain, kind, counter2, by, windowM
|
|
|
1733
1733
|
trippedAt: null,
|
|
1734
1734
|
trippedReason: null,
|
|
1735
1735
|
manuallyResumedAt: null,
|
|
1736
|
-
rates: { ...
|
|
1736
|
+
rates: { ...EMPTY_RATES }
|
|
1737
1737
|
},
|
|
1738
1738
|
$set: { updatedAt: /* @__PURE__ */ new Date() }
|
|
1739
1739
|
},
|
|
@@ -1760,7 +1760,7 @@ async function evaluateHealth(ctx) {
|
|
|
1760
1760
|
windowStartedAt: /* @__PURE__ */ new Date(),
|
|
1761
1761
|
windowDurationMs: windowMs,
|
|
1762
1762
|
counters: { ...ZERO_COUNTERS },
|
|
1763
|
-
rates: { ...
|
|
1763
|
+
rates: { ...EMPTY_RATES },
|
|
1764
1764
|
status: "healthy",
|
|
1765
1765
|
updatedAt: /* @__PURE__ */ new Date()
|
|
1766
1766
|
}
|
|
@@ -1768,13 +1768,21 @@ async function evaluateHealth(ctx) {
|
|
|
1768
1768
|
);
|
|
1769
1769
|
continue;
|
|
1770
1770
|
}
|
|
1771
|
-
const c = doc.counters;
|
|
1772
|
-
const
|
|
1771
|
+
const c = { ...ZERO_COUNTERS, ...doc.counters };
|
|
1772
|
+
const perSent = (n) => c.sent > 0 ? n / c.sent : null;
|
|
1773
|
+
const attempts = c.sent + c.failedToSend;
|
|
1773
1774
|
const rates = {
|
|
1774
|
-
bounceRate: c.bounced
|
|
1775
|
-
hardBounceRate: c.hardBounced
|
|
1776
|
-
complaintRate: c.complained
|
|
1777
|
-
|
|
1775
|
+
bounceRate: perSent(c.bounced),
|
|
1776
|
+
hardBounceRate: perSent(c.hardBounced),
|
|
1777
|
+
complaintRate: perSent(c.complained),
|
|
1778
|
+
// Failed attempts over all attempts, so it stays within 0–100%.
|
|
1779
|
+
failureRate: attempts > 0 ? c.failedToSend / attempts : null
|
|
1780
|
+
};
|
|
1781
|
+
const pct2 = {
|
|
1782
|
+
bounce: (rates.bounceRate ?? 0) * 100,
|
|
1783
|
+
hardBounce: (rates.hardBounceRate ?? 0) * 100,
|
|
1784
|
+
complaint: (rates.complaintRate ?? 0) * 100,
|
|
1785
|
+
failure: (rates.failureRate ?? 0) * 100
|
|
1778
1786
|
};
|
|
1779
1787
|
await ctx.collections.health.updateOne(
|
|
1780
1788
|
{ _id: doc._id },
|
|
@@ -1784,12 +1792,12 @@ async function evaluateHealth(ctx) {
|
|
|
1784
1792
|
if (c.sent < cb.minSendsBeforeEval) continue;
|
|
1785
1793
|
if (doc.status === "tripped") continue;
|
|
1786
1794
|
let trippedReason = null;
|
|
1787
|
-
if (
|
|
1788
|
-
trippedReason = `hard bounce rate ${
|
|
1789
|
-
} else if (
|
|
1790
|
-
trippedReason = `complaint rate ${
|
|
1791
|
-
} else if (
|
|
1792
|
-
trippedReason = `combined bounce rate ${
|
|
1795
|
+
if (pct2.hardBounce >= cb.hardBounceRatePctTrip) {
|
|
1796
|
+
trippedReason = `hard bounce rate ${pct2.hardBounce.toFixed(2)}% >= ${cb.hardBounceRatePctTrip}%`;
|
|
1797
|
+
} else if (pct2.complaint >= cb.complaintRatePctTrip) {
|
|
1798
|
+
trippedReason = `complaint rate ${pct2.complaint.toFixed(2)}% >= ${cb.complaintRatePctTrip}%`;
|
|
1799
|
+
} else if (pct2.bounce >= cb.combinedBounceRatePctTrip) {
|
|
1800
|
+
trippedReason = `combined bounce rate ${pct2.bounce.toFixed(2)}% >= ${cb.combinedBounceRatePctTrip}%`;
|
|
1793
1801
|
}
|
|
1794
1802
|
if (trippedReason) {
|
|
1795
1803
|
const result = await ctx.collections.health.updateOne(
|
|
@@ -1824,7 +1832,7 @@ async function evaluateHealth(ctx) {
|
|
|
1824
1832
|
}
|
|
1825
1833
|
continue;
|
|
1826
1834
|
}
|
|
1827
|
-
if (
|
|
1835
|
+
if (pct2.failure >= cb.failedToSendRatePctDegrade) {
|
|
1828
1836
|
if (doc.status !== "degraded") {
|
|
1829
1837
|
await ctx.collections.health.updateOne(
|
|
1830
1838
|
{ _id: doc._id },
|
|
@@ -2786,6 +2794,10 @@ async function dispatchSend(sendId, ctx) {
|
|
|
2786
2794
|
await markFailed(send._id, "contact_missing", ctx);
|
|
2787
2795
|
return;
|
|
2788
2796
|
}
|
|
2797
|
+
if (!send.emailAtSend?.trim() || !contact.email?.trim()) {
|
|
2798
|
+
await markFailed(send._id, "no_recipient: the contact has no email address", ctx);
|
|
2799
|
+
return;
|
|
2800
|
+
}
|
|
2789
2801
|
const run = send.flowRunId ? await ctx.collections.flowRuns.findOne({ _id: send.flowRunId }) : null;
|
|
2790
2802
|
if (run && run.status === "exited" && run.exitReason?.startsWith("aborted_by_host")) {
|
|
2791
2803
|
await ctx.collections.sends.updateOne(
|
|
@@ -2982,6 +2994,10 @@ async function processOneRunStep(runId, ctx) {
|
|
|
2982
2994
|
await exitFlowRun(run, "contact_missing", ctx);
|
|
2983
2995
|
return;
|
|
2984
2996
|
}
|
|
2997
|
+
if (!contact.email?.trim()) {
|
|
2998
|
+
await exitFlowRun(run, "contact_no_email", ctx);
|
|
2999
|
+
return;
|
|
3000
|
+
}
|
|
2985
3001
|
const sub = await ctx.collections.subscriptions.findOne({ externalId: run.externalId });
|
|
2986
3002
|
if (sub && sub.status !== "subscribed") {
|
|
2987
3003
|
await exitFlowRun(run, sub.status, ctx);
|
|
@@ -3877,7 +3893,7 @@ async function runDnsblChecks(ctx, opts = {}) {
|
|
|
3877
3893
|
}
|
|
3878
3894
|
let listedCount = 0;
|
|
3879
3895
|
async function processPair(p) {
|
|
3880
|
-
const queryName = buildQueryName(p.target, p.targetKind, p.list.host);
|
|
3896
|
+
const queryName = buildQueryName(p.target, p.targetKind, spamhausQueryHost(p.list.host, cfg.spamhausDqsKey));
|
|
3881
3897
|
const lookup2 = queryName ? await queryDnsbl(resolver, queryName) : { result: "error", returnCodes: [], errorMessage: "unsupported target format" };
|
|
3882
3898
|
if (lookup2.transient) return;
|
|
3883
3899
|
if (lookup2.result === "listed") listedCount++;
|
|
@@ -3924,6 +3940,10 @@ function collectTargets(ctx, cfg) {
|
|
|
3924
3940
|
ips: (cfg.dedicatedIps ?? []).filter((ip) => net.isIP(ip) !== 0)
|
|
3925
3941
|
};
|
|
3926
3942
|
}
|
|
3943
|
+
function spamhausQueryHost(listHost, dqsKey) {
|
|
3944
|
+
const zone = dqsKey ? /^([a-z0-9-]+)\.spamhaus\.org$/i.exec(listHost)?.[1] : void 0;
|
|
3945
|
+
return zone ? `${dqsKey}.${zone}.dq.spamhaus.net` : listHost;
|
|
3946
|
+
}
|
|
3927
3947
|
function buildQueryName(target, kind, listHost) {
|
|
3928
3948
|
if (kind === "ip") {
|
|
3929
3949
|
const v = net.isIP(target);
|
|
@@ -3959,16 +3979,22 @@ async function queryDnsbl(resolver, query) {
|
|
|
3959
3979
|
};
|
|
3960
3980
|
}
|
|
3961
3981
|
}
|
|
3982
|
+
var REFUSAL_REASONS = {
|
|
3983
|
+
"127.255.255.252": "malformed query name for this list",
|
|
3984
|
+
"127.255.255.254": "queried through a public or shared DNS resolver, which Spamhaus blocks. Set dnsbl.spamhausDqsKey (free Spamhaus DQS key) or use a private resolver",
|
|
3985
|
+
"127.255.255.255": "this resolver has exceeded the free query limit"
|
|
3986
|
+
};
|
|
3962
3987
|
function interpretRecords(records) {
|
|
3963
3988
|
if (!records || records.length === 0) {
|
|
3964
3989
|
return { result: "clean", returnCodes: [], errorMessage: null };
|
|
3965
3990
|
}
|
|
3966
3991
|
const errorish = records.filter((r) => r.startsWith("127.255.255."));
|
|
3967
3992
|
if (errorish.length === records.length) {
|
|
3993
|
+
const reasons = [...new Set(records.map((r) => REFUSAL_REASONS[r] ?? "unrecognised refusal code"))];
|
|
3968
3994
|
return {
|
|
3969
3995
|
result: "error",
|
|
3970
3996
|
returnCodes: records,
|
|
3971
|
-
errorMessage: `list
|
|
3997
|
+
errorMessage: `not a listing \u2014 the list refused the query (${records.join(", ")}): ${reasons.join("; ")}`
|
|
3972
3998
|
};
|
|
3973
3999
|
}
|
|
3974
4000
|
const listed = records.filter((r) => r.startsWith("127.") && !r.startsWith("127.255.255."));
|
|
@@ -6418,7 +6444,7 @@ async function checkDnsbl(mailer) {
|
|
|
6418
6444
|
label: "DNS block-list checks",
|
|
6419
6445
|
severity: "warn",
|
|
6420
6446
|
message: `all ${docs.length} checks errored`,
|
|
6421
|
-
hint: "Lookups against block lists are failing \u2014 usually a DNS-resolution problem on the host running mailery, or a list
|
|
6447
|
+
hint: "Lookups against block lists are failing \u2014 usually a DNS-resolution problem on the host running mailery, or a list refusing your public resolver (Spamhaus answers 127.255.255.254; set dnsbl.spamhausDqsKey). Verify outbound DNS works."
|
|
6422
6448
|
};
|
|
6423
6449
|
}
|
|
6424
6450
|
return {
|
|
@@ -9528,7 +9554,7 @@ function unitToMs2(value, unit) {
|
|
|
9528
9554
|
}
|
|
9529
9555
|
|
|
9530
9556
|
// src/server/api/agent.ts
|
|
9531
|
-
var VERSION = "0.
|
|
9557
|
+
var VERSION = "0.19.0" ;
|
|
9532
9558
|
var broadcastCountSchema = z.object({
|
|
9533
9559
|
recipientCap: z.number().int().positive().max(1e7).nullable().optional()
|
|
9534
9560
|
});
|
|
@@ -11595,7 +11621,7 @@ var DEDUPE_POLICIES = [
|
|
|
11595
11621
|
];
|
|
11596
11622
|
|
|
11597
11623
|
// src/server/index.ts
|
|
11598
|
-
var VERSION2 = "0.
|
|
11624
|
+
var VERSION2 = "0.19.0" ;
|
|
11599
11625
|
|
|
11600
11626
|
export { DEDUPE_POLICIES, DEFAULT_BOT_UA_RE, FLOW_STEP_KINDS, FlowOperationError, MIN_AGENT_TOKEN_LENGTH, Mailer, MongoContactAdapter, NullProvider, PREDICATE_KINDS, RESERVED_VAR_KEYS, SEGMENT_FILTER_KINDS, SendGridProvider, TRACKING_SIG_LENGTH, VERSION2 as VERSION, applyTracking, applyWebhookEvent, armFlow, compileMailyTemplate, compileTemplate, computeDeliveryTime, createAdminApiRouter, createAdminRouter, createAgentRouter, createPublicRouter, defaultFlowStep, defaultPredicate, defaultSegmentFilter, defineVars, derivePlaintext, disarmFlow, dispatchSend, drainPendingUnsubscribes, ensureIndexes, gateFlow, getCollections, isCanaryGate, predicateKind, processNewlyFiredEventTriggers, processOneRunStep, referencedPaths, renderForContact, renderTemplate, runTick, sendgridInboundParser, sha256Hex, signTrackingToken, signUnsubscribeToken, simulateFlow, stampWatermarkIfNull, sweepStrandedFlowRuns, ungateFlow, validateSenderDomain, varsJsonSchema, verifyTemplate, verifyTrackingToken, verifyUnsubscribeToken };
|
|
11601
11627
|
//# sourceMappingURL=index.js.map
|