mailery 0.10.1 → 0.11.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-BTww-4AA.js → index-l7m1PyMb.js} +2 -2
- package/dist/admin/spa/{index-BTww-4AA.js.map → index-l7m1PyMb.js.map} +1 -1
- package/dist/admin/spa/index.html +1 -1
- package/dist/admin/spa/{template-editor-CrOKLpSw.js → template-editor-pGcCFnJ7.js} +33 -33
- package/dist/admin/spa/{template-editor-CrOKLpSw.js.map → template-editor-pGcCFnJ7.js.map} +1 -1
- package/dist/index.cjs +29 -13
- 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 +29 -13
- package/dist/index.js.map +1 -1
- package/dist/{null-CU_EApC6.d.cts → null-CfzvqNEp.d.cts} +17 -1
- package/dist/{null-CU_EApC6.d.ts → null-CfzvqNEp.d.ts} +17 -1
- package/dist/testing.cjs +11 -4
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.d.cts +3 -2
- package/dist/testing.d.ts +3 -2
- package/dist/testing.js +11 -4
- 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, M as MailProvider, S as SendArgs, b as SendResult, c as MailTesterFeedback, d as Mailer, T as TemplateDoc, e as SuppressionScope, D as DeliveryWindow, F as FlowStep, f as SegmentFilter, P as Predicate } from './null-
|
|
2
|
-
export { g as AuditLogDoc, B as BroadcastDoc, h as BroadcastStatus, i as CircuitBreakerThresholds, j as Collections, k as ContactTagDoc, E as EventDoc, l as FlowDoc, m as FlowGoal, n as FlowRunDoc, o as FlowRunStatus, p as FlowVersionDoc, H as HealthDoc, q as HealthStatus, L as LeadDoc, r as MailerConfig, s as NullProvider, O as OutboxDoc, t as RESERVED_VAR_KEYS, u as RedisOptions, v as SegmentDefinition, w as SendDoc, x as SendStatus, y as SenderDomainConfig, z as SenderDomainRegistry, G as SenderDomainValidation, I as SubscriptionDoc, J as SubscriptionStatus, K as SuppressionDoc, Q as SuppressionReason, U as TemplateKind, V as TemplateVersionDoc, W as VarsAdapter, X as VarsResolveInfo, Y as WebhookEventDoc, Z as defineVars, _ as ensureIndexes, $ as getCollections, a0 as validateSenderDomain, a1 as varsJsonSchema } from './null-
|
|
1
|
+
import { R as RunnerContext, N as NormalizedEvent, C as ContactAdapter, a as Contact, A as AdapterFilter, M as MailProvider, S as SendArgs, b as SendResult, c as MailTesterFeedback, d as Mailer, T as TemplateDoc, e as SuppressionScope, D as DeliveryWindow, F as FlowStep, f as SegmentFilter, P as Predicate } from './null-CfzvqNEp.cjs';
|
|
2
|
+
export { g as AuditLogDoc, B as BroadcastDoc, h as BroadcastStatus, i as CircuitBreakerThresholds, j as Collections, k as ContactTagDoc, E as EventDoc, l as FlowDoc, m as FlowGoal, n as FlowRunDoc, o as FlowRunStatus, p as FlowVersionDoc, H as HealthDoc, q as HealthStatus, L as LeadDoc, r as MailerConfig, s as NullProvider, O as OutboxDoc, t as RESERVED_VAR_KEYS, u as RedisOptions, v as SegmentDefinition, w as SendDoc, x as SendStatus, y as SenderDomainConfig, z as SenderDomainRegistry, G as SenderDomainValidation, I as SubscriptionDoc, J as SubscriptionStatus, K as SuppressionDoc, Q as SuppressionReason, U as TemplateKind, V as TemplateVersionDoc, W as VarsAdapter, X as VarsResolveInfo, Y as WebhookEventDoc, Z as defineVars, _ as ensureIndexes, $ as getCollections, a0 as validateSenderDomain, a1 as varsJsonSchema } from './null-CfzvqNEp.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, M as MailProvider, S as SendArgs, b as SendResult, c as MailTesterFeedback, d as Mailer, T as TemplateDoc, e as SuppressionScope, D as DeliveryWindow, F as FlowStep, f as SegmentFilter, P as Predicate } from './null-
|
|
2
|
-
export { g as AuditLogDoc, B as BroadcastDoc, h as BroadcastStatus, i as CircuitBreakerThresholds, j as Collections, k as ContactTagDoc, E as EventDoc, l as FlowDoc, m as FlowGoal, n as FlowRunDoc, o as FlowRunStatus, p as FlowVersionDoc, H as HealthDoc, q as HealthStatus, L as LeadDoc, r as MailerConfig, s as NullProvider, O as OutboxDoc, t as RESERVED_VAR_KEYS, u as RedisOptions, v as SegmentDefinition, w as SendDoc, x as SendStatus, y as SenderDomainConfig, z as SenderDomainRegistry, G as SenderDomainValidation, I as SubscriptionDoc, J as SubscriptionStatus, K as SuppressionDoc, Q as SuppressionReason, U as TemplateKind, V as TemplateVersionDoc, W as VarsAdapter, X as VarsResolveInfo, Y as WebhookEventDoc, Z as defineVars, _ as ensureIndexes, $ as getCollections, a0 as validateSenderDomain, a1 as varsJsonSchema } from './null-
|
|
1
|
+
import { R as RunnerContext, N as NormalizedEvent, C as ContactAdapter, a as Contact, A as AdapterFilter, M as MailProvider, S as SendArgs, b as SendResult, c as MailTesterFeedback, d as Mailer, T as TemplateDoc, e as SuppressionScope, D as DeliveryWindow, F as FlowStep, f as SegmentFilter, P as Predicate } from './null-CfzvqNEp.js';
|
|
2
|
+
export { g as AuditLogDoc, B as BroadcastDoc, h as BroadcastStatus, i as CircuitBreakerThresholds, j as Collections, k as ContactTagDoc, E as EventDoc, l as FlowDoc, m as FlowGoal, n as FlowRunDoc, o as FlowRunStatus, p as FlowVersionDoc, H as HealthDoc, q as HealthStatus, L as LeadDoc, r as MailerConfig, s as NullProvider, O as OutboxDoc, t as RESERVED_VAR_KEYS, u as RedisOptions, v as SegmentDefinition, w as SendDoc, x as SendStatus, y as SenderDomainConfig, z as SenderDomainRegistry, G as SenderDomainValidation, I as SubscriptionDoc, J as SubscriptionStatus, K as SuppressionDoc, Q as SuppressionReason, U as TemplateKind, V as TemplateVersionDoc, W as VarsAdapter, X as VarsResolveInfo, Y as WebhookEventDoc, Z as defineVars, _ as ensureIndexes, $ as getCollections, a0 as validateSenderDomain, a1 as varsJsonSchema } from './null-CfzvqNEp.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
|
@@ -304,7 +304,10 @@ var init_sendgrid = __esm({
|
|
|
304
304
|
replyTo: args.replyTo,
|
|
305
305
|
subject: args.subject,
|
|
306
306
|
text: args.text,
|
|
307
|
-
html:
|
|
307
|
+
// Omit the key entirely rather than sending `html: undefined` — a
|
|
308
|
+
// text_only template must produce a single-part text/plain message, and
|
|
309
|
+
// an explicit undefined can still serialize into an empty HTML part.
|
|
310
|
+
...args.html ? { html: args.html } : {},
|
|
308
311
|
headers: args.headers,
|
|
309
312
|
customArgs: args.messageMeta,
|
|
310
313
|
trackingSettings: {
|
|
@@ -2143,7 +2146,8 @@ async function dispatchSend(sendId, ctx) {
|
|
|
2143
2146
|
await markFailed(send._id, `render error: ${String(err?.message ?? err)}`, ctx);
|
|
2144
2147
|
throw err;
|
|
2145
2148
|
}
|
|
2146
|
-
const
|
|
2149
|
+
const textOnly = template.bodyFormat === "text_only";
|
|
2150
|
+
const tracking = textOnly ? { html: "", links: [] } : applyTracking(rendered.html, {
|
|
2147
2151
|
sendId: String(send._id),
|
|
2148
2152
|
publicUrl: ctx.config.publicUrl,
|
|
2149
2153
|
trackOpens: template.trackOpens ?? ctx.config.trackOpens,
|
|
@@ -2155,7 +2159,9 @@ async function dispatchSend(sendId, ctx) {
|
|
|
2155
2159
|
{
|
|
2156
2160
|
$set: {
|
|
2157
2161
|
links: tracking.links,
|
|
2158
|
-
|
|
2162
|
+
// Hash what actually goes out, so a text_only send's fingerprint
|
|
2163
|
+
// tracks the text body rather than an HTML part it never had.
|
|
2164
|
+
bodyHash: sha256(textOnly ? rendered.plainText : tracking.html),
|
|
2159
2165
|
status: "sending",
|
|
2160
2166
|
fromName: rendered.fromName,
|
|
2161
2167
|
fromEmail: rendered.fromEmail,
|
|
@@ -2180,7 +2186,7 @@ async function dispatchSend(sendId, ctx) {
|
|
|
2180
2186
|
fromEmail: rendered.fromEmail,
|
|
2181
2187
|
replyTo: rendered.replyTo ?? void 0,
|
|
2182
2188
|
subject: rendered.subject,
|
|
2183
|
-
html: tracking.html,
|
|
2189
|
+
...textOnly ? {} : { html: tracking.html },
|
|
2184
2190
|
text: rendered.plainText,
|
|
2185
2191
|
headers,
|
|
2186
2192
|
messageMeta: { sendId: String(send._id) }
|
|
@@ -4811,12 +4817,15 @@ function isAllCaps(subject) {
|
|
|
4811
4817
|
// src/server/api/admin.ts
|
|
4812
4818
|
init_vars();
|
|
4813
4819
|
|
|
4820
|
+
// src/shared/enums.ts
|
|
4821
|
+
var TEMPLATE_BODY_FORMATS = ["multipart", "text_only"];
|
|
4822
|
+
|
|
4814
4823
|
// src/server/api/setup-status.ts
|
|
4815
4824
|
async function runSetupChecks(mailer) {
|
|
4816
4825
|
const checks = [];
|
|
4817
4826
|
checks.push(await checkMongo(mailer));
|
|
4818
4827
|
checks.push(await checkQueue(mailer));
|
|
4819
|
-
if (mailer.config.queue.driver !== "noop"
|
|
4828
|
+
if (mailer.config.queue.driver !== "noop") {
|
|
4820
4829
|
checks.push(await checkWorkersHeartbeat(mailer));
|
|
4821
4830
|
}
|
|
4822
4831
|
checks.push(await checkCircuitBreaker(mailer));
|
|
@@ -4881,13 +4890,14 @@ async function checkWorkersHeartbeat(mailer) {
|
|
|
4881
4890
|
);
|
|
4882
4891
|
const tickIntervalMs = mailer.config.tickIntervalSeconds * 1e3;
|
|
4883
4892
|
const staleAfterMs = Math.max(tickIntervalMs * 3, 3e4);
|
|
4893
|
+
const workerless = mailer.config.workerless;
|
|
4884
4894
|
if (!h) {
|
|
4885
4895
|
return {
|
|
4886
4896
|
name: "workers_heartbeat",
|
|
4887
4897
|
label: "Background workers",
|
|
4888
4898
|
severity: "warn",
|
|
4889
|
-
message: "no tick has run yet",
|
|
4890
|
-
hint: "If your separate worker process is started, the heartbeat will appear within one tick interval. If you forgot to run `mailer.startWorkers()`, sends will sit queued indefinitely."
|
|
4899
|
+
message: workerless ? "no tick has run yet (workers run in another process)" : "no tick has run yet",
|
|
4900
|
+
hint: workerless ? "This process is workerless; the heartbeat comes from your separate worker process. If that process is started, the heartbeat will appear within one tick interval \u2014 a fresh install legitimately has none yet." : "If your separate worker process is started, the heartbeat will appear within one tick interval. If you forgot to run `mailer.startWorkers()`, sends will sit queued indefinitely."
|
|
4891
4901
|
};
|
|
4892
4902
|
}
|
|
4893
4903
|
const ageMs = Date.now() - new Date(h.updatedAt).getTime();
|
|
@@ -4897,14 +4907,14 @@ async function checkWorkersHeartbeat(mailer) {
|
|
|
4897
4907
|
label: "Background workers",
|
|
4898
4908
|
severity: "error",
|
|
4899
4909
|
message: `last tick ${humanDuration(ageMs)} ago (expected within ${humanDuration(tickIntervalMs)})`,
|
|
4900
|
-
hint: "Workers appear to be down. Sends and flow advancement are halted. Restart your worker process (`mailer.startWorkers()`)."
|
|
4910
|
+
hint: workerless ? "Workers run in another process, and that process appears to be down. Sends and flow advancement are halted. Restart the process that calls `mailer.startWorkers()`." : "Workers appear to be down. Sends and flow advancement are halted. Restart your worker process (`mailer.startWorkers()`)."
|
|
4901
4911
|
};
|
|
4902
4912
|
}
|
|
4903
4913
|
return {
|
|
4904
4914
|
name: "workers_heartbeat",
|
|
4905
4915
|
label: "Background workers",
|
|
4906
4916
|
severity: "ok",
|
|
4907
|
-
message: `last tick ${humanDuration(ageMs)} ago`
|
|
4917
|
+
message: `last tick ${humanDuration(ageMs)} ago${workerless ? " (worker process)" : ""}`
|
|
4908
4918
|
};
|
|
4909
4919
|
}
|
|
4910
4920
|
async function checkCircuitBreaker(mailer) {
|
|
@@ -6469,6 +6479,7 @@ function apiRouter(mailer, opts = {}) {
|
|
|
6469
6479
|
lastModifiedAt: now
|
|
6470
6480
|
},
|
|
6471
6481
|
tags: [],
|
|
6482
|
+
bodyFormat: "multipart",
|
|
6472
6483
|
trackOpens: kind === "marketing",
|
|
6473
6484
|
trackClicks: kind === "marketing",
|
|
6474
6485
|
stats: { sent: 0, delivered: 0, opened: 0, clicked: 0, bounced: 0, complained: 0, unsubscribed: 0, lastSentAt: null },
|
|
@@ -6494,7 +6505,7 @@ function apiRouter(mailer, opts = {}) {
|
|
|
6494
6505
|
asyncHandler(async (req, res) => {
|
|
6495
6506
|
const tpl = await c.templates.findOne({ slug: req.params.slug });
|
|
6496
6507
|
if (!tpl) return res.status(404).json({ error: "not_found" });
|
|
6497
|
-
const { subject, preheader, mjml, editorJson, notes, name, fromName, fromEmail, replyTo, kind, trackOpens, trackClicks } = req.body ?? {};
|
|
6508
|
+
const { subject, preheader, mjml, editorJson, notes, name, fromName, fromEmail, replyTo, kind, bodyFormat, trackOpens, trackClicks } = req.body ?? {};
|
|
6498
6509
|
const set = {
|
|
6499
6510
|
"draft.lastModifiedBy": req.actor,
|
|
6500
6511
|
"draft.lastModifiedAt": /* @__PURE__ */ new Date(),
|
|
@@ -6522,6 +6533,7 @@ function apiRouter(mailer, opts = {}) {
|
|
|
6522
6533
|
});
|
|
6523
6534
|
}
|
|
6524
6535
|
}
|
|
6536
|
+
if (TEMPLATE_BODY_FORMATS.includes(bodyFormat)) set.bodyFormat = bodyFormat;
|
|
6525
6537
|
if (typeof trackOpens === "boolean") set.trackOpens = trackOpens;
|
|
6526
6538
|
if (typeof trackClicks === "boolean") set.trackClicks = trackClicks;
|
|
6527
6539
|
await c.templates.updateOne({ _id: tpl._id }, { $set: set });
|
|
@@ -6607,7 +6619,9 @@ function apiRouter(mailer, opts = {}) {
|
|
|
6607
6619
|
fromEmail: tpl.fromEmail,
|
|
6608
6620
|
replyTo: tpl.replyTo ?? void 0,
|
|
6609
6621
|
subject: draft.subject,
|
|
6610
|
-
|
|
6622
|
+
// Match the real send shape — a text_only template's deliverability
|
|
6623
|
+
// should be scored on the single-part message it actually sends.
|
|
6624
|
+
...tpl.bodyFormat === "text_only" ? {} : { html: compiled.html },
|
|
6611
6625
|
text: compiled.plainText,
|
|
6612
6626
|
headers: {},
|
|
6613
6627
|
messageMeta: { mailTesterCheckId: checkId }
|
|
@@ -6952,7 +6966,9 @@ function apiRouter(mailer, opts = {}) {
|
|
|
6952
6966
|
fromName: rendered.fromName,
|
|
6953
6967
|
fromEmail: rendered.fromEmail,
|
|
6954
6968
|
subject: `[TEST] ${rendered.subject}`,
|
|
6955
|
-
|
|
6969
|
+
// Match the real send shape, or a test of a text_only template would
|
|
6970
|
+
// arrive as HTML and hide exactly what the author is checking.
|
|
6971
|
+
...tpl.bodyFormat === "text_only" ? {} : { html: tracking.html },
|
|
6956
6972
|
text: rendered.plainText
|
|
6957
6973
|
});
|
|
6958
6974
|
await mailer.audit({
|
|
@@ -7592,7 +7608,7 @@ var DEDUPE_POLICIES = [
|
|
|
7592
7608
|
];
|
|
7593
7609
|
|
|
7594
7610
|
// src/server/index.ts
|
|
7595
|
-
var VERSION = "0.
|
|
7611
|
+
var VERSION = "0.11.0" ;
|
|
7596
7612
|
|
|
7597
7613
|
export { DEDUPE_POLICIES, FLOW_STEP_KINDS, Mailer, MongoContactAdapter, NullProvider, PREDICATE_KINDS, RESERVED_VAR_KEYS, SEGMENT_FILTER_KINDS, SendGridProvider, VERSION, applyTracking, applyWebhookEvent, compileMailyTemplate, compileTemplate, computeDeliveryTime, createAdminRouter, createPublicRouter, defaultFlowStep, defaultPredicate, defaultSegmentFilter, defineVars, derivePlaintext, dispatchSend, ensureIndexes, getCollections, predicateKind, processNewlyFiredEventTriggers, processOneRunStep, renderTemplate, runTick, sha256Hex, signUnsubscribeToken, sweepStrandedFlowRuns, validateSenderDomain, varsJsonSchema, verifyUnsubscribeToken };
|
|
7598
7614
|
//# sourceMappingURL=index.js.map
|