mailery 0.16.0 → 0.16.2

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
@@ -286,15 +286,49 @@ var init_webhook_tolerance = __esm({
286
286
  // src/server/providers/sendgrid.ts
287
287
  var sendgrid_exports = {};
288
288
  __export(sendgrid_exports, {
289
- SendGridProvider: () => exports.SendGridProvider
289
+ SendGridProvider: () => exports.SendGridProvider,
290
+ normalizeSendGridMessageId: () => normalizeSendGridMessageId,
291
+ normalizeWebhookVerificationKey: () => normalizeWebhookVerificationKey
290
292
  });
293
+ function normalizeWebhookVerificationKey(input) {
294
+ let key = String(input ?? "").trim().replace(/^["']|["']$/g, "");
295
+ if (key.includes("\\n")) key = key.replace(/\\n/g, "\n");
296
+ let pem;
297
+ if (key.includes(PEM_HEADER)) {
298
+ pem = key;
299
+ } else {
300
+ const b64 = key.replace(/\s+/g, "");
301
+ if (!b64 || !/^[A-Za-z0-9+/=]+$/.test(b64)) {
302
+ throw new Error(
303
+ "SendGridProvider: webhookVerificationKey is neither a PEM public key nor a base64 SPKI key. Copy the Verification Key from SendGrid \u2192 Settings \u2192 Mail Settings \u2192 Signed Event Webhook."
304
+ );
305
+ }
306
+ pem = `${PEM_HEADER}
307
+ ${b64.match(/.{1,64}/g).join("\n")}
308
+ ${PEM_FOOTER}
309
+ `;
310
+ }
311
+ try {
312
+ crypto2__default.default.createPublicKey(pem);
313
+ } catch (err) {
314
+ throw new Error(
315
+ `SendGridProvider: webhookVerificationKey did not parse as a public key (${String(err?.message ?? err)}). Copy the Verification Key from SendGrid \u2192 Settings \u2192 Mail Settings \u2192 Signed Event Webhook.`
316
+ );
317
+ }
318
+ return pem;
319
+ }
320
+ function normalizeSendGridMessageId(raw) {
321
+ const id = String(raw ?? "");
322
+ const marker = /\.(?:filter|recvd)\S*$/i.exec(id);
323
+ return marker ? id.slice(0, marker.index) : id;
324
+ }
291
325
  function normalizeSendGridEvent(e) {
292
326
  const type = mapEventType(e.event);
293
327
  if (!type) return null;
294
328
  return {
295
329
  type,
296
330
  providerEventId: String(e.sg_event_id ?? e["smtp-id"] ?? `${e.event}-${e.timestamp}-${e.email}`),
297
- providerMessageId: String(e.sg_message_id ?? e["smtp-id"] ?? ""),
331
+ providerMessageId: e.sg_message_id != null ? normalizeSendGridMessageId(e.sg_message_id) : String(e["smtp-id"] ?? ""),
298
332
  email: String(e.email ?? "").toLowerCase(),
299
333
  occurredAt: new Date(Number(e.timestamp) * 1e3),
300
334
  details: {
@@ -326,24 +360,29 @@ function mapEventType(sgEvent) {
326
360
  return null;
327
361
  }
328
362
  }
329
- var SG_SIG_HEADER, SG_TS_HEADER; exports.SendGridProvider = void 0;
363
+ var SG_SIG_HEADER, SG_TS_HEADER, PEM_HEADER, PEM_FOOTER; exports.SendGridProvider = void 0;
330
364
  var init_sendgrid = __esm({
331
365
  "src/server/providers/sendgrid.ts"() {
332
366
  init_webhook_tolerance();
333
367
  SG_SIG_HEADER = "x-twilio-email-event-webhook-signature";
334
368
  SG_TS_HEADER = "x-twilio-email-event-webhook-timestamp";
369
+ PEM_HEADER = "-----BEGIN PUBLIC KEY-----";
370
+ PEM_FOOTER = "-----END PUBLIC KEY-----";
335
371
  exports.SendGridProvider = class {
336
372
  constructor(opts) {
337
373
  this.opts = opts;
338
374
  sgMail__default.default.setApiKey(opts.apiKey);
339
375
  this.sendRatePerSecond = opts.sendRatePerSecond ?? 10;
340
376
  this.webhookToleranceSeconds = resolveWebhookToleranceSeconds(opts.webhookToleranceSeconds);
377
+ this.verificationKeyPem = opts.webhookVerificationKey ? normalizeWebhookVerificationKey(opts.webhookVerificationKey) : null;
341
378
  }
342
379
  opts;
343
380
  name = "sendgrid";
344
381
  sendRatePerSecond;
345
382
  /** Resolved replay window in seconds; `0` means the check is disabled. */
346
383
  webhookToleranceSeconds;
384
+ /** The verification key as PEM, whatever shape it was configured in. */
385
+ verificationKeyPem;
347
386
  async send(args) {
348
387
  const msg = {
349
388
  to: args.to,
@@ -375,7 +414,7 @@ var init_sendgrid = __esm({
375
414
  };
376
415
  }
377
416
  async verifyWebhook(rawBody, headers) {
378
- if (!this.opts.webhookVerificationKey) return false;
417
+ if (!this.verificationKeyPem) return false;
379
418
  const sig = headers[SG_SIG_HEADER];
380
419
  const ts = headers[SG_TS_HEADER];
381
420
  if (!sig || !ts) return false;
@@ -383,7 +422,7 @@ var init_sendgrid = __esm({
383
422
  try {
384
423
  const verifier = crypto2__default.default.createVerify("sha256");
385
424
  verifier.update(payload);
386
- if (!verifier.verify(this.opts.webhookVerificationKey, sig, "base64")) return false;
425
+ if (!verifier.verify(this.verificationKeyPem, sig, "base64")) return false;
387
426
  } catch {
388
427
  return false;
389
428
  }
@@ -1726,11 +1765,20 @@ async function processWebhookBacklog(ctx, opts = {}) {
1726
1765
  }
1727
1766
  }
1728
1767
  }
1768
+ async function findSendForEvent(event, ctx) {
1769
+ if (event.providerMessageId) {
1770
+ const byId = await ctx.collections.sends.findOne({ providerMessageId: event.providerMessageId });
1771
+ if (byId) return byId;
1772
+ }
1773
+ if (!event.email) return null;
1774
+ return ctx.collections.sends.findOne({ emailAtSend: event.email, sentAt: { $ne: null } }, { sort: { sentAt: -1 } });
1775
+ }
1776
+ function webhookEventsForMessageId(providerMessageId) {
1777
+ const escaped = providerMessageId.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1778
+ return { $or: [{ providerMessageId }, { providerMessageId: { $regex: `^${escaped}\\.` } }] };
1779
+ }
1729
1780
  async function applyWebhookEvent(event, ctx) {
1730
- const send = await ctx.collections.sends.findOne(
1731
- event.providerMessageId ? { $or: [{ providerMessageId: event.providerMessageId }, { emailAtSend: event.email }] } : { emailAtSend: event.email },
1732
- { sort: { queuedAt: -1 } }
1733
- );
1781
+ const send = await findSendForEvent(event, ctx);
1734
1782
  switch (event.type) {
1735
1783
  case "delivered":
1736
1784
  if (send) {
@@ -6484,7 +6532,7 @@ function createAdminApiRouter(mailer, opts = {}) {
6484
6532
  if (!mongodb.ObjectId.isValid(id)) return res.status(400).json({ error: "bad_id" });
6485
6533
  const send = await c.sends.findOne({ _id: new mongodb.ObjectId(id) });
6486
6534
  if (!send) return res.status(404).json({ error: "not_found" });
6487
- const events = send.providerMessageId ? await c.webhookEvents.find({ providerMessageId: send.providerMessageId }).sort({ receivedAt: -1 }).limit(100).toArray() : [];
6535
+ const events = send.providerMessageId ? await c.webhookEvents.find(webhookEventsForMessageId(send.providerMessageId)).sort({ receivedAt: -1 }).limit(100).toArray() : [];
6488
6536
  return res.json({ send, webhookEvents: events });
6489
6537
  })
6490
6538
  );
@@ -8223,7 +8271,7 @@ function unitToMs2(value, unit) {
8223
8271
  }
8224
8272
 
8225
8273
  // src/server/api/agent.ts
8226
- var VERSION = "0.16.0" ;
8274
+ var VERSION = "0.16.2" ;
8227
8275
  var MIN_AGENT_TOKEN_LENGTH = 24;
8228
8276
  function createAgentRouter(mailer, opts) {
8229
8277
  if (!opts || !Array.isArray(opts.tokens) || opts.tokens.length === 0) {
@@ -8426,7 +8474,7 @@ function createAgentRouter(mailer, opts) {
8426
8474
  if (reached || Date.now() - started >= timeoutMs) break;
8427
8475
  await sleep2(1e3);
8428
8476
  }
8429
- const webhookEvents = send.providerMessageId ? await c.webhookEvents.find({ providerMessageId: send.providerMessageId }).sort({ receivedAt: 1 }).limit(100).toArray() : [];
8477
+ const webhookEvents = send.providerMessageId ? await c.webhookEvents.find(webhookEventsForMessageId(send.providerMessageId)).sort({ receivedAt: 1 }).limit(100).toArray() : [];
8430
8478
  res.json({ reached, target, waitedMs: Date.now() - started, send: sendSummary(send), webhookEvents });
8431
8479
  })
8432
8480
  );
@@ -9923,7 +9971,7 @@ var DEDUPE_POLICIES = [
9923
9971
  ];
9924
9972
 
9925
9973
  // src/server/index.ts
9926
- var VERSION2 = "0.16.0" ;
9974
+ var VERSION2 = "0.16.2" ;
9927
9975
 
9928
9976
  exports.DEDUPE_POLICIES = DEDUPE_POLICIES;
9929
9977
  exports.DEFAULT_BOT_UA_RE = DEFAULT_BOT_UA_RE;