mailery 0.19.0 → 0.20.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
@@ -2500,7 +2500,7 @@ function derivePlaintext(html) {
2500
2500
  var PLAIN_TEXT = { noEscape: true };
2501
2501
  async function renderTemplate(template, ctx, opts = {}) {
2502
2502
  const hb = makeHandlebars(opts.helpers);
2503
- const subject = hb.compile(template.subject)(ctx);
2503
+ const subject = hb.compile(template.subject, PLAIN_TEXT)(ctx).replace(/[\r\n]+/g, " ");
2504
2504
  const preheader = hb.compile(template.preheader)(ctx);
2505
2505
  let html;
2506
2506
  if (template.body.html) {
@@ -3912,7 +3912,7 @@ async function runDnsblChecks(ctx, opts = {}) {
3912
3912
  let listedCount = 0;
3913
3913
  async function processPair(p) {
3914
3914
  const queryName = buildQueryName(p.target, p.targetKind, spamhausQueryHost(p.list.host, cfg.spamhausDqsKey));
3915
- const lookup2 = queryName ? await queryDnsbl(resolver, queryName) : { result: "error", returnCodes: [], errorMessage: "unsupported target format" };
3915
+ const lookup2 = queryName ? await queryDnsbl(resolver, queryName, p.list.host) : { result: "error", returnCodes: [], errorMessage: "unsupported target format" };
3916
3916
  if (lookup2.transient) return;
3917
3917
  if (lookup2.result === "listed") listedCount++;
3918
3918
  await ctx.collections.dnsblChecks.updateOne(
@@ -3973,10 +3973,10 @@ function buildQueryName(target, kind, listHost) {
3973
3973
  return `${domain}.${listHost}`;
3974
3974
  }
3975
3975
  var TRANSIENT_DNS_CODES = /* @__PURE__ */ new Set(["ESERVFAIL", "EREFUSED", "ETIMEOUT", "ETIMEDOUT", "ECONNRESET", "ECONNREFUSED"]);
3976
- async function queryDnsbl(resolver, query) {
3976
+ async function queryDnsbl(resolver, query, listHost) {
3977
3977
  try {
3978
3978
  const records = await resolver.resolve4(query);
3979
- return interpretRecords(records);
3979
+ return interpretRecords(records, listHost);
3980
3980
  } catch (err) {
3981
3981
  const code = err?.code;
3982
3982
  if (code === "ENOTFOUND" || code === "ENODATA") {
@@ -4002,10 +4002,17 @@ var REFUSAL_REASONS = {
4002
4002
  "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",
4003
4003
  "127.255.255.255": "this resolver has exceeded the free query limit"
4004
4004
  };
4005
- function interpretRecords(records) {
4005
+ function interpretRecords(records, listHost) {
4006
4006
  if (!records || records.length === 0) {
4007
4007
  return { result: "clean", returnCodes: [], errorMessage: null };
4008
4008
  }
4009
+ if (listHost && REGISTRABLE_ONLY_LISTS.has(listHost) && records.every((r) => r === "127.0.0.1")) {
4010
+ return {
4011
+ result: "error",
4012
+ returnCodes: records,
4013
+ errorMessage: `${listHost} refused the query (127.0.0.1): queried through a public or shared DNS resolver, or over the free query limit. Use a private resolver`
4014
+ };
4015
+ }
4009
4016
  const errorish = records.filter((r) => r.startsWith("127.255.255."));
4010
4017
  if (errorish.length === records.length) {
4011
4018
  const reasons = [...new Set(records.map((r) => REFUSAL_REASONS[r] ?? "unrecognised refusal code"))];
@@ -5141,6 +5148,7 @@ async function processScheduledBroadcasts2(ctx) {
5141
5148
  }
5142
5149
 
5143
5150
  // src/server/mailer.ts
5151
+ var STORAGE_REQUIRED = "Mailer.init: give exactly one of `db` (an open Db) or `mongo` (a connection mailery opens)";
5144
5152
  var Mailer = class _Mailer {
5145
5153
  db;
5146
5154
  collections;
@@ -5150,6 +5158,8 @@ var Mailer = class _Mailer {
5150
5158
  config;
5151
5159
  events;
5152
5160
  queueDriver;
5161
+ /** The client `Mailer.init` opened from `config.mongo`; closed by `stop()`. Null when the host gave `db`. */
5162
+ ownedClient;
5153
5163
  workersStarted = false;
5154
5164
  runnerContext;
5155
5165
  constructor(args) {
@@ -5159,6 +5169,7 @@ var Mailer = class _Mailer {
5159
5169
  this.adapter = args.adapter;
5160
5170
  this.providers = args.providers;
5161
5171
  this.queueDriver = args.queueDriver;
5172
+ this.ownedClient = args.ownedClient;
5162
5173
  this.queues = args.queueDriver.queues;
5163
5174
  this.events = args.events;
5164
5175
  this.runnerContext = {
@@ -5255,36 +5266,58 @@ var Mailer = class _Mailer {
5255
5266
  });
5256
5267
  }
5257
5268
  static async init(input) {
5258
- const config = resolveConfig(input);
5259
- if (!resolveProvider(config.providers, config.defaultProvider)) {
5269
+ if (input.db == null === (input.mongo == null)) throw new Error(STORAGE_REQUIRED);
5270
+ if (!resolveProvider(input.providers, input.defaultProvider)) {
5260
5271
  throw new Error(
5261
- `defaultProvider "${config.defaultProvider}" is not a registered provider. Registered: ${registeredProviderNames(config.providers).join(", ") || "(none)"}`
5272
+ `defaultProvider "${input.defaultProvider}" is not a registered provider. Registered: ${registeredProviderNames(input.providers).join(", ") || "(none)"}`
5262
5273
  );
5263
5274
  }
5264
- if (config.defaultTransactionalProvider != null && !resolveProvider(config.providers, config.defaultTransactionalProvider)) {
5275
+ if (input.defaultTransactionalProvider != null && !resolveProvider(input.providers, input.defaultTransactionalProvider)) {
5265
5276
  throw new Error(
5266
- `defaultTransactionalProvider "${config.defaultTransactionalProvider}" is not a registered provider. Registered: ${registeredProviderNames(config.providers).join(", ") || "(none)"}`
5277
+ `defaultTransactionalProvider "${input.defaultTransactionalProvider}" is not a registered provider. Registered: ${registeredProviderNames(input.providers).join(", ") || "(none)"}`
5267
5278
  );
5268
5279
  }
5269
- if (config.varsAdapter) {
5280
+ if (input.varsAdapter) {
5270
5281
  const { assertNoReservedVarKeys: assertNoReservedVarKeys2 } = await Promise.resolve().then(() => (init_vars(), vars_exports));
5271
- assertNoReservedVarKeys2(config.varsAdapter);
5272
- }
5273
- const collections = getCollections(config.db, config.collectionPrefix);
5274
- await ensureIndexes(config.db, config.collectionPrefix);
5275
- const queueDriver = await createQueueDriver(config.queue, config.db);
5276
- if (!config.workerless && config.queue.driver !== "noop") {
5277
- await queueDriver.scheduleRepeatingTick(config.tickIntervalSeconds);
5278
- }
5279
- return new _Mailer({
5280
- config,
5281
- db: config.db,
5282
- collections,
5283
- adapter: config.adapter,
5284
- providers: config.providers,
5285
- queueDriver,
5286
- events: new EventRegistry()
5287
- });
5282
+ assertNoReservedVarKeys2(input.varsAdapter);
5283
+ }
5284
+ let ownedClient = null;
5285
+ let db;
5286
+ if (input.mongo) {
5287
+ const { MongoClient } = await import('mongodb');
5288
+ ownedClient = await MongoClient.connect(input.mongo.uri, input.mongo.clientOptions);
5289
+ db = ownedClient.db(input.mongo.dbName);
5290
+ } else if (input.db) {
5291
+ db = input.db;
5292
+ } else {
5293
+ throw new Error(STORAGE_REQUIRED);
5294
+ }
5295
+ try {
5296
+ const config = resolveConfig({
5297
+ ...input,
5298
+ db,
5299
+ adapter: typeof input.adapter === "function" ? input.adapter(db) : input.adapter
5300
+ });
5301
+ const collections = getCollections(config.db, config.collectionPrefix);
5302
+ await ensureIndexes(config.db, config.collectionPrefix);
5303
+ const queueDriver = await createQueueDriver(config.queue, config.db);
5304
+ if (!config.workerless && config.queue.driver !== "noop") {
5305
+ await queueDriver.scheduleRepeatingTick(config.tickIntervalSeconds);
5306
+ }
5307
+ return new _Mailer({
5308
+ config,
5309
+ db: config.db,
5310
+ collections,
5311
+ adapter: config.adapter,
5312
+ providers: config.providers,
5313
+ queueDriver,
5314
+ events: new EventRegistry(),
5315
+ ownedClient
5316
+ });
5317
+ } catch (err) {
5318
+ await ownedClient?.close();
5319
+ throw err;
5320
+ }
5288
5321
  }
5289
5322
  // -------------------------------------------------------------------------
5290
5323
  // Event registration + firing
@@ -5757,6 +5790,8 @@ var Mailer = class _Mailer {
5757
5790
  async stop() {
5758
5791
  await this.queueDriver.close();
5759
5792
  this.workersStarted = false;
5793
+ await this.ownedClient?.close();
5794
+ this.ownedClient = null;
5760
5795
  }
5761
5796
  /** Used internally by the admin router and tests; not part of the public API. */
5762
5797
  getRunnerContext() {
@@ -9572,7 +9607,7 @@ function unitToMs2(value, unit) {
9572
9607
  }
9573
9608
 
9574
9609
  // src/server/api/agent.ts
9575
- var VERSION = "0.19.0" ;
9610
+ var VERSION = "0.20.0" ;
9576
9611
  var broadcastCountSchema = zod.z.object({
9577
9612
  recipientCap: zod.z.number().int().positive().max(1e7).nullable().optional()
9578
9613
  });
@@ -11639,7 +11674,7 @@ var DEDUPE_POLICIES = [
11639
11674
  ];
11640
11675
 
11641
11676
  // src/server/index.ts
11642
- var VERSION2 = "0.19.0" ;
11677
+ var VERSION2 = "0.20.0" ;
11643
11678
 
11644
11679
  exports.DEDUPE_POLICIES = DEDUPE_POLICIES;
11645
11680
  exports.DEFAULT_BOT_UA_RE = DEFAULT_BOT_UA_RE;