mailery 0.10.2 → 0.12.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.
@@ -236,7 +236,8 @@ interface SendArgs {
236
236
  fromEmail: string;
237
237
  replyTo?: string;
238
238
  subject: string;
239
- html: string;
239
+ /** Omitted for `text_only` templates — send the text part alone. */
240
+ html?: string;
240
241
  text: string;
241
242
  headers?: Record<string, string>;
242
243
  messageMeta?: Record<string, string>;
@@ -418,6 +419,19 @@ type QueueDriverConfig = {
418
419
  type SubscriptionStatus = 'subscribed' | 'pending_doi' | 'unsubscribed' | 'bounced' | 'complained';
419
420
  type SendStatus = 'queued' | 'sending' | 'sent' | 'delivered' | 'bounced' | 'complained' | 'failed' | 'suppressed' | 'cancelled';
420
421
  type TemplateKind = 'transactional' | 'marketing';
422
+ /**
423
+ * How a template's body goes on the wire.
424
+ *
425
+ * multipart — HTML + plain-text alternative (the default, and what almost
426
+ * every template wants).
427
+ * text_only — plain text alone, no HTML part at all. For mail that should
428
+ * read as if it were typed by a person. Open tracking is
429
+ * impossible without an HTML part to carry the pixel, and click
430
+ * tracking is skipped too (rewriting bare URLs in text produces
431
+ * the opaque redirect links this format exists to avoid), so a
432
+ * text_only send reports no opens and no clicks — by design.
433
+ */
434
+ type TemplateBodyFormat = 'multipart' | 'text_only';
421
435
  type SuppressionScope = 'all' | 'marketing' | 'transactional';
422
436
  type SuppressionReason = 'unsubscribed' | 'hard_bounce' | 'complaint' | 'manual' | 'list_cleaning' | 'gdpr_forget';
423
437
  type FlowRunStatus = 'active' | 'completed' | 'exited' | 'failed';
@@ -928,6 +942,8 @@ interface TemplateDoc {
928
942
  }>;
929
943
  draft: TemplateDraft | null;
930
944
  tags: string[];
945
+ /** Wire format for the body. Absent on documents written before 0.11. */
946
+ bodyFormat?: TemplateBodyFormat;
931
947
  trackOpens: boolean;
932
948
  trackClicks: boolean;
933
949
  stats: {
@@ -236,7 +236,8 @@ interface SendArgs {
236
236
  fromEmail: string;
237
237
  replyTo?: string;
238
238
  subject: string;
239
- html: string;
239
+ /** Omitted for `text_only` templates — send the text part alone. */
240
+ html?: string;
240
241
  text: string;
241
242
  headers?: Record<string, string>;
242
243
  messageMeta?: Record<string, string>;
@@ -418,6 +419,19 @@ type QueueDriverConfig = {
418
419
  type SubscriptionStatus = 'subscribed' | 'pending_doi' | 'unsubscribed' | 'bounced' | 'complained';
419
420
  type SendStatus = 'queued' | 'sending' | 'sent' | 'delivered' | 'bounced' | 'complained' | 'failed' | 'suppressed' | 'cancelled';
420
421
  type TemplateKind = 'transactional' | 'marketing';
422
+ /**
423
+ * How a template's body goes on the wire.
424
+ *
425
+ * multipart — HTML + plain-text alternative (the default, and what almost
426
+ * every template wants).
427
+ * text_only — plain text alone, no HTML part at all. For mail that should
428
+ * read as if it were typed by a person. Open tracking is
429
+ * impossible without an HTML part to carry the pixel, and click
430
+ * tracking is skipped too (rewriting bare URLs in text produces
431
+ * the opaque redirect links this format exists to avoid), so a
432
+ * text_only send reports no opens and no clicks — by design.
433
+ */
434
+ type TemplateBodyFormat = 'multipart' | 'text_only';
421
435
  type SuppressionScope = 'all' | 'marketing' | 'transactional';
422
436
  type SuppressionReason = 'unsubscribed' | 'hard_bounce' | 'complaint' | 'manual' | 'list_cleaning' | 'gdpr_forget';
423
437
  type FlowRunStatus = 'active' | 'completed' | 'exited' | 'failed';
@@ -928,6 +942,8 @@ interface TemplateDoc {
928
942
  }>;
929
943
  draft: TemplateDraft | null;
930
944
  tags: string[];
945
+ /** Wire format for the body. Absent on documents written before 0.11. */
946
+ bodyFormat?: TemplateBodyFormat;
931
947
  trackOpens: boolean;
932
948
  trackClicks: boolean;
933
949
  stats: {
package/dist/testing.cjs CHANGED
@@ -336,7 +336,10 @@ var init_sendgrid = __esm({
336
336
  replyTo: args.replyTo,
337
337
  subject: args.subject,
338
338
  text: args.text,
339
- html: args.html,
339
+ // Omit the key entirely rather than sending `html: undefined` — a
340
+ // text_only template must produce a single-part text/plain message, and
341
+ // an explicit undefined can still serialize into an empty HTML part.
342
+ ...args.html ? { html: args.html } : {},
340
343
  headers: args.headers,
341
344
  customArgs: args.messageMeta,
342
345
  trackingSettings: {
@@ -15856,7 +15859,8 @@ async function dispatchSend(sendId, ctx) {
15856
15859
  await markFailed(send._id, `render error: ${String(err?.message ?? err)}`, ctx);
15857
15860
  throw err;
15858
15861
  }
15859
- const tracking = applyTracking(rendered.html, {
15862
+ const textOnly = template.bodyFormat === "text_only";
15863
+ const tracking = textOnly ? { html: "", links: [] } : applyTracking(rendered.html, {
15860
15864
  sendId: String(send._id),
15861
15865
  publicUrl: ctx.config.publicUrl,
15862
15866
  trackOpens: template.trackOpens ?? ctx.config.trackOpens,
@@ -15868,7 +15872,9 @@ async function dispatchSend(sendId, ctx) {
15868
15872
  {
15869
15873
  $set: {
15870
15874
  links: tracking.links,
15871
- bodyHash: sha256(tracking.html),
15875
+ // Hash what actually goes out, so a text_only send's fingerprint
15876
+ // tracks the text body rather than an HTML part it never had.
15877
+ bodyHash: sha256(textOnly ? rendered.plainText : tracking.html),
15872
15878
  status: "sending",
15873
15879
  fromName: rendered.fromName,
15874
15880
  fromEmail: rendered.fromEmail,
@@ -15893,7 +15899,7 @@ async function dispatchSend(sendId, ctx) {
15893
15899
  fromEmail: rendered.fromEmail,
15894
15900
  replyTo: rendered.replyTo ?? void 0,
15895
15901
  subject: rendered.subject,
15896
- html: tracking.html,
15902
+ ...textOnly ? {} : { html: tracking.html },
15897
15903
  text: rendered.plainText,
15898
15904
  headers,
15899
15905
  messageMeta: { sendId: String(send._id) }
@@ -18131,6 +18137,7 @@ async function buildTemplate(spec) {
18131
18137
  variablesSchema: spec.variablesSchema ?? {},
18132
18138
  draft: null,
18133
18139
  tags: spec.tags ?? [],
18140
+ bodyFormat: spec.bodyFormat ?? "multipart",
18134
18141
  trackOpens: spec.trackOpens ?? false,
18135
18142
  trackClicks: spec.trackClicks ?? false,
18136
18143
  stats: {