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.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { R as RunnerContext, N as NormalizedEvent, C as ContactAdapter, a as Contact, A as AdapterFilter, b as AdapterSort, M as MailProvider, S as SendArgs, c as SendResult, d as MailTesterFeedback, e as Mailer, T as TemplateDoc, F as FlowStep, f as Collections, g as FlowDoc, h as SuppressionScope, D as DeliveryWindow, i as SegmentFilter, P as Predicate } from './null-DxTHQSrJ.cjs';
2
- export { j as AuditLogDoc, B as BotFilterConfig, k as BroadcastDoc, l as BroadcastOrder, m as BroadcastPauseReason, n as BroadcastStatus, o as CircuitBreakerThresholds, p as ContactTagDoc, E as EventDoc, q as FlowGoal, r as FlowRunDoc, s as FlowRunStatus, t as FlowVersionDoc, H as HealthDoc, u as HealthStatus, L as LeadDoc, v as MailerConfig, w as NullProvider, O as OutboxDoc, x as RESERVED_VAR_KEYS, y as RedisOptions, z as SegmentDefinition, G as SendDoc, I as SendStatus, J as SenderDomainConfig, K as SenderDomainRegistry, Q as SenderDomainValidation, U as SubscriptionDoc, V as SubscriptionStatus, W as SuppressionDoc, X as SuppressionReason, Y as TemplateKind, Z as TemplateVersionDoc, _ as VarsAdapter, $ as VarsResolveInfo, a0 as WebhookEventDoc, a1 as defineVars, a2 as ensureIndexes, a3 as getCollections, a4 as validateSenderDomain, a5 as varsJsonSchema } from './null-DxTHQSrJ.cjs';
1
+ import { R as RunnerContext, N as NormalizedEvent, C as ContactAdapter, a as Contact, A as AdapterFilter, b as AdapterSort, M as MailProvider, S as SendArgs, c as SendResult, d as MailTesterFeedback, e as Mailer, T as TemplateDoc, F as FlowStep, f as Collections, g as FlowDoc, h as SuppressionScope, D as DeliveryWindow, i as SegmentFilter, P as Predicate } from './null-B3N6bTpc.cjs';
2
+ export { j as AuditLogDoc, B as BotFilterConfig, k as BroadcastDoc, l as BroadcastOrder, m as BroadcastPauseReason, n as BroadcastStatus, o as CircuitBreakerThresholds, p as ContactAdapterFactory, q as ContactTagDoc, E as EventDoc, r as FlowGoal, s as FlowRunDoc, t as FlowRunStatus, u as FlowVersionDoc, H as HealthDoc, v as HealthStatus, L as LeadDoc, w as MailerConfig, x as MailerMongoConnection, y as NullProvider, O as OutboxDoc, z as RESERVED_VAR_KEYS, G as RedisOptions, I as SegmentDefinition, J as SendDoc, K as SendStatus, Q as SenderDomainConfig, U as SenderDomainRegistry, V as SenderDomainValidation, W as SubscriptionDoc, X as SubscriptionStatus, Y as SuppressionDoc, Z as SuppressionReason, _ as TemplateKind, $ as TemplateVersionDoc, a0 as VarsAdapter, a1 as VarsResolveInfo, a2 as WebhookEventDoc, a3 as defineVars, a4 as ensureIndexes, a5 as getCollections, a6 as validateSenderDomain, a7 as varsJsonSchema } from './null-B3N6bTpc.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, b as AdapterSort, M as MailProvider, S as SendArgs, c as SendResult, d as MailTesterFeedback, e as Mailer, T as TemplateDoc, F as FlowStep, f as Collections, g as FlowDoc, h as SuppressionScope, D as DeliveryWindow, i as SegmentFilter, P as Predicate } from './null-DxTHQSrJ.js';
2
- export { j as AuditLogDoc, B as BotFilterConfig, k as BroadcastDoc, l as BroadcastOrder, m as BroadcastPauseReason, n as BroadcastStatus, o as CircuitBreakerThresholds, p as ContactTagDoc, E as EventDoc, q as FlowGoal, r as FlowRunDoc, s as FlowRunStatus, t as FlowVersionDoc, H as HealthDoc, u as HealthStatus, L as LeadDoc, v as MailerConfig, w as NullProvider, O as OutboxDoc, x as RESERVED_VAR_KEYS, y as RedisOptions, z as SegmentDefinition, G as SendDoc, I as SendStatus, J as SenderDomainConfig, K as SenderDomainRegistry, Q as SenderDomainValidation, U as SubscriptionDoc, V as SubscriptionStatus, W as SuppressionDoc, X as SuppressionReason, Y as TemplateKind, Z as TemplateVersionDoc, _ as VarsAdapter, $ as VarsResolveInfo, a0 as WebhookEventDoc, a1 as defineVars, a2 as ensureIndexes, a3 as getCollections, a4 as validateSenderDomain, a5 as varsJsonSchema } from './null-DxTHQSrJ.js';
1
+ import { R as RunnerContext, N as NormalizedEvent, C as ContactAdapter, a as Contact, A as AdapterFilter, b as AdapterSort, M as MailProvider, S as SendArgs, c as SendResult, d as MailTesterFeedback, e as Mailer, T as TemplateDoc, F as FlowStep, f as Collections, g as FlowDoc, h as SuppressionScope, D as DeliveryWindow, i as SegmentFilter, P as Predicate } from './null-B3N6bTpc.js';
2
+ export { j as AuditLogDoc, B as BotFilterConfig, k as BroadcastDoc, l as BroadcastOrder, m as BroadcastPauseReason, n as BroadcastStatus, o as CircuitBreakerThresholds, p as ContactAdapterFactory, q as ContactTagDoc, E as EventDoc, r as FlowGoal, s as FlowRunDoc, t as FlowRunStatus, u as FlowVersionDoc, H as HealthDoc, v as HealthStatus, L as LeadDoc, w as MailerConfig, x as MailerMongoConnection, y as NullProvider, O as OutboxDoc, z as RESERVED_VAR_KEYS, G as RedisOptions, I as SegmentDefinition, J as SendDoc, K as SendStatus, Q as SenderDomainConfig, U as SenderDomainRegistry, V as SenderDomainValidation, W as SubscriptionDoc, X as SubscriptionStatus, Y as SuppressionDoc, Z as SuppressionReason, _ as TemplateKind, $ as TemplateVersionDoc, a0 as VarsAdapter, a1 as VarsResolveInfo, a2 as WebhookEventDoc, a3 as defineVars, a4 as ensureIndexes, a5 as getCollections, a6 as validateSenderDomain, a7 as varsJsonSchema } from './null-B3N6bTpc.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
@@ -2482,7 +2482,7 @@ function derivePlaintext(html) {
2482
2482
  var PLAIN_TEXT = { noEscape: true };
2483
2483
  async function renderTemplate(template, ctx, opts = {}) {
2484
2484
  const hb = makeHandlebars(opts.helpers);
2485
- const subject = hb.compile(template.subject)(ctx);
2485
+ const subject = hb.compile(template.subject, PLAIN_TEXT)(ctx).replace(/[\r\n]+/g, " ");
2486
2486
  const preheader = hb.compile(template.preheader)(ctx);
2487
2487
  let html;
2488
2488
  if (template.body.html) {
@@ -3894,7 +3894,7 @@ async function runDnsblChecks(ctx, opts = {}) {
3894
3894
  let listedCount = 0;
3895
3895
  async function processPair(p) {
3896
3896
  const queryName = buildQueryName(p.target, p.targetKind, spamhausQueryHost(p.list.host, cfg.spamhausDqsKey));
3897
- const lookup2 = queryName ? await queryDnsbl(resolver, queryName) : { result: "error", returnCodes: [], errorMessage: "unsupported target format" };
3897
+ const lookup2 = queryName ? await queryDnsbl(resolver, queryName, p.list.host) : { result: "error", returnCodes: [], errorMessage: "unsupported target format" };
3898
3898
  if (lookup2.transient) return;
3899
3899
  if (lookup2.result === "listed") listedCount++;
3900
3900
  await ctx.collections.dnsblChecks.updateOne(
@@ -3955,10 +3955,10 @@ function buildQueryName(target, kind, listHost) {
3955
3955
  return `${domain}.${listHost}`;
3956
3956
  }
3957
3957
  var TRANSIENT_DNS_CODES = /* @__PURE__ */ new Set(["ESERVFAIL", "EREFUSED", "ETIMEOUT", "ETIMEDOUT", "ECONNRESET", "ECONNREFUSED"]);
3958
- async function queryDnsbl(resolver, query) {
3958
+ async function queryDnsbl(resolver, query, listHost) {
3959
3959
  try {
3960
3960
  const records = await resolver.resolve4(query);
3961
- return interpretRecords(records);
3961
+ return interpretRecords(records, listHost);
3962
3962
  } catch (err) {
3963
3963
  const code = err?.code;
3964
3964
  if (code === "ENOTFOUND" || code === "ENODATA") {
@@ -3984,10 +3984,17 @@ var REFUSAL_REASONS = {
3984
3984
  "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",
3985
3985
  "127.255.255.255": "this resolver has exceeded the free query limit"
3986
3986
  };
3987
- function interpretRecords(records) {
3987
+ function interpretRecords(records, listHost) {
3988
3988
  if (!records || records.length === 0) {
3989
3989
  return { result: "clean", returnCodes: [], errorMessage: null };
3990
3990
  }
3991
+ if (listHost && REGISTRABLE_ONLY_LISTS.has(listHost) && records.every((r) => r === "127.0.0.1")) {
3992
+ return {
3993
+ result: "error",
3994
+ returnCodes: records,
3995
+ 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`
3996
+ };
3997
+ }
3991
3998
  const errorish = records.filter((r) => r.startsWith("127.255.255."));
3992
3999
  if (errorish.length === records.length) {
3993
4000
  const reasons = [...new Set(records.map((r) => REFUSAL_REASONS[r] ?? "unrecognised refusal code"))];
@@ -5123,6 +5130,7 @@ async function processScheduledBroadcasts2(ctx) {
5123
5130
  }
5124
5131
 
5125
5132
  // src/server/mailer.ts
5133
+ var STORAGE_REQUIRED = "Mailer.init: give exactly one of `db` (an open Db) or `mongo` (a connection mailery opens)";
5126
5134
  var Mailer = class _Mailer {
5127
5135
  db;
5128
5136
  collections;
@@ -5132,6 +5140,8 @@ var Mailer = class _Mailer {
5132
5140
  config;
5133
5141
  events;
5134
5142
  queueDriver;
5143
+ /** The client `Mailer.init` opened from `config.mongo`; closed by `stop()`. Null when the host gave `db`. */
5144
+ ownedClient;
5135
5145
  workersStarted = false;
5136
5146
  runnerContext;
5137
5147
  constructor(args) {
@@ -5141,6 +5151,7 @@ var Mailer = class _Mailer {
5141
5151
  this.adapter = args.adapter;
5142
5152
  this.providers = args.providers;
5143
5153
  this.queueDriver = args.queueDriver;
5154
+ this.ownedClient = args.ownedClient;
5144
5155
  this.queues = args.queueDriver.queues;
5145
5156
  this.events = args.events;
5146
5157
  this.runnerContext = {
@@ -5237,36 +5248,58 @@ var Mailer = class _Mailer {
5237
5248
  });
5238
5249
  }
5239
5250
  static async init(input) {
5240
- const config = resolveConfig(input);
5241
- if (!resolveProvider(config.providers, config.defaultProvider)) {
5251
+ if (input.db == null === (input.mongo == null)) throw new Error(STORAGE_REQUIRED);
5252
+ if (!resolveProvider(input.providers, input.defaultProvider)) {
5242
5253
  throw new Error(
5243
- `defaultProvider "${config.defaultProvider}" is not a registered provider. Registered: ${registeredProviderNames(config.providers).join(", ") || "(none)"}`
5254
+ `defaultProvider "${input.defaultProvider}" is not a registered provider. Registered: ${registeredProviderNames(input.providers).join(", ") || "(none)"}`
5244
5255
  );
5245
5256
  }
5246
- if (config.defaultTransactionalProvider != null && !resolveProvider(config.providers, config.defaultTransactionalProvider)) {
5257
+ if (input.defaultTransactionalProvider != null && !resolveProvider(input.providers, input.defaultTransactionalProvider)) {
5247
5258
  throw new Error(
5248
- `defaultTransactionalProvider "${config.defaultTransactionalProvider}" is not a registered provider. Registered: ${registeredProviderNames(config.providers).join(", ") || "(none)"}`
5259
+ `defaultTransactionalProvider "${input.defaultTransactionalProvider}" is not a registered provider. Registered: ${registeredProviderNames(input.providers).join(", ") || "(none)"}`
5249
5260
  );
5250
5261
  }
5251
- if (config.varsAdapter) {
5262
+ if (input.varsAdapter) {
5252
5263
  const { assertNoReservedVarKeys: assertNoReservedVarKeys2 } = await Promise.resolve().then(() => (init_vars(), vars_exports));
5253
- assertNoReservedVarKeys2(config.varsAdapter);
5254
- }
5255
- const collections = getCollections(config.db, config.collectionPrefix);
5256
- await ensureIndexes(config.db, config.collectionPrefix);
5257
- const queueDriver = await createQueueDriver(config.queue, config.db);
5258
- if (!config.workerless && config.queue.driver !== "noop") {
5259
- await queueDriver.scheduleRepeatingTick(config.tickIntervalSeconds);
5260
- }
5261
- return new _Mailer({
5262
- config,
5263
- db: config.db,
5264
- collections,
5265
- adapter: config.adapter,
5266
- providers: config.providers,
5267
- queueDriver,
5268
- events: new EventRegistry()
5269
- });
5264
+ assertNoReservedVarKeys2(input.varsAdapter);
5265
+ }
5266
+ let ownedClient = null;
5267
+ let db;
5268
+ if (input.mongo) {
5269
+ const { MongoClient } = await import('mongodb');
5270
+ ownedClient = await MongoClient.connect(input.mongo.uri, input.mongo.clientOptions);
5271
+ db = ownedClient.db(input.mongo.dbName);
5272
+ } else if (input.db) {
5273
+ db = input.db;
5274
+ } else {
5275
+ throw new Error(STORAGE_REQUIRED);
5276
+ }
5277
+ try {
5278
+ const config = resolveConfig({
5279
+ ...input,
5280
+ db,
5281
+ adapter: typeof input.adapter === "function" ? input.adapter(db) : input.adapter
5282
+ });
5283
+ const collections = getCollections(config.db, config.collectionPrefix);
5284
+ await ensureIndexes(config.db, config.collectionPrefix);
5285
+ const queueDriver = await createQueueDriver(config.queue, config.db);
5286
+ if (!config.workerless && config.queue.driver !== "noop") {
5287
+ await queueDriver.scheduleRepeatingTick(config.tickIntervalSeconds);
5288
+ }
5289
+ return new _Mailer({
5290
+ config,
5291
+ db: config.db,
5292
+ collections,
5293
+ adapter: config.adapter,
5294
+ providers: config.providers,
5295
+ queueDriver,
5296
+ events: new EventRegistry(),
5297
+ ownedClient
5298
+ });
5299
+ } catch (err) {
5300
+ await ownedClient?.close();
5301
+ throw err;
5302
+ }
5270
5303
  }
5271
5304
  // -------------------------------------------------------------------------
5272
5305
  // Event registration + firing
@@ -5739,6 +5772,8 @@ var Mailer = class _Mailer {
5739
5772
  async stop() {
5740
5773
  await this.queueDriver.close();
5741
5774
  this.workersStarted = false;
5775
+ await this.ownedClient?.close();
5776
+ this.ownedClient = null;
5742
5777
  }
5743
5778
  /** Used internally by the admin router and tests; not part of the public API. */
5744
5779
  getRunnerContext() {
@@ -9554,7 +9589,7 @@ function unitToMs2(value, unit) {
9554
9589
  }
9555
9590
 
9556
9591
  // src/server/api/agent.ts
9557
- var VERSION = "0.19.0" ;
9592
+ var VERSION = "0.20.0" ;
9558
9593
  var broadcastCountSchema = z.object({
9559
9594
  recipientCap: z.number().int().positive().max(1e7).nullable().optional()
9560
9595
  });
@@ -11621,7 +11656,7 @@ var DEDUPE_POLICIES = [
11621
11656
  ];
11622
11657
 
11623
11658
  // src/server/index.ts
11624
- var VERSION2 = "0.19.0" ;
11659
+ var VERSION2 = "0.20.0" ;
11625
11660
 
11626
11661
  export { DEDUPE_POLICIES, DEFAULT_BOT_UA_RE, FLOW_STEP_KINDS, FlowOperationError, MIN_AGENT_TOKEN_LENGTH, Mailer, MongoContactAdapter, NullProvider, PREDICATE_KINDS, RESERVED_VAR_KEYS, SEGMENT_FILTER_KINDS, SendGridProvider, TRACKING_SIG_LENGTH, VERSION2 as VERSION, applyTracking, applyWebhookEvent, armFlow, compileMailyTemplate, compileTemplate, computeDeliveryTime, createAdminApiRouter, createAdminRouter, createAgentRouter, createPublicRouter, defaultFlowStep, defaultPredicate, defaultSegmentFilter, defineVars, derivePlaintext, disarmFlow, dispatchSend, drainPendingUnsubscribes, ensureIndexes, gateFlow, getCollections, isCanaryGate, predicateKind, processNewlyFiredEventTriggers, processOneRunStep, referencedPaths, renderForContact, renderTemplate, runTick, sendgridInboundParser, sha256Hex, signTrackingToken, signUnsubscribeToken, simulateFlow, stampWatermarkIfNull, sweepStrandedFlowRuns, ungateFlow, validateSenderDomain, varsJsonSchema, verifyTemplate, verifyTrackingToken, verifyUnsubscribeToken };
11627
11662
  //# sourceMappingURL=index.js.map