mailery 0.10.2 → 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/index.cjs CHANGED
@@ -322,7 +322,10 @@ var init_sendgrid = __esm({
322
322
  replyTo: args.replyTo,
323
323
  subject: args.subject,
324
324
  text: args.text,
325
- html: args.html,
325
+ // Omit the key entirely rather than sending `html: undefined` — a
326
+ // text_only template must produce a single-part text/plain message, and
327
+ // an explicit undefined can still serialize into an empty HTML part.
328
+ ...args.html ? { html: args.html } : {},
326
329
  headers: args.headers,
327
330
  customArgs: args.messageMeta,
328
331
  trackingSettings: {
@@ -2161,7 +2164,8 @@ async function dispatchSend(sendId, ctx) {
2161
2164
  await markFailed(send._id, `render error: ${String(err?.message ?? err)}`, ctx);
2162
2165
  throw err;
2163
2166
  }
2164
- const tracking = applyTracking(rendered.html, {
2167
+ const textOnly = template.bodyFormat === "text_only";
2168
+ const tracking = textOnly ? { html: "", links: [] } : applyTracking(rendered.html, {
2165
2169
  sendId: String(send._id),
2166
2170
  publicUrl: ctx.config.publicUrl,
2167
2171
  trackOpens: template.trackOpens ?? ctx.config.trackOpens,
@@ -2173,7 +2177,9 @@ async function dispatchSend(sendId, ctx) {
2173
2177
  {
2174
2178
  $set: {
2175
2179
  links: tracking.links,
2176
- bodyHash: sha256(tracking.html),
2180
+ // Hash what actually goes out, so a text_only send's fingerprint
2181
+ // tracks the text body rather than an HTML part it never had.
2182
+ bodyHash: sha256(textOnly ? rendered.plainText : tracking.html),
2177
2183
  status: "sending",
2178
2184
  fromName: rendered.fromName,
2179
2185
  fromEmail: rendered.fromEmail,
@@ -2198,7 +2204,7 @@ async function dispatchSend(sendId, ctx) {
2198
2204
  fromEmail: rendered.fromEmail,
2199
2205
  replyTo: rendered.replyTo ?? void 0,
2200
2206
  subject: rendered.subject,
2201
- html: tracking.html,
2207
+ ...textOnly ? {} : { html: tracking.html },
2202
2208
  text: rendered.plainText,
2203
2209
  headers,
2204
2210
  messageMeta: { sendId: String(send._id) }
@@ -4829,6 +4835,9 @@ function isAllCaps(subject) {
4829
4835
  // src/server/api/admin.ts
4830
4836
  init_vars();
4831
4837
 
4838
+ // src/shared/enums.ts
4839
+ var TEMPLATE_BODY_FORMATS = ["multipart", "text_only"];
4840
+
4832
4841
  // src/server/api/setup-status.ts
4833
4842
  async function runSetupChecks(mailer) {
4834
4843
  const checks = [];
@@ -6488,6 +6497,7 @@ function apiRouter(mailer, opts = {}) {
6488
6497
  lastModifiedAt: now
6489
6498
  },
6490
6499
  tags: [],
6500
+ bodyFormat: "multipart",
6491
6501
  trackOpens: kind === "marketing",
6492
6502
  trackClicks: kind === "marketing",
6493
6503
  stats: { sent: 0, delivered: 0, opened: 0, clicked: 0, bounced: 0, complained: 0, unsubscribed: 0, lastSentAt: null },
@@ -6513,7 +6523,7 @@ function apiRouter(mailer, opts = {}) {
6513
6523
  asyncHandler(async (req, res) => {
6514
6524
  const tpl = await c.templates.findOne({ slug: req.params.slug });
6515
6525
  if (!tpl) return res.status(404).json({ error: "not_found" });
6516
- const { subject, preheader, mjml, editorJson, notes, name, fromName, fromEmail, replyTo, kind, trackOpens, trackClicks } = req.body ?? {};
6526
+ const { subject, preheader, mjml, editorJson, notes, name, fromName, fromEmail, replyTo, kind, bodyFormat, trackOpens, trackClicks } = req.body ?? {};
6517
6527
  const set = {
6518
6528
  "draft.lastModifiedBy": req.actor,
6519
6529
  "draft.lastModifiedAt": /* @__PURE__ */ new Date(),
@@ -6541,6 +6551,7 @@ function apiRouter(mailer, opts = {}) {
6541
6551
  });
6542
6552
  }
6543
6553
  }
6554
+ if (TEMPLATE_BODY_FORMATS.includes(bodyFormat)) set.bodyFormat = bodyFormat;
6544
6555
  if (typeof trackOpens === "boolean") set.trackOpens = trackOpens;
6545
6556
  if (typeof trackClicks === "boolean") set.trackClicks = trackClicks;
6546
6557
  await c.templates.updateOne({ _id: tpl._id }, { $set: set });
@@ -6626,7 +6637,9 @@ function apiRouter(mailer, opts = {}) {
6626
6637
  fromEmail: tpl.fromEmail,
6627
6638
  replyTo: tpl.replyTo ?? void 0,
6628
6639
  subject: draft.subject,
6629
- html: compiled.html,
6640
+ // Match the real send shape — a text_only template's deliverability
6641
+ // should be scored on the single-part message it actually sends.
6642
+ ...tpl.bodyFormat === "text_only" ? {} : { html: compiled.html },
6630
6643
  text: compiled.plainText,
6631
6644
  headers: {},
6632
6645
  messageMeta: { mailTesterCheckId: checkId }
@@ -6971,7 +6984,9 @@ function apiRouter(mailer, opts = {}) {
6971
6984
  fromName: rendered.fromName,
6972
6985
  fromEmail: rendered.fromEmail,
6973
6986
  subject: `[TEST] ${rendered.subject}`,
6974
- html: tracking.html,
6987
+ // Match the real send shape, or a test of a text_only template would
6988
+ // arrive as HTML and hide exactly what the author is checking.
6989
+ ...tpl.bodyFormat === "text_only" ? {} : { html: tracking.html },
6975
6990
  text: rendered.plainText
6976
6991
  });
6977
6992
  await mailer.audit({
@@ -7611,7 +7626,7 @@ var DEDUPE_POLICIES = [
7611
7626
  ];
7612
7627
 
7613
7628
  // src/server/index.ts
7614
- var VERSION = "0.10.2" ;
7629
+ var VERSION = "0.11.0" ;
7615
7630
 
7616
7631
  exports.DEDUPE_POLICIES = DEDUPE_POLICIES;
7617
7632
  exports.FLOW_STEP_KINDS = FLOW_STEP_KINDS;