mailery 0.7.0 → 0.8.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
@@ -1932,6 +1932,10 @@ async function dispatchSend(sendId, ctx) {
1932
1932
  await markFailed(send._id, `provider_unknown: ${send.provider}`, ctx);
1933
1933
  return;
1934
1934
  }
1935
+ const headers = send.kind === "marketing" ? {
1936
+ "List-Unsubscribe": `<${renderCtx.unsubscribeUrl}>`,
1937
+ "List-Unsubscribe-Post": "List-Unsubscribe=One-Click"
1938
+ } : {};
1935
1939
  try {
1936
1940
  const result = await provider.send({
1937
1941
  to: send.emailAtSend,
@@ -1941,10 +1945,7 @@ async function dispatchSend(sendId, ctx) {
1941
1945
  subject: rendered.subject,
1942
1946
  html: tracking.html,
1943
1947
  text: rendered.plainText,
1944
- headers: {
1945
- "List-Unsubscribe": `<${renderCtx.unsubscribeUrl}>`,
1946
- "List-Unsubscribe-Post": "List-Unsubscribe=One-Click"
1947
- },
1948
+ headers,
1948
1949
  messageMeta: { sendId: String(send._id) }
1949
1950
  });
1950
1951
  await ctx.collections.sends.updateOne(
@@ -5071,7 +5072,7 @@ async function checkPostalAddress(mailer) {
5071
5072
  label: "CAN-SPAM postal address",
5072
5073
  severity: "warn",
5073
5074
  message: `${marketingCount} published marketing template${marketingCount === 1 ? "" : "s"} but senderAddress is unset`,
5074
- hint: "CAN-SPAM requires a postal address in marketing emails. Set `senderAddress` in your Mailer config and reference it via `{{senderAddress}}` in your templates."
5075
+ hint: "CAN-SPAM requires a postal address in marketing emails. Set `senderAddress` in your Mailer config and reference the `{{senderAddress}}` render variable in your templates."
5075
5076
  };
5076
5077
  }
5077
5078
  async function checkDoiTemplate(mailer) {
@@ -5365,19 +5366,30 @@ async function persistScore(ctx, input) {
5365
5366
  }
5366
5367
  async function evaluateMailTesterGate(ctx, input) {
5367
5368
  const cfg = ctx.config.mailTester;
5368
- if (!cfg?.apiKey) return { allowed: true, reason: null, score: null };
5369
+ if (!cfg?.apiKey) return { allowed: true, reason: null, score: null, code: null };
5369
5370
  const minScore = cfg.minScore ?? 8;
5370
5371
  const key = mailTesterContentKey(input);
5371
5372
  const cached = await findCachedScore(ctx, key);
5372
- if (!cached) return { allowed: true, reason: null, score: null };
5373
+ if (!cached) {
5374
+ if (cfg.requireScore) {
5375
+ return {
5376
+ allowed: false,
5377
+ reason: "No Mail-Tester score for this exact content. Run a deliverability check before publishing.",
5378
+ score: null,
5379
+ code: "no_score"
5380
+ };
5381
+ }
5382
+ return { allowed: true, reason: null, score: null, code: null };
5383
+ }
5373
5384
  if (cached.score < minScore) {
5374
5385
  return {
5375
5386
  allowed: false,
5376
5387
  reason: `Mail-Tester score ${cached.score.toFixed(1)} is below minimum ${minScore.toFixed(1)}`,
5377
- score: cached
5388
+ score: cached,
5389
+ code: "low_score"
5378
5390
  };
5379
5391
  }
5380
- return { allowed: true, reason: null, score: cached };
5392
+ return { allowed: true, reason: null, score: cached, code: null };
5381
5393
  }
5382
5394
 
5383
5395
  // src/server/api/admin.ts
@@ -6371,6 +6383,7 @@ function apiRouter(mailer, opts = {}) {
6371
6383
  res.json({
6372
6384
  configured,
6373
6385
  minScore: cfg?.minScore ?? 8,
6386
+ requireScore: !!cfg?.requireScore,
6374
6387
  cacheHours: cfg?.cacheHours ?? 24,
6375
6388
  score: cached
6376
6389
  });
@@ -6585,9 +6598,10 @@ function apiRouter(mailer, opts = {}) {
6585
6598
  if (!gate.allowed) {
6586
6599
  return res.status(422).json({
6587
6600
  error: "mail_tester_blocked",
6601
+ code: gate.code,
6588
6602
  message: gate.reason,
6589
6603
  score: gate.score,
6590
- hint: "Re-run the deliverability check after fixing the feedback, or POST `bypassMailTester: true` to publish anyway."
6604
+ hint: gate.code === "no_score" ? "POST to `/templates/:slug/mail-tester-check`, poll `/mail-tester-result`, then publish \u2014 or POST `bypassMailTester: true` to publish anyway." : "Re-run the deliverability check after fixing the feedback, or POST `bypassMailTester: true` to publish anyway."
6591
6605
  });
6592
6606
  }
6593
6607
  }
@@ -7395,7 +7409,7 @@ var DEDUPE_POLICIES = [
7395
7409
  ];
7396
7410
 
7397
7411
  // src/server/index.ts
7398
- var VERSION = "0.7.0";
7412
+ var VERSION = "0.8.0";
7399
7413
 
7400
7414
  exports.DEDUPE_POLICIES = DEDUPE_POLICIES;
7401
7415
  exports.FLOW_STEP_KINDS = FLOW_STEP_KINDS;