mailery 0.16.0 → 0.16.1

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
@@ -190,7 +190,15 @@ type WebhookToleranceOption = number | false;
190
190
 
191
191
  interface SendGridProviderOptions {
192
192
  apiKey: string;
193
- /** ECDSA public key (PEM) from SendGrid → Settings → Mail Settings → Signed Event Webhook. */
193
+ /**
194
+ * ECDSA public key from SendGrid → Settings → Mail Settings → Signed Event
195
+ * Webhook. Either form SendGrid hands out is accepted: the single-line
196
+ * base64 string its dashboard shows and its API returns as `public_key`
197
+ * (what `mailery setup-sendgrid` prints), or the same key as a PEM block.
198
+ * A PEM whose newlines were escaped to `\n` for an .env line is unescaped.
199
+ * Anything that does not parse as a public key throws at construction —
200
+ * a key that silently verifies nothing is the failure this replaces.
201
+ */
194
202
  webhookVerificationKey?: string;
195
203
  /**
196
204
  * Replay window for the signed webhook timestamp, in seconds.
@@ -213,6 +221,8 @@ declare class SendGridProvider implements MailProvider {
213
221
  readonly sendRatePerSecond: number;
214
222
  /** Resolved replay window in seconds; `0` means the check is disabled. */
215
223
  readonly webhookToleranceSeconds: number;
224
+ /** The verification key as PEM, whatever shape it was configured in. */
225
+ private readonly verificationKeyPem;
216
226
  constructor(opts: SendGridProviderOptions);
217
227
  send(args: SendArgs): Promise<SendResult>;
218
228
  verifyWebhook(rawBody: Buffer, headers: Record<string, string>): Promise<boolean>;
package/dist/index.d.ts CHANGED
@@ -190,7 +190,15 @@ type WebhookToleranceOption = number | false;
190
190
 
191
191
  interface SendGridProviderOptions {
192
192
  apiKey: string;
193
- /** ECDSA public key (PEM) from SendGrid → Settings → Mail Settings → Signed Event Webhook. */
193
+ /**
194
+ * ECDSA public key from SendGrid → Settings → Mail Settings → Signed Event
195
+ * Webhook. Either form SendGrid hands out is accepted: the single-line
196
+ * base64 string its dashboard shows and its API returns as `public_key`
197
+ * (what `mailery setup-sendgrid` prints), or the same key as a PEM block.
198
+ * A PEM whose newlines were escaped to `\n` for an .env line is unescaped.
199
+ * Anything that does not parse as a public key throws at construction —
200
+ * a key that silently verifies nothing is the failure this replaces.
201
+ */
194
202
  webhookVerificationKey?: string;
195
203
  /**
196
204
  * Replay window for the signed webhook timestamp, in seconds.
@@ -213,6 +221,8 @@ declare class SendGridProvider implements MailProvider {
213
221
  readonly sendRatePerSecond: number;
214
222
  /** Resolved replay window in seconds; `0` means the check is disabled. */
215
223
  readonly webhookToleranceSeconds: number;
224
+ /** The verification key as PEM, whatever shape it was configured in. */
225
+ private readonly verificationKeyPem;
216
226
  constructor(opts: SendGridProviderOptions);
217
227
  send(args: SendArgs): Promise<SendResult>;
218
228
  verifyWebhook(rawBody: Buffer, headers: Record<string, string>): Promise<boolean>;
package/dist/index.js CHANGED
@@ -268,8 +268,36 @@ var init_webhook_tolerance = __esm({
268
268
  // src/server/providers/sendgrid.ts
269
269
  var sendgrid_exports = {};
270
270
  __export(sendgrid_exports, {
271
- SendGridProvider: () => SendGridProvider
271
+ SendGridProvider: () => SendGridProvider,
272
+ normalizeWebhookVerificationKey: () => normalizeWebhookVerificationKey
272
273
  });
274
+ function normalizeWebhookVerificationKey(input) {
275
+ let key = String(input ?? "").trim().replace(/^["']|["']$/g, "");
276
+ if (key.includes("\\n")) key = key.replace(/\\n/g, "\n");
277
+ let pem;
278
+ if (key.includes(PEM_HEADER)) {
279
+ pem = key;
280
+ } else {
281
+ const b64 = key.replace(/\s+/g, "");
282
+ if (!b64 || !/^[A-Za-z0-9+/=]+$/.test(b64)) {
283
+ throw new Error(
284
+ "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."
285
+ );
286
+ }
287
+ pem = `${PEM_HEADER}
288
+ ${b64.match(/.{1,64}/g).join("\n")}
289
+ ${PEM_FOOTER}
290
+ `;
291
+ }
292
+ try {
293
+ crypto2.createPublicKey(pem);
294
+ } catch (err) {
295
+ throw new Error(
296
+ `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.`
297
+ );
298
+ }
299
+ return pem;
300
+ }
273
301
  function normalizeSendGridEvent(e) {
274
302
  const type = mapEventType(e.event);
275
303
  if (!type) return null;
@@ -308,24 +336,29 @@ function mapEventType(sgEvent) {
308
336
  return null;
309
337
  }
310
338
  }
311
- var SG_SIG_HEADER, SG_TS_HEADER, SendGridProvider;
339
+ var SG_SIG_HEADER, SG_TS_HEADER, PEM_HEADER, PEM_FOOTER, SendGridProvider;
312
340
  var init_sendgrid = __esm({
313
341
  "src/server/providers/sendgrid.ts"() {
314
342
  init_webhook_tolerance();
315
343
  SG_SIG_HEADER = "x-twilio-email-event-webhook-signature";
316
344
  SG_TS_HEADER = "x-twilio-email-event-webhook-timestamp";
345
+ PEM_HEADER = "-----BEGIN PUBLIC KEY-----";
346
+ PEM_FOOTER = "-----END PUBLIC KEY-----";
317
347
  SendGridProvider = class {
318
348
  constructor(opts) {
319
349
  this.opts = opts;
320
350
  sgMail.setApiKey(opts.apiKey);
321
351
  this.sendRatePerSecond = opts.sendRatePerSecond ?? 10;
322
352
  this.webhookToleranceSeconds = resolveWebhookToleranceSeconds(opts.webhookToleranceSeconds);
353
+ this.verificationKeyPem = opts.webhookVerificationKey ? normalizeWebhookVerificationKey(opts.webhookVerificationKey) : null;
323
354
  }
324
355
  opts;
325
356
  name = "sendgrid";
326
357
  sendRatePerSecond;
327
358
  /** Resolved replay window in seconds; `0` means the check is disabled. */
328
359
  webhookToleranceSeconds;
360
+ /** The verification key as PEM, whatever shape it was configured in. */
361
+ verificationKeyPem;
329
362
  async send(args) {
330
363
  const msg = {
331
364
  to: args.to,
@@ -357,7 +390,7 @@ var init_sendgrid = __esm({
357
390
  };
358
391
  }
359
392
  async verifyWebhook(rawBody, headers) {
360
- if (!this.opts.webhookVerificationKey) return false;
393
+ if (!this.verificationKeyPem) return false;
361
394
  const sig = headers[SG_SIG_HEADER];
362
395
  const ts = headers[SG_TS_HEADER];
363
396
  if (!sig || !ts) return false;
@@ -365,7 +398,7 @@ var init_sendgrid = __esm({
365
398
  try {
366
399
  const verifier = crypto2.createVerify("sha256");
367
400
  verifier.update(payload);
368
- if (!verifier.verify(this.opts.webhookVerificationKey, sig, "base64")) return false;
401
+ if (!verifier.verify(this.verificationKeyPem, sig, "base64")) return false;
369
402
  } catch {
370
403
  return false;
371
404
  }
@@ -8205,7 +8238,7 @@ function unitToMs2(value, unit) {
8205
8238
  }
8206
8239
 
8207
8240
  // src/server/api/agent.ts
8208
- var VERSION = "0.16.0" ;
8241
+ var VERSION = "0.16.1" ;
8209
8242
  var MIN_AGENT_TOKEN_LENGTH = 24;
8210
8243
  function createAgentRouter(mailer, opts) {
8211
8244
  if (!opts || !Array.isArray(opts.tokens) || opts.tokens.length === 0) {
@@ -9905,7 +9938,7 @@ var DEDUPE_POLICIES = [
9905
9938
  ];
9906
9939
 
9907
9940
  // src/server/index.ts
9908
- var VERSION2 = "0.16.0" ;
9941
+ var VERSION2 = "0.16.1" ;
9909
9942
 
9910
9943
  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 };
9911
9944
  //# sourceMappingURL=index.js.map