sently 0.4.5 → 0.4.6

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.
Files changed (38) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/dist/adapters/bun.d.ts +7 -0
  3. package/dist/adapters/bun.js.map +2 -2
  4. package/dist/adapters/cf.d.ts +6 -0
  5. package/dist/adapters/cf.js.map +2 -2
  6. package/dist/adapters/deno.d.ts +4 -0
  7. package/dist/adapters/deno.js.map +2 -2
  8. package/dist/adapters/node.d.ts +7 -0
  9. package/dist/adapters/node.js.map +2 -2
  10. package/dist/auth/oauth2.d.ts +4 -0
  11. package/dist/chunk-7fqv71z1.js.map +2 -2
  12. package/dist/chunk-mp5c9bfd.js.map +3 -3
  13. package/dist/chunk-tymfm441.js.map +2 -2
  14. package/dist/chunk-x3szga4k.js.map +2 -2
  15. package/dist/chunk-ym3zzv8b.js.map +2 -2
  16. package/dist/core/mime.d.ts +4 -0
  17. package/dist/core/sigv4.d.ts +10 -0
  18. package/dist/core/smtp.d.ts +5 -0
  19. package/dist/pool/connection.d.ts +4 -0
  20. package/dist/pool/pool.d.ts +20 -0
  21. package/dist/transports/brevo.d.ts +1 -0
  22. package/dist/transports/brevo.js.map +2 -2
  23. package/dist/transports/mailgun.d.ts +3 -0
  24. package/dist/transports/mailgun.js.map +2 -2
  25. package/dist/transports/postmark.d.ts +1 -0
  26. package/dist/transports/postmark.js.map +2 -2
  27. package/dist/transports/preview.d.ts +3 -0
  28. package/dist/transports/preview.js.map +2 -2
  29. package/dist/transports/resend.d.ts +2 -0
  30. package/dist/transports/resend.js.map +2 -2
  31. package/dist/transports/retry.d.ts +12 -1
  32. package/dist/transports/retry.js.map +2 -2
  33. package/dist/transports/sendgrid.d.ts +1 -0
  34. package/dist/transports/sendgrid.js.map +2 -2
  35. package/dist/transports/ses.d.ts +5 -0
  36. package/dist/transports/ses.js.map +3 -3
  37. package/dist/transports/smtp.d.ts +3 -0
  38. package/package.json +1 -1
@@ -2,9 +2,9 @@
2
2
  "version": 3,
3
3
  "sources": ["../src/transports/preview.ts"],
4
4
  "sourcesContent": [
5
- "/**\n * @module\n * PreviewTransport — development transport that writes emails to disk\n * instead of sending them. Optionally opens in the default browser.\n * Use in place of a real transport during local development.\n *\n * @example\n * ```ts\n * import { PreviewTransport } from 'sently/transports/preview'\n * const transport = new PreviewTransport({ outDir: './.emails', open: true })\n * ```\n */\nimport { extractEmails } from \"../core/address.js\";\nimport { buildMIME } from \"../core/mime.js\";\nimport type {\n MailOptions,\n PreviewConfig,\n SendResult,\n Transport,\n VerifyResult,\n} from \"../core/types.js\";\nimport { resolveAttachments } from \"./resolve-attachments.js\";\n\nfunction sanitizeSubject(subject: string): string {\n const sanitized = subject.replace(/[^a-zA-Z0-9]+/g, \"-\").replace(/^-|-$/g, \"\");\n return sanitized.slice(0, 40) || \"no-subject\";\n}\n\nfunction getOpenCommand(platform: string): string {\n if (platform === \"darwin\") {\n return \"open\";\n }\n if (platform === \"win32\") {\n return \"start\";\n }\n return \"xdg-open\";\n}\n\n/**\n * Development transport that writes emails to disk instead of sending them.\n */\nexport class PreviewTransport implements Transport {\n private readonly outDir: string;\n private readonly open: boolean;\n private readonly format: \"eml\" | \"html\";\n\n /** Creates a preview transport with optional output directory and format. */\n constructor(config?: PreviewConfig) {\n this.outDir = config?.outDir ?? \"./.emails\";\n this.open = config?.open ?? false;\n this.format = config?.format ?? \"eml\";\n }\n\n /** Writes the message to disk and returns a synthetic SendResult. */\n async send(options: MailOptions): Promise<SendResult> {\n const attachments = await resolveAttachments(options.attachments);\n const resolvedOptions = { ...options, attachments };\n const mime = await buildMIME(resolvedOptions);\n\n const fs = await import(\"node:fs/promises\");\n await fs.mkdir(this.outDir, { recursive: true });\n\n const filename = `${Date.now()}-${sanitizeSubject(options.subject)}.${this.format}`;\n const filepath = `${this.outDir}/${filename}`;\n\n if (this.format === \"html\") {\n const html =\n options.html ??\n (options.text\n ? `<pre>${options.text.replace(/&/g, \"&amp;\").replace(/</g, \"&lt;\").replace(/>/g, \"&gt;\")}</pre>`\n : \"\");\n await fs.writeFile(filepath, html, \"utf8\");\n } else {\n await fs.writeFile(filepath, mime.raw);\n }\n\n console.log(`[sently preview] Written: ${filepath}`);\n\n if (this.open) {\n const { spawn } = await import(\"node:child_process\");\n const command = getOpenCommand(process.platform);\n spawn(command, [filepath], { detached: true, stdio: \"ignore\" }).unref();\n }\n\n return {\n messageId: mime.messageId,\n accepted: extractEmails(options.to),\n rejected: [],\n response: `preview: ${filepath}`,\n envelope: mime.envelope,\n };\n }\n\n /** Always succeeds — preview transport requires no external connectivity. */\n async verify(): Promise<VerifyResult> {\n return { ok: true, provider: \"preview\" };\n }\n}\n"
5
+ "/**\n * @module\n * PreviewTransport — development transport that writes emails to disk\n * instead of sending them. Optionally opens in the default browser.\n * Use in place of a real transport during local development.\n *\n * @example\n * ```ts\n * import { PreviewTransport } from 'sently/transports/preview'\n * const transport = new PreviewTransport({ outDir: './.emails', open: true })\n * ```\n */\nimport { extractEmails } from \"../core/address.js\";\nimport { buildMIME } from \"../core/mime.js\";\nimport type {\n MailOptions,\n PreviewConfig,\n SendResult,\n Transport,\n VerifyResult,\n} from \"../core/types.js\";\nimport { resolveAttachments } from \"./resolve-attachments.js\";\n\nfunction sanitizeSubject(subject: string): string {\n const sanitized = subject.replace(/[^a-zA-Z0-9]+/g, \"-\").replace(/^-|-$/g, \"\");\n return sanitized.slice(0, 40) || \"no-subject\";\n}\n\nfunction getOpenCommand(platform: string): string {\n if (platform === \"darwin\") {\n return \"open\";\n }\n if (platform === \"win32\") {\n return \"start\";\n }\n return \"xdg-open\";\n}\n\n/**\n * Development transport that writes emails to disk instead of sending them.\n */\nexport class PreviewTransport implements Transport {\n /** Directory where preview `.eml` or `.html` files are written. */\n private readonly outDir: string;\n /** Whether to open the written file in the default browser. */\n private readonly open: boolean;\n /** Output format: full MIME (`.eml`) or HTML body only. */\n private readonly format: \"eml\" | \"html\";\n\n /** Creates a preview transport with optional output directory and format. */\n constructor(config?: PreviewConfig) {\n this.outDir = config?.outDir ?? \"./.emails\";\n this.open = config?.open ?? false;\n this.format = config?.format ?? \"eml\";\n }\n\n /** Writes the message to disk and returns a synthetic SendResult. */\n async send(options: MailOptions): Promise<SendResult> {\n const attachments = await resolveAttachments(options.attachments);\n const resolvedOptions = { ...options, attachments };\n const mime = await buildMIME(resolvedOptions);\n\n const fs = await import(\"node:fs/promises\");\n await fs.mkdir(this.outDir, { recursive: true });\n\n const filename = `${Date.now()}-${sanitizeSubject(options.subject)}.${this.format}`;\n const filepath = `${this.outDir}/${filename}`;\n\n if (this.format === \"html\") {\n const html =\n options.html ??\n (options.text\n ? `<pre>${options.text.replace(/&/g, \"&amp;\").replace(/</g, \"&lt;\").replace(/>/g, \"&gt;\")}</pre>`\n : \"\");\n await fs.writeFile(filepath, html, \"utf8\");\n } else {\n await fs.writeFile(filepath, mime.raw);\n }\n\n console.log(`[sently preview] Written: ${filepath}`);\n\n if (this.open) {\n const { spawn } = await import(\"node:child_process\");\n const command = getOpenCommand(process.platform);\n spawn(command, [filepath], { detached: true, stdio: \"ignore\" }).unref();\n }\n\n return {\n messageId: mime.messageId,\n accepted: extractEmails(options.to),\n rejected: [],\n response: `preview: ${filepath}`,\n envelope: mime.envelope,\n };\n }\n\n /** Always succeeds — preview transport requires no external connectivity. */\n async verify(): Promise<VerifyResult> {\n return { ok: true, provider: \"preview\" };\n }\n}\n"
6
6
  ],
7
- "mappings": ";;;;;;;;;;;;;AAuBA,SAAS,eAAe,CAAC,SAAyB;AAAA,EAChD,MAAM,YAAY,QAAQ,QAAQ,kBAAkB,GAAG,EAAE,QAAQ,UAAU,EAAE;AAAA,EAC7E,OAAO,UAAU,MAAM,GAAG,EAAE,KAAK;AAAA;AAGnC,SAAS,cAAc,CAAC,UAA0B;AAAA,EAChD,IAAI,aAAa,UAAU;AAAA,IACzB,OAAO;AAAA,EACT;AAAA,EACA,IAAI,aAAa,SAAS;AAAA,IACxB,OAAO;AAAA,EACT;AAAA,EACA,OAAO;AAAA;AAAA;AAMF,MAAM,iBAAsC;AAAA,EAChC;AAAA,EACA;AAAA,EACA;AAAA,EAGjB,WAAW,CAAC,QAAwB;AAAA,IAClC,KAAK,SAAS,QAAQ,UAAU;AAAA,IAChC,KAAK,OAAO,QAAQ,QAAQ;AAAA,IAC5B,KAAK,SAAS,QAAQ,UAAU;AAAA;AAAA,OAI5B,KAAI,CAAC,SAA2C;AAAA,IACpD,MAAM,cAAc,MAAM,mBAAmB,QAAQ,WAAW;AAAA,IAChE,MAAM,kBAAkB,KAAK,SAAS,YAAY;AAAA,IAClD,MAAM,OAAO,MAAM,UAAU,eAAe;AAAA,IAE5C,MAAM,KAAK,MAAa;AAAA,IACxB,MAAM,GAAG,MAAM,KAAK,QAAQ,EAAE,WAAW,KAAK,CAAC;AAAA,IAE/C,MAAM,WAAW,GAAG,KAAK,IAAI,KAAK,gBAAgB,QAAQ,OAAO,KAAK,KAAK;AAAA,IAC3E,MAAM,WAAW,GAAG,KAAK,UAAU;AAAA,IAEnC,IAAI,KAAK,WAAW,QAAQ;AAAA,MAC1B,MAAM,OACJ,QAAQ,SACP,QAAQ,OACL,QAAQ,QAAQ,KAAK,QAAQ,MAAM,OAAO,EAAE,QAAQ,MAAM,MAAM,EAAE,QAAQ,MAAM,MAAM,YACtF;AAAA,MACN,MAAM,GAAG,UAAU,UAAU,MAAM,MAAM;AAAA,IAC3C,EAAO;AAAA,MACL,MAAM,GAAG,UAAU,UAAU,KAAK,GAAG;AAAA;AAAA,IAGvC,QAAQ,IAAI,6BAA6B,UAAU;AAAA,IAEnD,IAAI,KAAK,MAAM;AAAA,MACb,QAAQ,UAAU,MAAa;AAAA,MAC/B,MAAM,UAAU,eAAe,QAAQ,QAAQ;AAAA,MAC/C,MAAM,SAAS,CAAC,QAAQ,GAAG,EAAE,UAAU,MAAM,OAAO,SAAS,CAAC,EAAE,MAAM;AAAA,IACxE;AAAA,IAEA,OAAO;AAAA,MACL,WAAW,KAAK;AAAA,MAChB,UAAU,cAAc,QAAQ,EAAE;AAAA,MAClC,UAAU,CAAC;AAAA,MACX,UAAU,YAAY;AAAA,MACtB,UAAU,KAAK;AAAA,IACjB;AAAA;AAAA,OAII,OAAM,GAA0B;AAAA,IACpC,OAAO,EAAE,IAAI,MAAM,UAAU,UAAU;AAAA;AAE3C;",
7
+ "mappings": ";;;;;;;;;;;;;AAuBA,SAAS,eAAe,CAAC,SAAyB;AAAA,EAChD,MAAM,YAAY,QAAQ,QAAQ,kBAAkB,GAAG,EAAE,QAAQ,UAAU,EAAE;AAAA,EAC7E,OAAO,UAAU,MAAM,GAAG,EAAE,KAAK;AAAA;AAGnC,SAAS,cAAc,CAAC,UAA0B;AAAA,EAChD,IAAI,aAAa,UAAU;AAAA,IACzB,OAAO;AAAA,EACT;AAAA,EACA,IAAI,aAAa,SAAS;AAAA,IACxB,OAAO;AAAA,EACT;AAAA,EACA,OAAO;AAAA;AAAA;AAMF,MAAM,iBAAsC;AAAA,EAEhC;AAAA,EAEA;AAAA,EAEA;AAAA,EAGjB,WAAW,CAAC,QAAwB;AAAA,IAClC,KAAK,SAAS,QAAQ,UAAU;AAAA,IAChC,KAAK,OAAO,QAAQ,QAAQ;AAAA,IAC5B,KAAK,SAAS,QAAQ,UAAU;AAAA;AAAA,OAI5B,KAAI,CAAC,SAA2C;AAAA,IACpD,MAAM,cAAc,MAAM,mBAAmB,QAAQ,WAAW;AAAA,IAChE,MAAM,kBAAkB,KAAK,SAAS,YAAY;AAAA,IAClD,MAAM,OAAO,MAAM,UAAU,eAAe;AAAA,IAE5C,MAAM,KAAK,MAAa;AAAA,IACxB,MAAM,GAAG,MAAM,KAAK,QAAQ,EAAE,WAAW,KAAK,CAAC;AAAA,IAE/C,MAAM,WAAW,GAAG,KAAK,IAAI,KAAK,gBAAgB,QAAQ,OAAO,KAAK,KAAK;AAAA,IAC3E,MAAM,WAAW,GAAG,KAAK,UAAU;AAAA,IAEnC,IAAI,KAAK,WAAW,QAAQ;AAAA,MAC1B,MAAM,OACJ,QAAQ,SACP,QAAQ,OACL,QAAQ,QAAQ,KAAK,QAAQ,MAAM,OAAO,EAAE,QAAQ,MAAM,MAAM,EAAE,QAAQ,MAAM,MAAM,YACtF;AAAA,MACN,MAAM,GAAG,UAAU,UAAU,MAAM,MAAM;AAAA,IAC3C,EAAO;AAAA,MACL,MAAM,GAAG,UAAU,UAAU,KAAK,GAAG;AAAA;AAAA,IAGvC,QAAQ,IAAI,6BAA6B,UAAU;AAAA,IAEnD,IAAI,KAAK,MAAM;AAAA,MACb,QAAQ,UAAU,MAAa;AAAA,MAC/B,MAAM,UAAU,eAAe,QAAQ,QAAQ;AAAA,MAC/C,MAAM,SAAS,CAAC,QAAQ,GAAG,EAAE,UAAU,MAAM,OAAO,SAAS,CAAC,EAAE,MAAM;AAAA,IACxE;AAAA,IAEA,OAAO;AAAA,MACL,WAAW,KAAK;AAAA,MAChB,UAAU,cAAc,QAAQ,EAAE;AAAA,MAClC,UAAU,CAAC;AAAA,MACX,UAAU,YAAY;AAAA,MACtB,UAAU,KAAK;AAAA,IACjB;AAAA;AAAA,OAII,OAAM,GAA0B;AAAA,IACpC,OAAO,EAAE,IAAI,MAAM,UAAU,UAAU;AAAA;AAE3C;",
8
8
  "debugId": "A9DB64292AD1542264756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -17,7 +17,9 @@ export declare class ResendError extends Error {
17
17
  * Resend HTTP API transport.
18
18
  */
19
19
  export declare class ResendTransport implements Transport {
20
+ /** Resend API key for Bearer authentication. */
20
21
  private readonly apiKey;
22
+ /** Resend API base URL. */
21
23
  private readonly baseUrl;
22
24
  /** Creates a Resend transport with the given API key. */
23
25
  constructor(config: ResendConfig);
@@ -2,9 +2,9 @@
2
2
  "version": 3,
3
3
  "sources": ["../src/transports/resend.ts"],
4
4
  "sourcesContent": [
5
- "/**\n * @module\n * Resend HTTP API transport for sending email via api.resend.com.\n *\n * @example\n * ```ts\n * import { ResendTransport } from \"sently/transports/resend\";\n * import { createMailer } from \"sently\";\n *\n * const mailer = await createMailer({\n * transport: new ResendTransport({ apiKey: process.env.RESEND_API_KEY! }),\n * });\n *\n * await mailer.send({\n * from: \"onboarding@yourdomain.com\",\n * to: \"recipient@example.com\",\n * subject: \"Hello\",\n * html: \"<p>Sent via Resend</p>\",\n * });\n * ```\n */\nimport { extractEmails, parseAddresses, toMIMEHeader } from \"../core/address.js\";\nimport { encodeBase64 } from \"../core/base64.js\";\nimport type { MailOptions, SendResult, Transport, VerifyResult } from \"../core/types.js\";\nimport { resolveAttachments } from \"./resolve-attachments.js\";\n\n/** Resend API configuration. */\nexport interface ResendConfig {\n /** Resend API key (starts with `re_`). */\n apiKey: string;\n /** API base URL. Default: `https://api.resend.com`. */\n baseUrl?: string;\n}\n\n/** Error thrown when the Resend API returns a non-success response. */\nexport class ResendError extends Error {\n /** Creates a Resend API error with status code and response payload. */\n constructor(\n message: string,\n public readonly statusCode: number,\n public readonly apiError: unknown,\n ) {\n super(message);\n this.name = \"ResendError\";\n }\n}\n\n/**\n * Resend HTTP API transport.\n */\nexport class ResendTransport implements Transport {\n private readonly apiKey: string;\n private readonly baseUrl: string;\n\n /** Creates a Resend transport with the given API key. */\n constructor(config: ResendConfig) {\n this.apiKey = config.apiKey;\n this.baseUrl = config.baseUrl ?? \"https://api.resend.com\";\n }\n\n /** Sends an email via the Resend HTTP API. */\n async send(options: MailOptions): Promise<SendResult> {\n const attachments = await resolveAttachments(options.attachments);\n const from = parseAddresses(options.from)[0];\n const body = {\n from: from ? toMIMEHeader(from) : \"\",\n to: extractEmails(options.to),\n subject: options.subject,\n ...(options.cc ? { cc: extractEmails(options.cc) } : {}),\n ...(options.bcc ? { bcc: extractEmails(options.bcc) } : {}),\n ...(options.replyTo ? { reply_to: extractEmails(options.replyTo) } : {}),\n ...(options.text ? { text: options.text } : {}),\n ...(options.html ? { html: options.html } : {}),\n ...(options.headers ? { headers: options.headers } : {}),\n ...(attachments.length > 0\n ? {\n attachments: attachments.map((att) => ({\n filename: att.filename,\n content:\n att.content instanceof Uint8Array\n ? encodeBase64(att.content).replace(/\\r\\n/g, \"\")\n : att.content,\n ...(att.contentType ? { content_type: att.contentType } : {}),\n })),\n }\n : {}),\n };\n\n const response = await fetch(`${this.baseUrl}/emails`, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${this.apiKey}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(body),\n });\n\n const payload = (await response.json()) as { id?: string; message?: string };\n\n if (!response.ok) {\n throw new ResendError(payload.message ?? \"Resend API error\", response.status, payload);\n }\n\n const accepted = extractEmails(options.to);\n return {\n messageId: payload.id ?? options.messageId ?? \"\",\n accepted,\n rejected: [],\n response: payload.message ?? \"Email sent\",\n envelope: {\n from: from?.address ?? \"\",\n to: [\n ...extractEmails(options.to),\n ...(options.cc ? extractEmails(options.cc) : []),\n ...(options.bcc ? extractEmails(options.bcc) : []),\n ],\n },\n };\n }\n\n /** Verifies the Resend API key by listing domains. */\n async verify(): Promise<VerifyResult> {\n try {\n const response = await fetch(`${this.baseUrl}/domains`, {\n headers: {\n Authorization: `Bearer ${this.apiKey}`,\n },\n });\n\n if (!response.ok) {\n const payload = (await response.json().catch(() => ({}))) as { message?: string };\n return {\n ok: false,\n provider: \"resend\",\n message: payload.message ?? `HTTP ${response.status}`,\n };\n }\n\n return { ok: true, provider: \"resend\", message: \"API key is valid\" };\n } catch (err) {\n return {\n ok: false,\n provider: \"resend\",\n message: err instanceof Error ? err.message : String(err),\n };\n }\n }\n}\n"
5
+ "/**\n * @module\n * Resend HTTP API transport for sending email via api.resend.com.\n *\n * @example\n * ```ts\n * import { ResendTransport } from \"sently/transports/resend\";\n * import { createMailer } from \"sently\";\n *\n * const mailer = await createMailer({\n * transport: new ResendTransport({ apiKey: process.env.RESEND_API_KEY! }),\n * });\n *\n * await mailer.send({\n * from: \"onboarding@yourdomain.com\",\n * to: \"recipient@example.com\",\n * subject: \"Hello\",\n * html: \"<p>Sent via Resend</p>\",\n * });\n * ```\n */\nimport { extractEmails, parseAddresses, toMIMEHeader } from \"../core/address.js\";\nimport { encodeBase64 } from \"../core/base64.js\";\nimport type { MailOptions, SendResult, Transport, VerifyResult } from \"../core/types.js\";\nimport { resolveAttachments } from \"./resolve-attachments.js\";\n\n/** Resend API configuration. */\nexport interface ResendConfig {\n /** Resend API key (starts with `re_`). */\n apiKey: string;\n /** API base URL. Default: `https://api.resend.com`. */\n baseUrl?: string;\n}\n\n/** Error thrown when the Resend API returns a non-success response. */\nexport class ResendError extends Error {\n /** Creates a Resend API error with status code and response payload. */\n constructor(\n message: string,\n public readonly statusCode: number,\n public readonly apiError: unknown,\n ) {\n super(message);\n this.name = \"ResendError\";\n }\n}\n\n/**\n * Resend HTTP API transport.\n */\nexport class ResendTransport implements Transport {\n /** Resend API key for Bearer authentication. */\n private readonly apiKey: string;\n /** Resend API base URL. */\n private readonly baseUrl: string;\n\n /** Creates a Resend transport with the given API key. */\n constructor(config: ResendConfig) {\n this.apiKey = config.apiKey;\n this.baseUrl = config.baseUrl ?? \"https://api.resend.com\";\n }\n\n /** Sends an email via the Resend HTTP API. */\n async send(options: MailOptions): Promise<SendResult> {\n const attachments = await resolveAttachments(options.attachments);\n const from = parseAddresses(options.from)[0];\n const body = {\n from: from ? toMIMEHeader(from) : \"\",\n to: extractEmails(options.to),\n subject: options.subject,\n ...(options.cc ? { cc: extractEmails(options.cc) } : {}),\n ...(options.bcc ? { bcc: extractEmails(options.bcc) } : {}),\n ...(options.replyTo ? { reply_to: extractEmails(options.replyTo) } : {}),\n ...(options.text ? { text: options.text } : {}),\n ...(options.html ? { html: options.html } : {}),\n ...(options.headers ? { headers: options.headers } : {}),\n ...(attachments.length > 0\n ? {\n attachments: attachments.map((att) => ({\n filename: att.filename,\n content:\n att.content instanceof Uint8Array\n ? encodeBase64(att.content).replace(/\\r\\n/g, \"\")\n : att.content,\n ...(att.contentType ? { content_type: att.contentType } : {}),\n })),\n }\n : {}),\n };\n\n const response = await fetch(`${this.baseUrl}/emails`, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${this.apiKey}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(body),\n });\n\n const payload = (await response.json()) as { id?: string; message?: string };\n\n if (!response.ok) {\n throw new ResendError(payload.message ?? \"Resend API error\", response.status, payload);\n }\n\n const accepted = extractEmails(options.to);\n return {\n messageId: payload.id ?? options.messageId ?? \"\",\n accepted,\n rejected: [],\n response: payload.message ?? \"Email sent\",\n envelope: {\n from: from?.address ?? \"\",\n to: [\n ...extractEmails(options.to),\n ...(options.cc ? extractEmails(options.cc) : []),\n ...(options.bcc ? extractEmails(options.bcc) : []),\n ],\n },\n };\n }\n\n /** Verifies the Resend API key by listing domains. */\n async verify(): Promise<VerifyResult> {\n try {\n const response = await fetch(`${this.baseUrl}/domains`, {\n headers: {\n Authorization: `Bearer ${this.apiKey}`,\n },\n });\n\n if (!response.ok) {\n const payload = (await response.json().catch(() => ({}))) as { message?: string };\n return {\n ok: false,\n provider: \"resend\",\n message: payload.message ?? `HTTP ${response.status}`,\n };\n }\n\n return { ok: true, provider: \"resend\", message: \"API key is valid\" };\n } catch (err) {\n return {\n ok: false,\n provider: \"resend\",\n message: err instanceof Error ? err.message : String(err),\n };\n }\n }\n}\n"
6
6
  ],
7
- "mappings": "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",
7
+ "mappings": "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",
8
8
  "debugId": "C97D4F9F26828E0A64756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -3,15 +3,26 @@ import type { MailOptions, RetryConfig, SendResult, Transport, VerifyResult } fr
3
3
  * Decorator transport that retries failed sends with configurable backoff.
4
4
  */
5
5
  export declare class RetryTransport implements Transport {
6
+ /** Transport that performs the actual send on each attempt. */
6
7
  private readonly inner;
8
+ /** Injectable sleep function for testing backoff timing. */
7
9
  private readonly _sleep;
10
+ /** Maximum send attempts including the initial try. */
8
11
  private readonly maxAttempts;
12
+ /** Backoff strategy between retries. */
9
13
  private readonly backoff;
14
+ /** Base delay in milliseconds before the first retry. */
10
15
  private readonly baseDelay;
16
+ /** HTTP status codes that trigger a retry. */
11
17
  private readonly retryOn;
18
+ /** Optional callback invoked before each retry attempt. */
12
19
  private readonly onRetry;
13
20
  /** Wraps an inner transport with retry logic and optional backoff configuration. */
14
- constructor(inner: Transport, config?: RetryConfig, _sleep?: (ms: number) => Promise<void>);
21
+ constructor(
22
+ /** Transport that performs the actual send on each attempt. */
23
+ inner: Transport, config?: RetryConfig,
24
+ /** Injectable sleep function for testing backoff timing. */
25
+ _sleep?: (ms: number) => Promise<void>);
15
26
  /** Sends with retries according to configured backoff and retry rules. */
16
27
  send(options: MailOptions): Promise<SendResult>;
17
28
  /** Delegates to the inner transport verify or returns a default success result. */
@@ -2,9 +2,9 @@
2
2
  "version": 3,
3
3
  "sources": ["../src/transports/retry.ts"],
4
4
  "sourcesContent": [
5
- "/**\n * @module\n * RetryTransport — decorator that wraps any sently transport\n * and retries failed sends with configurable backoff.\n * Works with SMTP, Resend, SendGrid, SES, and any custom transport.\n *\n * @example\n * ```ts\n * import { RetryTransport } from 'sently/transports/retry'\n * import { ResendTransport } from 'sently/transports/resend'\n *\n * const transport = new RetryTransport(\n * new ResendTransport({ apiKey }),\n * { maxAttempts: 3, backoff: 'exponential', retryOn: [429, 503] }\n * )\n * ```\n */\nimport { SMTPError } from \"../core/smtp.js\";\nimport type {\n MailOptions,\n RetryConfig,\n SendResult,\n Transport,\n VerifyResult,\n} from \"../core/types.js\";\n\nconst DEFAULT_RETRY_ON = [429, 500, 502, 503, 504];\n\nfunction computeDelay(attempt: number, backoff: string, base: number): number {\n if (backoff === \"exponential\") {\n return base * 2 ** (attempt - 1);\n }\n if (backoff === \"linear\") {\n return base * attempt;\n }\n return base;\n}\n\nfunction shouldRetry(err: unknown, retryOn: number[]): boolean {\n if (err instanceof SMTPError && err.code === 535) {\n return false;\n }\n\n if (\n typeof err === \"object\" &&\n err !== null &&\n \"statusCode\" in err &&\n typeof (err as { statusCode: unknown }).statusCode === \"number\"\n ) {\n return retryOn.includes((err as { statusCode: number }).statusCode);\n }\n\n return true;\n}\n\n/**\n * Decorator transport that retries failed sends with configurable backoff.\n */\nexport class RetryTransport implements Transport {\n private readonly maxAttempts: number;\n private readonly backoff: \"exponential\" | \"linear\" | \"fixed\";\n private readonly baseDelay: number;\n private readonly retryOn: number[];\n private readonly onRetry: RetryConfig[\"onRetry\"];\n\n /** Wraps an inner transport with retry logic and optional backoff configuration. */\n constructor(\n private readonly inner: Transport,\n config?: RetryConfig,\n private readonly _sleep: (ms: number) => Promise<void> = (ms) =>\n new Promise((resolve) => setTimeout(resolve, ms)),\n ) {\n this.maxAttempts = config?.maxAttempts ?? 3;\n this.backoff = config?.backoff ?? \"exponential\";\n this.baseDelay = config?.baseDelay ?? 1000;\n this.retryOn = config?.retryOn ?? DEFAULT_RETRY_ON;\n this.onRetry = config?.onRetry;\n }\n\n /** Sends with retries according to configured backoff and retry rules. */\n async send(options: MailOptions): Promise<SendResult> {\n let lastError: unknown;\n\n for (let attempt = 1; attempt <= this.maxAttempts; attempt++) {\n try {\n return await this.inner.send(options);\n } catch (err) {\n lastError = err;\n if (attempt === this.maxAttempts) {\n throw err;\n }\n if (!shouldRetry(err, this.retryOn)) {\n throw err;\n }\n const delay = computeDelay(attempt, this.backoff, this.baseDelay);\n this.onRetry?.(attempt, err);\n await this._sleep(delay);\n }\n }\n\n throw lastError;\n }\n\n /** Delegates to the inner transport verify or returns a default success result. */\n async verify(): Promise<VerifyResult> {\n if (this.inner.verify) {\n return this.inner.verify();\n }\n return { ok: true, provider: \"retry\" };\n }\n\n /** Delegates close to the inner transport if available. */\n async close(): Promise<void> {\n await this.inner.close?.();\n }\n}\n"
5
+ "/**\n * @module\n * RetryTransport — decorator that wraps any sently transport\n * and retries failed sends with configurable backoff.\n * Works with SMTP, Resend, SendGrid, SES, and any custom transport.\n *\n * @example\n * ```ts\n * import { RetryTransport } from 'sently/transports/retry'\n * import { ResendTransport } from 'sently/transports/resend'\n *\n * const transport = new RetryTransport(\n * new ResendTransport({ apiKey }),\n * { maxAttempts: 3, backoff: 'exponential', retryOn: [429, 503] }\n * )\n * ```\n */\nimport { SMTPError } from \"../core/smtp.js\";\nimport type {\n MailOptions,\n RetryConfig,\n SendResult,\n Transport,\n VerifyResult,\n} from \"../core/types.js\";\n\nconst DEFAULT_RETRY_ON = [429, 500, 502, 503, 504];\n\nfunction computeDelay(attempt: number, backoff: string, base: number): number {\n if (backoff === \"exponential\") {\n return base * 2 ** (attempt - 1);\n }\n if (backoff === \"linear\") {\n return base * attempt;\n }\n return base;\n}\n\nfunction shouldRetry(err: unknown, retryOn: number[]): boolean {\n if (err instanceof SMTPError && err.code === 535) {\n return false;\n }\n\n if (\n typeof err === \"object\" &&\n err !== null &&\n \"statusCode\" in err &&\n typeof (err as { statusCode: unknown }).statusCode === \"number\"\n ) {\n return retryOn.includes((err as { statusCode: number }).statusCode);\n }\n\n return true;\n}\n\n/**\n * Decorator transport that retries failed sends with configurable backoff.\n */\nexport class RetryTransport implements Transport {\n /** Maximum send attempts including the initial try. */\n private readonly maxAttempts: number;\n /** Backoff strategy between retries. */\n private readonly backoff: \"exponential\" | \"linear\" | \"fixed\";\n /** Base delay in milliseconds before the first retry. */\n private readonly baseDelay: number;\n /** HTTP status codes that trigger a retry. */\n private readonly retryOn: number[];\n /** Optional callback invoked before each retry attempt. */\n private readonly onRetry: RetryConfig[\"onRetry\"];\n\n /** Wraps an inner transport with retry logic and optional backoff configuration. */\n constructor(\n /** Transport that performs the actual send on each attempt. */\n private readonly inner: Transport,\n config?: RetryConfig,\n /** Injectable sleep function for testing backoff timing. */\n private readonly _sleep: (ms: number) => Promise<void> = (ms) =>\n new Promise((resolve) => setTimeout(resolve, ms)),\n ) {\n this.maxAttempts = config?.maxAttempts ?? 3;\n this.backoff = config?.backoff ?? \"exponential\";\n this.baseDelay = config?.baseDelay ?? 1000;\n this.retryOn = config?.retryOn ?? DEFAULT_RETRY_ON;\n this.onRetry = config?.onRetry;\n }\n\n /** Sends with retries according to configured backoff and retry rules. */\n async send(options: MailOptions): Promise<SendResult> {\n let lastError: unknown;\n\n for (let attempt = 1; attempt <= this.maxAttempts; attempt++) {\n try {\n return await this.inner.send(options);\n } catch (err) {\n lastError = err;\n if (attempt === this.maxAttempts) {\n throw err;\n }\n if (!shouldRetry(err, this.retryOn)) {\n throw err;\n }\n const delay = computeDelay(attempt, this.backoff, this.baseDelay);\n this.onRetry?.(attempt, err);\n await this._sleep(delay);\n }\n }\n\n throw lastError;\n }\n\n /** Delegates to the inner transport verify or returns a default success result. */\n async verify(): Promise<VerifyResult> {\n if (this.inner.verify) {\n return this.inner.verify();\n }\n return { ok: true, provider: \"retry\" };\n }\n\n /** Delegates close to the inner transport if available. */\n async close(): Promise<void> {\n await this.inner.close?.();\n }\n}\n"
6
6
  ],
7
- "mappings": ";;;;;;;AA0BA,IAAM,mBAAmB,CAAC,KAAK,KAAK,KAAK,KAAK,GAAG;AAEjD,SAAS,YAAY,CAAC,SAAiB,SAAiB,MAAsB;AAAA,EAC5E,IAAI,YAAY,eAAe;AAAA,IAC7B,OAAO,OAAO,MAAM,UAAU;AAAA,EAChC;AAAA,EACA,IAAI,YAAY,UAAU;AAAA,IACxB,OAAO,OAAO;AAAA,EAChB;AAAA,EACA,OAAO;AAAA;AAGT,SAAS,WAAW,CAAC,KAAc,SAA4B;AAAA,EAC7D,IAAI,eAAe,aAAa,IAAI,SAAS,KAAK;AAAA,IAChD,OAAO;AAAA,EACT;AAAA,EAEA,IACE,OAAO,QAAQ,YACf,QAAQ,QACR,gBAAgB,OAChB,OAAQ,IAAgC,eAAe,UACvD;AAAA,IACA,OAAO,QAAQ,SAAU,IAA+B,UAAU;AAAA,EACpE;AAAA,EAEA,OAAO;AAAA;AAAA;AAMF,MAAM,eAAoC;AAAA,EAS5B;AAAA,EAEA;AAAA,EAVF;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAGjB,WAAW,CACQ,OACjB,QACiB,SAAwC,CAAC,OACxD,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,EAAE,CAAC,GAClD;AAAA,IAJiB;AAAA,IAEA;AAAA,IAGjB,KAAK,cAAc,QAAQ,eAAe;AAAA,IAC1C,KAAK,UAAU,QAAQ,WAAW;AAAA,IAClC,KAAK,YAAY,QAAQ,aAAa;AAAA,IACtC,KAAK,UAAU,QAAQ,WAAW;AAAA,IAClC,KAAK,UAAU,QAAQ;AAAA;AAAA,OAInB,KAAI,CAAC,SAA2C;AAAA,IACpD,IAAI;AAAA,IAEJ,SAAS,UAAU,EAAG,WAAW,KAAK,aAAa,WAAW;AAAA,MAC5D,IAAI;AAAA,QACF,OAAO,MAAM,KAAK,MAAM,KAAK,OAAO;AAAA,QACpC,OAAO,KAAK;AAAA,QACZ,YAAY;AAAA,QACZ,IAAI,YAAY,KAAK,aAAa;AAAA,UAChC,MAAM;AAAA,QACR;AAAA,QACA,IAAI,CAAC,YAAY,KAAK,KAAK,OAAO,GAAG;AAAA,UACnC,MAAM;AAAA,QACR;AAAA,QACA,MAAM,QAAQ,aAAa,SAAS,KAAK,SAAS,KAAK,SAAS;AAAA,QAChE,KAAK,UAAU,SAAS,GAAG;AAAA,QAC3B,MAAM,KAAK,OAAO,KAAK;AAAA;AAAA,IAE3B;AAAA,IAEA,MAAM;AAAA;AAAA,OAIF,OAAM,GAA0B;AAAA,IACpC,IAAI,KAAK,MAAM,QAAQ;AAAA,MACrB,OAAO,KAAK,MAAM,OAAO;AAAA,IAC3B;AAAA,IACA,OAAO,EAAE,IAAI,MAAM,UAAU,QAAQ;AAAA;AAAA,OAIjC,MAAK,GAAkB;AAAA,IAC3B,MAAM,KAAK,MAAM,QAAQ;AAAA;AAE7B;",
7
+ "mappings": ";;;;;;;AA0BA,IAAM,mBAAmB,CAAC,KAAK,KAAK,KAAK,KAAK,GAAG;AAEjD,SAAS,YAAY,CAAC,SAAiB,SAAiB,MAAsB;AAAA,EAC5E,IAAI,YAAY,eAAe;AAAA,IAC7B,OAAO,OAAO,MAAM,UAAU;AAAA,EAChC;AAAA,EACA,IAAI,YAAY,UAAU;AAAA,IACxB,OAAO,OAAO;AAAA,EAChB;AAAA,EACA,OAAO;AAAA;AAGT,SAAS,WAAW,CAAC,KAAc,SAA4B;AAAA,EAC7D,IAAI,eAAe,aAAa,IAAI,SAAS,KAAK;AAAA,IAChD,OAAO;AAAA,EACT;AAAA,EAEA,IACE,OAAO,QAAQ,YACf,QAAQ,QACR,gBAAgB,OAChB,OAAQ,IAAgC,eAAe,UACvD;AAAA,IACA,OAAO,QAAQ,SAAU,IAA+B,UAAU;AAAA,EACpE;AAAA,EAEA,OAAO;AAAA;AAAA;AAMF,MAAM,eAAoC;AAAA,EAe5B;AAAA,EAGA;AAAA,EAhBF;AAAA,EAEA;AAAA,EAEA;AAAA,EAEA;AAAA,EAEA;AAAA,EAGjB,WAAW,CAEQ,OACjB,QAEiB,SAAwC,CAAC,OACxD,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,EAAE,CAAC,GAClD;AAAA,IALiB;AAAA,IAGA;AAAA,IAGjB,KAAK,cAAc,QAAQ,eAAe;AAAA,IAC1C,KAAK,UAAU,QAAQ,WAAW;AAAA,IAClC,KAAK,YAAY,QAAQ,aAAa;AAAA,IACtC,KAAK,UAAU,QAAQ,WAAW;AAAA,IAClC,KAAK,UAAU,QAAQ;AAAA;AAAA,OAInB,KAAI,CAAC,SAA2C;AAAA,IACpD,IAAI;AAAA,IAEJ,SAAS,UAAU,EAAG,WAAW,KAAK,aAAa,WAAW;AAAA,MAC5D,IAAI;AAAA,QACF,OAAO,MAAM,KAAK,MAAM,KAAK,OAAO;AAAA,QACpC,OAAO,KAAK;AAAA,QACZ,YAAY;AAAA,QACZ,IAAI,YAAY,KAAK,aAAa;AAAA,UAChC,MAAM;AAAA,QACR;AAAA,QACA,IAAI,CAAC,YAAY,KAAK,KAAK,OAAO,GAAG;AAAA,UACnC,MAAM;AAAA,QACR;AAAA,QACA,MAAM,QAAQ,aAAa,SAAS,KAAK,SAAS,KAAK,SAAS;AAAA,QAChE,KAAK,UAAU,SAAS,GAAG;AAAA,QAC3B,MAAM,KAAK,OAAO,KAAK;AAAA;AAAA,IAE3B;AAAA,IAEA,MAAM;AAAA;AAAA,OAIF,OAAM,GAA0B;AAAA,IACpC,IAAI,KAAK,MAAM,QAAQ;AAAA,MACrB,OAAO,KAAK,MAAM,OAAO;AAAA,IAC3B;AAAA,IACA,OAAO,EAAE,IAAI,MAAM,UAAU,QAAQ;AAAA;AAAA,OAIjC,MAAK,GAAkB;AAAA,IAC3B,MAAM,KAAK,MAAM,QAAQ;AAAA;AAE7B;",
8
8
  "debugId": "F5AD32E7B75CAE2A64756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -15,6 +15,7 @@ export declare class SendGridError extends Error {
15
15
  * SendGrid v3 HTTP API transport.
16
16
  */
17
17
  export declare class SendGridTransport implements Transport {
18
+ /** SendGrid API key for Bearer authentication. */
18
19
  private readonly apiKey;
19
20
  /** Creates a SendGrid transport with the given API key. */
20
21
  constructor(config: SendGridConfig);
@@ -2,9 +2,9 @@
2
2
  "version": 3,
3
3
  "sources": ["../src/transports/sendgrid.ts"],
4
4
  "sourcesContent": [
5
- "/**\n * @module\n * SendGrid v3 HTTP API transport for sending email via api.sendgrid.com.\n *\n * @example\n * ```ts\n * import { SendGridTransport } from \"sently/transports/sendgrid\";\n * import { createMailer } from \"sently\";\n *\n * const mailer = await createMailer({\n * transport: new SendGridTransport({ apiKey: process.env.SENDGRID_API_KEY! }),\n * });\n *\n * await mailer.send({\n * from: \"sender@example.com\",\n * to: \"recipient@example.com\",\n * subject: \"Hello\",\n * text: \"Plain text body\",\n * });\n * ```\n */\nimport { extractEmails, parseAddresses } from \"../core/address.js\";\nimport { encodeBase64 } from \"../core/base64.js\";\nimport type { MailOptions, SendResult, Transport, VerifyResult } from \"../core/types.js\";\nimport { resolveAttachments } from \"./resolve-attachments.js\";\n\n/** SendGrid API configuration. */\nexport interface SendGridConfig {\n /** SendGrid API key (Bearer token). */\n apiKey: string;\n}\n\n/** Error thrown when the SendGrid API returns a non-success response. */\nexport class SendGridError extends Error {\n /** Creates a SendGrid API error with status code and response payload. */\n constructor(\n message: string,\n public readonly statusCode: number,\n public readonly apiError: unknown,\n ) {\n super(message);\n this.name = \"SendGridError\";\n }\n}\n\n/**\n * SendGrid v3 HTTP API transport.\n */\nexport class SendGridTransport implements Transport {\n private readonly apiKey: string;\n\n /** Creates a SendGrid transport with the given API key. */\n constructor(config: SendGridConfig) {\n this.apiKey = config.apiKey;\n }\n\n /** Sends an email via the SendGrid v3 HTTP API. */\n async send(options: MailOptions): Promise<SendResult> {\n const attachments = await resolveAttachments(options.attachments);\n const from = parseAddresses(options.from)[0];\n\n const personalization = {\n to: parseAddresses(options.to).map((addr) => ({ email: addr.address, name: addr.name })),\n ...(options.cc\n ? {\n cc: parseAddresses(options.cc).map((addr) => ({\n email: addr.address,\n name: addr.name,\n })),\n }\n : {}),\n ...(options.bcc\n ? {\n bcc: parseAddresses(options.bcc).map((addr) => ({\n email: addr.address,\n name: addr.name,\n })),\n }\n : {}),\n };\n\n const body = {\n personalizations: [personalization],\n from: from\n ? { email: from.address, ...(from.name ? { name: from.name } : {}) }\n : { email: \"\" },\n subject: options.subject,\n ...(options.replyTo\n ? {\n reply_to: parseAddresses(options.replyTo).map((addr) => ({\n email: addr.address,\n name: addr.name,\n }))[0],\n }\n : {}),\n content: [\n ...(options.text ? [{ type: \"text/plain\", value: options.text }] : []),\n ...(options.html ? [{ type: \"text/html\", value: options.html }] : []),\n ],\n ...(attachments.length > 0\n ? {\n attachments: attachments.map((att) => ({\n filename: att.filename,\n type: att.contentType ?? \"application/octet-stream\",\n content:\n att.content instanceof Uint8Array\n ? encodeBase64(att.content).replace(/\\r\\n/g, \"\")\n : att.content,\n ...(att.contentId ? { content_id: att.contentId } : {}),\n disposition: att.inline ? \"inline\" : \"attachment\",\n })),\n }\n : {}),\n };\n\n const response = await fetch(\"https://api.sendgrid.com/v3/mail/send\", {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${this.apiKey}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n const apiError = await response.text();\n throw new SendGridError(\"SendGrid API error\", response.status, apiError);\n }\n\n const messageId = response.headers.get(\"x-message-id\") ?? options.messageId ?? \"\";\n\n return {\n messageId,\n accepted: extractEmails(options.to),\n rejected: [],\n response: \"Accepted\",\n envelope: {\n from: from?.address ?? \"\",\n to: [\n ...extractEmails(options.to),\n ...(options.cc ? extractEmails(options.cc) : []),\n ...(options.bcc ? extractEmails(options.bcc) : []),\n ],\n },\n };\n }\n\n /** Verifies the SendGrid API key by fetching the user profile. */\n async verify(): Promise<VerifyResult> {\n try {\n const response = await fetch(\"https://api.sendgrid.com/v3/user/profile\", {\n headers: {\n Authorization: `Bearer ${this.apiKey}`,\n },\n });\n\n if (!response.ok) {\n const apiError = await response.text().catch(() => \"\");\n return {\n ok: false,\n provider: \"sendgrid\",\n message: apiError || `HTTP ${response.status}`,\n };\n }\n\n const payload = (await response.json()) as { username?: string };\n return {\n ok: true,\n provider: \"sendgrid\",\n ...(payload.username ? { message: payload.username } : {}),\n };\n } catch (err) {\n return {\n ok: false,\n provider: \"sendgrid\",\n message: err instanceof Error ? err.message : String(err),\n };\n }\n }\n}\n"
5
+ "/**\n * @module\n * SendGrid v3 HTTP API transport for sending email via api.sendgrid.com.\n *\n * @example\n * ```ts\n * import { SendGridTransport } from \"sently/transports/sendgrid\";\n * import { createMailer } from \"sently\";\n *\n * const mailer = await createMailer({\n * transport: new SendGridTransport({ apiKey: process.env.SENDGRID_API_KEY! }),\n * });\n *\n * await mailer.send({\n * from: \"sender@example.com\",\n * to: \"recipient@example.com\",\n * subject: \"Hello\",\n * text: \"Plain text body\",\n * });\n * ```\n */\nimport { extractEmails, parseAddresses } from \"../core/address.js\";\nimport { encodeBase64 } from \"../core/base64.js\";\nimport type { MailOptions, SendResult, Transport, VerifyResult } from \"../core/types.js\";\nimport { resolveAttachments } from \"./resolve-attachments.js\";\n\n/** SendGrid API configuration. */\nexport interface SendGridConfig {\n /** SendGrid API key (Bearer token). */\n apiKey: string;\n}\n\n/** Error thrown when the SendGrid API returns a non-success response. */\nexport class SendGridError extends Error {\n /** Creates a SendGrid API error with status code and response payload. */\n constructor(\n message: string,\n public readonly statusCode: number,\n public readonly apiError: unknown,\n ) {\n super(message);\n this.name = \"SendGridError\";\n }\n}\n\n/**\n * SendGrid v3 HTTP API transport.\n */\nexport class SendGridTransport implements Transport {\n /** SendGrid API key for Bearer authentication. */\n private readonly apiKey: string;\n\n /** Creates a SendGrid transport with the given API key. */\n constructor(config: SendGridConfig) {\n this.apiKey = config.apiKey;\n }\n\n /** Sends an email via the SendGrid v3 HTTP API. */\n async send(options: MailOptions): Promise<SendResult> {\n const attachments = await resolveAttachments(options.attachments);\n const from = parseAddresses(options.from)[0];\n\n const personalization = {\n to: parseAddresses(options.to).map((addr) => ({ email: addr.address, name: addr.name })),\n ...(options.cc\n ? {\n cc: parseAddresses(options.cc).map((addr) => ({\n email: addr.address,\n name: addr.name,\n })),\n }\n : {}),\n ...(options.bcc\n ? {\n bcc: parseAddresses(options.bcc).map((addr) => ({\n email: addr.address,\n name: addr.name,\n })),\n }\n : {}),\n };\n\n const body = {\n personalizations: [personalization],\n from: from\n ? { email: from.address, ...(from.name ? { name: from.name } : {}) }\n : { email: \"\" },\n subject: options.subject,\n ...(options.replyTo\n ? {\n reply_to: parseAddresses(options.replyTo).map((addr) => ({\n email: addr.address,\n name: addr.name,\n }))[0],\n }\n : {}),\n content: [\n ...(options.text ? [{ type: \"text/plain\", value: options.text }] : []),\n ...(options.html ? [{ type: \"text/html\", value: options.html }] : []),\n ],\n ...(attachments.length > 0\n ? {\n attachments: attachments.map((att) => ({\n filename: att.filename,\n type: att.contentType ?? \"application/octet-stream\",\n content:\n att.content instanceof Uint8Array\n ? encodeBase64(att.content).replace(/\\r\\n/g, \"\")\n : att.content,\n ...(att.contentId ? { content_id: att.contentId } : {}),\n disposition: att.inline ? \"inline\" : \"attachment\",\n })),\n }\n : {}),\n };\n\n const response = await fetch(\"https://api.sendgrid.com/v3/mail/send\", {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${this.apiKey}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n const apiError = await response.text();\n throw new SendGridError(\"SendGrid API error\", response.status, apiError);\n }\n\n const messageId = response.headers.get(\"x-message-id\") ?? options.messageId ?? \"\";\n\n return {\n messageId,\n accepted: extractEmails(options.to),\n rejected: [],\n response: \"Accepted\",\n envelope: {\n from: from?.address ?? \"\",\n to: [\n ...extractEmails(options.to),\n ...(options.cc ? extractEmails(options.cc) : []),\n ...(options.bcc ? extractEmails(options.bcc) : []),\n ],\n },\n };\n }\n\n /** Verifies the SendGrid API key by fetching the user profile. */\n async verify(): Promise<VerifyResult> {\n try {\n const response = await fetch(\"https://api.sendgrid.com/v3/user/profile\", {\n headers: {\n Authorization: `Bearer ${this.apiKey}`,\n },\n });\n\n if (!response.ok) {\n const apiError = await response.text().catch(() => \"\");\n return {\n ok: false,\n provider: \"sendgrid\",\n message: apiError || `HTTP ${response.status}`,\n };\n }\n\n const payload = (await response.json()) as { username?: string };\n return {\n ok: true,\n provider: \"sendgrid\",\n ...(payload.username ? { message: payload.username } : {}),\n };\n } catch (err) {\n return {\n ok: false,\n provider: \"sendgrid\",\n message: err instanceof Error ? err.message : String(err),\n };\n }\n }\n}\n"
6
6
  ],
7
- "mappings": "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",
7
+ "mappings": "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",
8
8
  "debugId": "53D65F527A73629564756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -11,10 +11,15 @@ export declare class SESError extends Error {
11
11
  * AWS SES v2 HTTP API transport.
12
12
  */
13
13
  export declare class SESTransport implements Transport {
14
+ /** AWS access key ID for SigV4 signing. */
14
15
  private readonly accessKeyId;
16
+ /** AWS secret access key for SigV4 signing. */
15
17
  private readonly secretAccessKey;
18
+ /** AWS region for the SES endpoint. */
16
19
  private readonly region;
20
+ /** Optional STS session token for temporary credentials. */
17
21
  private readonly sessionToken;
22
+ /** Optional DKIM config for raw MIME attachment sends. */
18
23
  private readonly dkim;
19
24
  /** Creates an SES transport with AWS credentials. */
20
25
  constructor(config: SESConfig);
@@ -2,10 +2,10 @@
2
2
  "version": 3,
3
3
  "sources": ["../src/core/sigv4.ts", "../src/transports/ses.ts"],
4
4
  "sourcesContent": [
5
- "/**\n * @module\n * AWS Signature Version 4 signing using Web Crypto (HMAC-SHA256).\n * Works on Node.js, Bun, Deno, and Cloudflare Workers.\n * No external dependencies.\n *\n * @example\n * ```ts\n * import { signRequest } from \"sently/core/sigv4\";\n * const signed = await signRequest({\n * method: \"POST\",\n * url: \"https://email.us-east-1.amazonaws.com/v2/email/outbound-emails\",\n * headers: { \"content-type\": \"application/json\" },\n * body: '{\"...\"}',\n * credentials: { accessKeyId, secretAccessKey, region: \"us-east-1\", service: \"ses\" },\n * });\n * ```\n */\n\n/** AWS credentials and signing scope for SigV4. */\nexport interface SigV4Credentials {\n accessKeyId: string;\n secretAccessKey: string;\n region: string;\n service: string;\n sessionToken?: string;\n}\n\n/** HTTP request to sign with AWS Signature Version 4. */\nexport interface SigV4Request {\n method: string;\n url: string;\n headers: Record<string, string>;\n body: string;\n credentials: SigV4Credentials;\n /** Override datetime for testing. Full 'YYYYMMDDTHHMMSSZ' when provided. */\n _date?: string;\n}\n\n/** Signed request headers including Authorization. */\nexport interface SigV4Result {\n /** All headers including Authorization, x-amz-date, and x-amz-security-token */\n headers: Record<string, string>;\n}\n\nconst encoder = new TextEncoder();\n\n/**\n * Compute SHA-256 hash of a string using Web Crypto.\n * Returns lowercase hex string.\n * @internal\n */\nexport async function sha256Hex(data: string): Promise<string> {\n const hash = await crypto.subtle.digest(\"SHA-256\", encoder.encode(data));\n return bytesToHex(new Uint8Array(hash));\n}\n\n/**\n * Compute HMAC-SHA256 using Web Crypto.\n * @internal\n */\nexport async function hmacSHA256(key: Uint8Array | string, data: string): Promise<Uint8Array> {\n const keyBytes = typeof key === \"string\" ? encoder.encode(key) : new Uint8Array(key);\n const cryptoKey = await crypto.subtle.importKey(\n \"raw\",\n keyBytes,\n { name: \"HMAC\", hash: \"SHA-256\" },\n false,\n [\"sign\"],\n );\n const signature = await crypto.subtle.sign(\"HMAC\", cryptoKey, encoder.encode(data));\n return new Uint8Array(signature);\n}\n\n/**\n * Sign an HTTP request with AWS Signature Version 4.\n * Returns the complete set of headers to include in the request.\n */\nexport async function signRequest(request: SigV4Request): Promise<SigV4Result> {\n const { method, url, body, credentials } = request;\n const parsed = new URL(url);\n const amzDate = request._date ?? `${new Date().toISOString().replace(/[-:]/g, \"\").slice(0, 15)}Z`;\n const dateStamp = amzDate.slice(0, 8);\n\n const headers: Record<string, string> = {\n ...Object.fromEntries(\n Object.entries(request.headers).map(([key, value]) => [key.toLowerCase(), value.trim()]),\n ),\n host: parsed.host,\n \"x-amz-date\": amzDate,\n };\n\n if (credentials.sessionToken) {\n headers[\"x-amz-security-token\"] = credentials.sessionToken;\n }\n\n const signedHeaderNames = Object.keys(headers)\n .map((name) => name.toLowerCase())\n .sort();\n const signedHeaders = signedHeaderNames.join(\";\");\n const canonicalHeaders = `${signedHeaderNames.map((name) => `${name}:${headers[name]}`).join(\"\\n\")}\\n`;\n const canonicalQuery = normalizeQuery(parsed.searchParams);\n const payloadHash = await sha256Hex(body);\n const canonicalRequest = [\n method.toUpperCase(),\n canonicalUri(parsed.pathname),\n canonicalQuery,\n canonicalHeaders,\n signedHeaders,\n payloadHash,\n ].join(\"\\n\");\n\n const credentialScope = `${dateStamp}/${credentials.region}/${credentials.service}/aws4_request`;\n const stringToSign = [\n \"AWS4-HMAC-SHA256\",\n amzDate,\n credentialScope,\n await sha256Hex(canonicalRequest),\n ].join(\"\\n\");\n\n const signingKey = await deriveSigningKey(\n credentials.secretAccessKey,\n dateStamp,\n credentials.region,\n credentials.service,\n );\n const signature = bytesToHex(await hmacSHA256(signingKey, stringToSign));\n const authorization = [\n `AWS4-HMAC-SHA256 Credential=${credentials.accessKeyId}/${credentialScope}`,\n `SignedHeaders=${signedHeaders}`,\n `Signature=${signature}`,\n ].join(\", \");\n\n return {\n headers: {\n ...headers,\n Authorization: authorization,\n },\n };\n}\n\nasync function deriveSigningKey(\n secretAccessKey: string,\n dateStamp: string,\n region: string,\n service: string,\n): Promise<Uint8Array> {\n const kDate = await hmacSHA256(`AWS4${secretAccessKey}`, dateStamp);\n const kRegion = await hmacSHA256(kDate, region);\n const kService = await hmacSHA256(kRegion, service);\n return hmacSHA256(kService, \"aws4_request\");\n}\n\nfunction canonicalUri(pathname: string): string {\n if (!pathname || pathname === \"/\") {\n return \"/\";\n }\n return pathname\n .split(\"/\")\n .map((segment) => encodeURIComponent(decodeURIComponent(segment)))\n .join(\"/\");\n}\n\nfunction normalizeQuery(searchParams: URLSearchParams): string {\n const pairs: string[] = [];\n for (const [key, value] of searchParams.entries()) {\n pairs.push(`${encodeRfc3986(key)}=${encodeRfc3986(value)}`);\n }\n pairs.sort();\n return pairs.join(\"&\");\n}\n\nfunction encodeRfc3986(value: string): string {\n return encodeURIComponent(value).replace(\n /[!'()*]/g,\n (char) => `%${char.charCodeAt(0).toString(16).toUpperCase()}`,\n );\n}\n\nfunction bytesToHex(bytes: Uint8Array): string {\n return Array.from(bytes, (byte) => byte.toString(16).padStart(2, \"0\")).join(\"\");\n}\n",
6
- "/**\n * @module\n * AWS SES v2 HTTP transport for sently.\n * Signs requests with AWS Signature Version 4 using Web Crypto.\n * Works on Node.js, Bun, Deno, and Cloudflare Workers.\n *\n * @example\n * ```ts\n * import { SESTransport } from \"sently/transports/ses\";\n * import { createMailer } from \"sently\";\n *\n * const mailer = await createMailer({\n * transport: new SESTransport({\n * accessKeyId: process.env.AWS_ACCESS_KEY_ID!,\n * secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,\n * region: \"us-east-1\",\n * }),\n * });\n * ```\n */\nimport { extractEmails, parseAddresses, toMIMEHeader } from \"../core/address.js\";\nimport { encodeBase64 } from \"../core/base64.js\";\nimport { buildMIME } from \"../core/mime.js\";\nimport { signRequest } from \"../core/sigv4.js\";\nimport type { MailOptions, SESConfig, SendResult, Transport, VerifyResult } from \"../core/types.js\";\nimport { resolveAttachments } from \"./resolve-attachments.js\";\n\n/** Error thrown when the AWS SES API returns a non-success response. */\nexport class SESError extends Error {\n /** Creates an AWS SES API error with status code, error code, and request ID. */\n constructor(\n message: string,\n public readonly statusCode: number,\n public readonly code: string,\n public readonly requestId: string,\n ) {\n super(message);\n this.name = \"SESError\";\n }\n}\n\n/**\n * AWS SES v2 HTTP API transport.\n */\nexport class SESTransport implements Transport {\n private readonly accessKeyId: string;\n private readonly secretAccessKey: string;\n private readonly region: string;\n private readonly sessionToken: string | undefined;\n private readonly dkim: SESConfig[\"dkim\"];\n\n /** Creates an SES transport with AWS credentials. */\n constructor(config: SESConfig) {\n this.accessKeyId = config.accessKeyId;\n this.secretAccessKey = config.secretAccessKey;\n this.region = config.region ?? \"us-east-1\";\n this.sessionToken = config.sessionToken;\n this.dkim = config.dkim;\n }\n\n /** Sends an email via the AWS SES v2 HTTP API. */\n async send(options: MailOptions): Promise<SendResult> {\n const attachments = await resolveAttachments(options.attachments);\n const resolvedOptions = { ...options, attachments };\n const from = parseAddresses(options.from)[0];\n const fromEmail = from ? toMIMEHeader(from) : \"\";\n const toEmails = extractEmails(options.to);\n const ccEmails = options.cc ? extractEmails(options.cc) : [];\n const bccEmails = options.bcc ? extractEmails(options.bcc) : [];\n\n const destination = {\n ToAddresses: toEmails,\n CcAddresses: ccEmails,\n BccAddresses: bccEmails,\n };\n\n let requestBody: Record<string, unknown>;\n\n if (attachments.length > 0) {\n const mime = await buildMIME(resolvedOptions, this.dkim);\n requestBody = {\n FromEmailAddress: fromEmail,\n Destination: destination,\n Content: {\n Raw: {\n Data: encodeBase64(mime.raw).replace(/\\r\\n/g, \"\"),\n },\n },\n };\n } else {\n requestBody = {\n FromEmailAddress: fromEmail,\n Destination: destination,\n Content: {\n Simple: {\n Subject: { Data: options.subject, Charset: \"UTF-8\" },\n Body: {\n ...(options.text ? { Text: { Data: options.text, Charset: \"UTF-8\" } } : {}),\n ...(options.html ? { Html: { Data: options.html, Charset: \"UTF-8\" } } : {}),\n },\n },\n },\n };\n }\n\n const body = JSON.stringify(requestBody);\n const url = `https://email.${this.region}.amazonaws.com/v2/email/outbound-emails`;\n const signed = await signRequest({\n method: \"POST\",\n url,\n headers: {\n \"content-type\": \"application/json\",\n },\n body,\n credentials: {\n accessKeyId: this.accessKeyId,\n secretAccessKey: this.secretAccessKey,\n region: this.region,\n service: \"ses\",\n ...(this.sessionToken ? { sessionToken: this.sessionToken } : {}),\n },\n });\n\n const response = await fetch(url, {\n method: \"POST\",\n headers: signed.headers,\n body,\n });\n\n const payload = (await response.json()) as {\n MessageId?: string;\n message?: string;\n Code?: string;\n };\n\n if (!response.ok) {\n throw new SESError(\n payload.message ?? \"SES API error\",\n response.status,\n payload.Code ?? \"\",\n response.headers.get(\"x-amzn-requestid\") ?? \"\",\n );\n }\n\n const messageId = payload.MessageId ?? \"\";\n return {\n messageId,\n accepted: [...toEmails, ...ccEmails, ...bccEmails],\n rejected: [],\n response: `MessageId: ${messageId}`,\n envelope: {\n from: from?.address ?? \"\",\n to: toEmails,\n },\n };\n }\n\n /** Verifies AWS credentials by listing SES configuration sets. */\n async verify(): Promise<VerifyResult> {\n try {\n const url = `https://email.${this.region}.amazonaws.com/v2/email/configuration-sets`;\n const signed = await signRequest({\n method: \"GET\",\n url,\n headers: {},\n body: \"\",\n credentials: {\n accessKeyId: this.accessKeyId,\n secretAccessKey: this.secretAccessKey,\n region: this.region,\n service: \"ses\",\n ...(this.sessionToken ? { sessionToken: this.sessionToken } : {}),\n },\n });\n\n const response = await fetch(url, {\n method: \"GET\",\n headers: signed.headers,\n });\n\n const payload = (await response.json().catch(() => ({}))) as { message?: string };\n\n if (!response.ok) {\n return {\n ok: false,\n provider: \"ses\",\n message: payload.message ?? `HTTP ${response.status}`,\n };\n }\n\n return { ok: true, provider: \"ses\", message: \"Credentials are valid\" };\n } catch (err) {\n return {\n ok: false,\n provider: \"ses\",\n message: err instanceof Error ? err.message : String(err),\n };\n }\n }\n}\n"
5
+ "/**\n * @module\n * AWS Signature Version 4 signing using Web Crypto (HMAC-SHA256).\n * Works on Node.js, Bun, Deno, and Cloudflare Workers.\n * No external dependencies.\n *\n * @example\n * ```ts\n * import { signRequest } from \"sently/core/sigv4\";\n * const signed = await signRequest({\n * method: \"POST\",\n * url: \"https://email.us-east-1.amazonaws.com/v2/email/outbound-emails\",\n * headers: { \"content-type\": \"application/json\" },\n * body: '{\"...\"}',\n * credentials: { accessKeyId, secretAccessKey, region: \"us-east-1\", service: \"ses\" },\n * });\n * ```\n */\n\n/** AWS credentials and signing scope for SigV4. */\nexport interface SigV4Credentials {\n /** AWS access key ID. */\n accessKeyId: string;\n /** AWS secret access key. */\n secretAccessKey: string;\n /** AWS region (e.g. `us-east-1`). */\n region: string;\n /** AWS service name (e.g. `ses`, `s3`). */\n service: string;\n /** Optional STS session token for temporary credentials. */\n sessionToken?: string;\n}\n\n/** HTTP request to sign with AWS Signature Version 4. */\nexport interface SigV4Request {\n /** HTTP method (e.g. `POST`). */\n method: string;\n /** Full request URL including path and query. */\n url: string;\n /** Request headers to include in the signature. */\n headers: Record<string, string>;\n /** Request body as a string (empty for GET). */\n body: string;\n /** AWS credentials and signing scope. */\n credentials: SigV4Credentials;\n /** Override datetime for testing. Full 'YYYYMMDDTHHMMSSZ' when provided. */\n _date?: string;\n}\n\n/** Signed request headers including Authorization. */\nexport interface SigV4Result {\n /** All headers including Authorization, x-amz-date, and x-amz-security-token */\n headers: Record<string, string>;\n}\n\nconst encoder = new TextEncoder();\n\n/**\n * Compute SHA-256 hash of a string using Web Crypto.\n * Returns lowercase hex string.\n * @internal\n */\nexport async function sha256Hex(data: string): Promise<string> {\n const hash = await crypto.subtle.digest(\"SHA-256\", encoder.encode(data));\n return bytesToHex(new Uint8Array(hash));\n}\n\n/**\n * Compute HMAC-SHA256 using Web Crypto.\n * @internal\n */\nexport async function hmacSHA256(key: Uint8Array | string, data: string): Promise<Uint8Array> {\n const keyBytes = typeof key === \"string\" ? encoder.encode(key) : new Uint8Array(key);\n const cryptoKey = await crypto.subtle.importKey(\n \"raw\",\n keyBytes,\n { name: \"HMAC\", hash: \"SHA-256\" },\n false,\n [\"sign\"],\n );\n const signature = await crypto.subtle.sign(\"HMAC\", cryptoKey, encoder.encode(data));\n return new Uint8Array(signature);\n}\n\n/**\n * Sign an HTTP request with AWS Signature Version 4.\n * Returns the complete set of headers to include in the request.\n */\nexport async function signRequest(request: SigV4Request): Promise<SigV4Result> {\n const { method, url, body, credentials } = request;\n const parsed = new URL(url);\n const amzDate = request._date ?? `${new Date().toISOString().replace(/[-:]/g, \"\").slice(0, 15)}Z`;\n const dateStamp = amzDate.slice(0, 8);\n\n const headers: Record<string, string> = {\n ...Object.fromEntries(\n Object.entries(request.headers).map(([key, value]) => [key.toLowerCase(), value.trim()]),\n ),\n host: parsed.host,\n \"x-amz-date\": amzDate,\n };\n\n if (credentials.sessionToken) {\n headers[\"x-amz-security-token\"] = credentials.sessionToken;\n }\n\n const signedHeaderNames = Object.keys(headers)\n .map((name) => name.toLowerCase())\n .sort();\n const signedHeaders = signedHeaderNames.join(\";\");\n const canonicalHeaders = `${signedHeaderNames.map((name) => `${name}:${headers[name]}`).join(\"\\n\")}\\n`;\n const canonicalQuery = normalizeQuery(parsed.searchParams);\n const payloadHash = await sha256Hex(body);\n const canonicalRequest = [\n method.toUpperCase(),\n canonicalUri(parsed.pathname),\n canonicalQuery,\n canonicalHeaders,\n signedHeaders,\n payloadHash,\n ].join(\"\\n\");\n\n const credentialScope = `${dateStamp}/${credentials.region}/${credentials.service}/aws4_request`;\n const stringToSign = [\n \"AWS4-HMAC-SHA256\",\n amzDate,\n credentialScope,\n await sha256Hex(canonicalRequest),\n ].join(\"\\n\");\n\n const signingKey = await deriveSigningKey(\n credentials.secretAccessKey,\n dateStamp,\n credentials.region,\n credentials.service,\n );\n const signature = bytesToHex(await hmacSHA256(signingKey, stringToSign));\n const authorization = [\n `AWS4-HMAC-SHA256 Credential=${credentials.accessKeyId}/${credentialScope}`,\n `SignedHeaders=${signedHeaders}`,\n `Signature=${signature}`,\n ].join(\", \");\n\n return {\n headers: {\n ...headers,\n Authorization: authorization,\n },\n };\n}\n\nasync function deriveSigningKey(\n secretAccessKey: string,\n dateStamp: string,\n region: string,\n service: string,\n): Promise<Uint8Array> {\n const kDate = await hmacSHA256(`AWS4${secretAccessKey}`, dateStamp);\n const kRegion = await hmacSHA256(kDate, region);\n const kService = await hmacSHA256(kRegion, service);\n return hmacSHA256(kService, \"aws4_request\");\n}\n\nfunction canonicalUri(pathname: string): string {\n if (!pathname || pathname === \"/\") {\n return \"/\";\n }\n return pathname\n .split(\"/\")\n .map((segment) => encodeURIComponent(decodeURIComponent(segment)))\n .join(\"/\");\n}\n\nfunction normalizeQuery(searchParams: URLSearchParams): string {\n const pairs: string[] = [];\n for (const [key, value] of searchParams.entries()) {\n pairs.push(`${encodeRfc3986(key)}=${encodeRfc3986(value)}`);\n }\n pairs.sort();\n return pairs.join(\"&\");\n}\n\nfunction encodeRfc3986(value: string): string {\n return encodeURIComponent(value).replace(\n /[!'()*]/g,\n (char) => `%${char.charCodeAt(0).toString(16).toUpperCase()}`,\n );\n}\n\nfunction bytesToHex(bytes: Uint8Array): string {\n return Array.from(bytes, (byte) => byte.toString(16).padStart(2, \"0\")).join(\"\");\n}\n",
6
+ "/**\n * @module\n * AWS SES v2 HTTP transport for sently.\n * Signs requests with AWS Signature Version 4 using Web Crypto.\n * Works on Node.js, Bun, Deno, and Cloudflare Workers.\n *\n * @example\n * ```ts\n * import { SESTransport } from \"sently/transports/ses\";\n * import { createMailer } from \"sently\";\n *\n * const mailer = await createMailer({\n * transport: new SESTransport({\n * accessKeyId: process.env.AWS_ACCESS_KEY_ID!,\n * secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,\n * region: \"us-east-1\",\n * }),\n * });\n * ```\n */\nimport { extractEmails, parseAddresses, toMIMEHeader } from \"../core/address.js\";\nimport { encodeBase64 } from \"../core/base64.js\";\nimport { buildMIME } from \"../core/mime.js\";\nimport { signRequest } from \"../core/sigv4.js\";\nimport type { MailOptions, SESConfig, SendResult, Transport, VerifyResult } from \"../core/types.js\";\nimport { resolveAttachments } from \"./resolve-attachments.js\";\n\n/** Error thrown when the AWS SES API returns a non-success response. */\nexport class SESError extends Error {\n /** Creates an AWS SES API error with status code, error code, and request ID. */\n constructor(\n message: string,\n public readonly statusCode: number,\n public readonly code: string,\n public readonly requestId: string,\n ) {\n super(message);\n this.name = \"SESError\";\n }\n}\n\n/**\n * AWS SES v2 HTTP API transport.\n */\nexport class SESTransport implements Transport {\n /** AWS access key ID for SigV4 signing. */\n private readonly accessKeyId: string;\n /** AWS secret access key for SigV4 signing. */\n private readonly secretAccessKey: string;\n /** AWS region for the SES endpoint. */\n private readonly region: string;\n /** Optional STS session token for temporary credentials. */\n private readonly sessionToken: string | undefined;\n /** Optional DKIM config for raw MIME attachment sends. */\n private readonly dkim: SESConfig[\"dkim\"];\n\n /** Creates an SES transport with AWS credentials. */\n constructor(config: SESConfig) {\n this.accessKeyId = config.accessKeyId;\n this.secretAccessKey = config.secretAccessKey;\n this.region = config.region ?? \"us-east-1\";\n this.sessionToken = config.sessionToken;\n this.dkim = config.dkim;\n }\n\n /** Sends an email via the AWS SES v2 HTTP API. */\n async send(options: MailOptions): Promise<SendResult> {\n const attachments = await resolveAttachments(options.attachments);\n const resolvedOptions = { ...options, attachments };\n const from = parseAddresses(options.from)[0];\n const fromEmail = from ? toMIMEHeader(from) : \"\";\n const toEmails = extractEmails(options.to);\n const ccEmails = options.cc ? extractEmails(options.cc) : [];\n const bccEmails = options.bcc ? extractEmails(options.bcc) : [];\n\n const destination = {\n ToAddresses: toEmails,\n CcAddresses: ccEmails,\n BccAddresses: bccEmails,\n };\n\n let requestBody: Record<string, unknown>;\n\n if (attachments.length > 0) {\n const mime = await buildMIME(resolvedOptions, this.dkim);\n requestBody = {\n FromEmailAddress: fromEmail,\n Destination: destination,\n Content: {\n Raw: {\n Data: encodeBase64(mime.raw).replace(/\\r\\n/g, \"\"),\n },\n },\n };\n } else {\n requestBody = {\n FromEmailAddress: fromEmail,\n Destination: destination,\n Content: {\n Simple: {\n Subject: { Data: options.subject, Charset: \"UTF-8\" },\n Body: {\n ...(options.text ? { Text: { Data: options.text, Charset: \"UTF-8\" } } : {}),\n ...(options.html ? { Html: { Data: options.html, Charset: \"UTF-8\" } } : {}),\n },\n },\n },\n };\n }\n\n const body = JSON.stringify(requestBody);\n const url = `https://email.${this.region}.amazonaws.com/v2/email/outbound-emails`;\n const signed = await signRequest({\n method: \"POST\",\n url,\n headers: {\n \"content-type\": \"application/json\",\n },\n body,\n credentials: {\n accessKeyId: this.accessKeyId,\n secretAccessKey: this.secretAccessKey,\n region: this.region,\n service: \"ses\",\n ...(this.sessionToken ? { sessionToken: this.sessionToken } : {}),\n },\n });\n\n const response = await fetch(url, {\n method: \"POST\",\n headers: signed.headers,\n body,\n });\n\n const payload = (await response.json()) as {\n MessageId?: string;\n message?: string;\n Code?: string;\n };\n\n if (!response.ok) {\n throw new SESError(\n payload.message ?? \"SES API error\",\n response.status,\n payload.Code ?? \"\",\n response.headers.get(\"x-amzn-requestid\") ?? \"\",\n );\n }\n\n const messageId = payload.MessageId ?? \"\";\n return {\n messageId,\n accepted: [...toEmails, ...ccEmails, ...bccEmails],\n rejected: [],\n response: `MessageId: ${messageId}`,\n envelope: {\n from: from?.address ?? \"\",\n to: toEmails,\n },\n };\n }\n\n /** Verifies AWS credentials by listing SES configuration sets. */\n async verify(): Promise<VerifyResult> {\n try {\n const url = `https://email.${this.region}.amazonaws.com/v2/email/configuration-sets`;\n const signed = await signRequest({\n method: \"GET\",\n url,\n headers: {},\n body: \"\",\n credentials: {\n accessKeyId: this.accessKeyId,\n secretAccessKey: this.secretAccessKey,\n region: this.region,\n service: \"ses\",\n ...(this.sessionToken ? { sessionToken: this.sessionToken } : {}),\n },\n });\n\n const response = await fetch(url, {\n method: \"GET\",\n headers: signed.headers,\n });\n\n const payload = (await response.json().catch(() => ({}))) as { message?: string };\n\n if (!response.ok) {\n return {\n ok: false,\n provider: \"ses\",\n message: payload.message ?? `HTTP ${response.status}`,\n };\n }\n\n return { ok: true, provider: \"ses\", message: \"Credentials are valid\" };\n } catch (err) {\n return {\n ok: false,\n provider: \"ses\",\n message: err instanceof Error ? err.message : String(err),\n };\n }\n }\n}\n"
7
7
  ],
8
- "mappings": "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",
8
+ "mappings": ";;;;;;;;;;;;;;;AAuDA,IAAM,UAAU,IAAI;AAOpB,eAAsB,SAAS,CAAC,MAA+B;AAAA,EAC7D,MAAM,OAAO,MAAM,OAAO,OAAO,OAAO,WAAW,QAAQ,OAAO,IAAI,CAAC;AAAA,EACvE,OAAO,WAAW,IAAI,WAAW,IAAI,CAAC;AAAA;AAOxC,eAAsB,UAAU,CAAC,KAA0B,MAAmC;AAAA,EAC5F,MAAM,WAAW,OAAO,QAAQ,WAAW,QAAQ,OAAO,GAAG,IAAI,IAAI,WAAW,GAAG;AAAA,EACnF,MAAM,YAAY,MAAM,OAAO,OAAO,UACpC,OACA,UACA,EAAE,MAAM,QAAQ,MAAM,UAAU,GAChC,OACA,CAAC,MAAM,CACT;AAAA,EACA,MAAM,YAAY,MAAM,OAAO,OAAO,KAAK,QAAQ,WAAW,QAAQ,OAAO,IAAI,CAAC;AAAA,EAClF,OAAO,IAAI,WAAW,SAAS;AAAA;AAOjC,eAAsB,WAAW,CAAC,SAA6C;AAAA,EAC7E,QAAQ,QAAQ,KAAK,MAAM,gBAAgB;AAAA,EAC3C,MAAM,SAAS,IAAI,IAAI,GAAG;AAAA,EAC1B,MAAM,UAAU,QAAQ,SAAS,GAAG,IAAI,KAAK,EAAE,YAAY,EAAE,QAAQ,SAAS,EAAE,EAAE,MAAM,GAAG,EAAE;AAAA,EAC7F,MAAM,YAAY,QAAQ,MAAM,GAAG,CAAC;AAAA,EAEpC,MAAM,UAAkC;AAAA,OACnC,OAAO,YACR,OAAO,QAAQ,QAAQ,OAAO,EAAE,IAAI,EAAE,KAAK,WAAW,CAAC,IAAI,YAAY,GAAG,MAAM,KAAK,CAAC,CAAC,CACzF;AAAA,IACA,MAAM,OAAO;AAAA,IACb,cAAc;AAAA,EAChB;AAAA,EAEA,IAAI,YAAY,cAAc;AAAA,IAC5B,QAAQ,0BAA0B,YAAY;AAAA,EAChD;AAAA,EAEA,MAAM,oBAAoB,OAAO,KAAK,OAAO,EAC1C,IAAI,CAAC,SAAS,KAAK,YAAY,CAAC,EAChC,KAAK;AAAA,EACR,MAAM,gBAAgB,kBAAkB,KAAK,GAAG;AAAA,EAChD,MAAM,mBAAmB,GAAG,kBAAkB,IAAI,CAAC,SAAS,GAAG,QAAQ,QAAQ,OAAO,EAAE,KAAK;AAAA,CAAI;AAAA;AAAA,EACjG,MAAM,iBAAiB,eAAe,OAAO,YAAY;AAAA,EACzD,MAAM,cAAc,MAAM,UAAU,IAAI;AAAA,EACxC,MAAM,mBAAmB;AAAA,IACvB,OAAO,YAAY;AAAA,IACnB,aAAa,OAAO,QAAQ;AAAA,IAC5B;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,EAAE,KAAK;AAAA,CAAI;AAAA,EAEX,MAAM,kBAAkB,GAAG,aAAa,YAAY,UAAU,YAAY;AAAA,EAC1E,MAAM,eAAe;AAAA,IACnB;AAAA,IACA;AAAA,IACA;AAAA,IACA,MAAM,UAAU,gBAAgB;AAAA,EAClC,EAAE,KAAK;AAAA,CAAI;AAAA,EAEX,MAAM,aAAa,MAAM,iBACvB,YAAY,iBACZ,WACA,YAAY,QACZ,YAAY,OACd;AAAA,EACA,MAAM,YAAY,WAAW,MAAM,WAAW,YAAY,YAAY,CAAC;AAAA,EACvE,MAAM,gBAAgB;AAAA,IACpB,+BAA+B,YAAY,eAAe;AAAA,IAC1D,iBAAiB;AAAA,IACjB,aAAa;AAAA,EACf,EAAE,KAAK,IAAI;AAAA,EAEX,OAAO;AAAA,IACL,SAAS;AAAA,SACJ;AAAA,MACH,eAAe;AAAA,IACjB;AAAA,EACF;AAAA;AAGF,eAAe,gBAAgB,CAC7B,iBACA,WACA,QACA,SACqB;AAAA,EACrB,MAAM,QAAQ,MAAM,WAAW,OAAO,mBAAmB,SAAS;AAAA,EAClE,MAAM,UAAU,MAAM,WAAW,OAAO,MAAM;AAAA,EAC9C,MAAM,WAAW,MAAM,WAAW,SAAS,OAAO;AAAA,EAClD,OAAO,WAAW,UAAU,cAAc;AAAA;AAG5C,SAAS,YAAY,CAAC,UAA0B;AAAA,EAC9C,IAAI,CAAC,YAAY,aAAa,KAAK;AAAA,IACjC,OAAO;AAAA,EACT;AAAA,EACA,OAAO,SACJ,MAAM,GAAG,EACT,IAAI,CAAC,YAAY,mBAAmB,mBAAmB,OAAO,CAAC,CAAC,EAChE,KAAK,GAAG;AAAA;AAGb,SAAS,cAAc,CAAC,cAAuC;AAAA,EAC7D,MAAM,QAAkB,CAAC;AAAA,EACzB,YAAY,KAAK,UAAU,aAAa,QAAQ,GAAG;AAAA,IACjD,MAAM,KAAK,GAAG,cAAc,GAAG,KAAK,cAAc,KAAK,GAAG;AAAA,EAC5D;AAAA,EACA,MAAM,KAAK;AAAA,EACX,OAAO,MAAM,KAAK,GAAG;AAAA;AAGvB,SAAS,aAAa,CAAC,OAAuB;AAAA,EAC5C,OAAO,mBAAmB,KAAK,EAAE,QAC/B,YACA,CAAC,SAAS,IAAI,KAAK,WAAW,CAAC,EAAE,SAAS,EAAE,EAAE,YAAY,GAC5D;AAAA;AAGF,SAAS,UAAU,CAAC,OAA2B;AAAA,EAC7C,OAAO,MAAM,KAAK,OAAO,CAAC,SAAS,KAAK,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC,EAAE,KAAK,EAAE;AAAA;;;AClKzE,MAAM,iBAAiB,MAAM;AAAA,EAIhB;AAAA,EACA;AAAA,EACA;AAAA,EAJlB,WAAW,CACT,SACgB,YACA,MACA,WAChB;AAAA,IACA,MAAM,OAAO;AAAA,IAJG;AAAA,IACA;AAAA,IACA;AAAA,IAGhB,KAAK,OAAO;AAAA;AAEhB;AAAA;AAKO,MAAM,aAAkC;AAAA,EAE5B;AAAA,EAEA;AAAA,EAEA;AAAA,EAEA;AAAA,EAEA;AAAA,EAGjB,WAAW,CAAC,QAAmB;AAAA,IAC7B,KAAK,cAAc,OAAO;AAAA,IAC1B,KAAK,kBAAkB,OAAO;AAAA,IAC9B,KAAK,SAAS,OAAO,UAAU;AAAA,IAC/B,KAAK,eAAe,OAAO;AAAA,IAC3B,KAAK,OAAO,OAAO;AAAA;AAAA,OAIf,KAAI,CAAC,SAA2C;AAAA,IACpD,MAAM,cAAc,MAAM,mBAAmB,QAAQ,WAAW;AAAA,IAChE,MAAM,kBAAkB,KAAK,SAAS,YAAY;AAAA,IAClD,MAAM,OAAO,eAAe,QAAQ,IAAI,EAAE;AAAA,IAC1C,MAAM,YAAY,OAAO,aAAa,IAAI,IAAI;AAAA,IAC9C,MAAM,WAAW,cAAc,QAAQ,EAAE;AAAA,IACzC,MAAM,WAAW,QAAQ,KAAK,cAAc,QAAQ,EAAE,IAAI,CAAC;AAAA,IAC3D,MAAM,YAAY,QAAQ,MAAM,cAAc,QAAQ,GAAG,IAAI,CAAC;AAAA,IAE9D,MAAM,cAAc;AAAA,MAClB,aAAa;AAAA,MACb,aAAa;AAAA,MACb,cAAc;AAAA,IAChB;AAAA,IAEA,IAAI;AAAA,IAEJ,IAAI,YAAY,SAAS,GAAG;AAAA,MAC1B,MAAM,OAAO,MAAM,UAAU,iBAAiB,KAAK,IAAI;AAAA,MACvD,cAAc;AAAA,QACZ,kBAAkB;AAAA,QAClB,aAAa;AAAA,QACb,SAAS;AAAA,UACP,KAAK;AAAA,YACH,MAAM,aAAa,KAAK,GAAG,EAAE,QAAQ,SAAS,EAAE;AAAA,UAClD;AAAA,QACF;AAAA,MACF;AAAA,IACF,EAAO;AAAA,MACL,cAAc;AAAA,QACZ,kBAAkB;AAAA,QAClB,aAAa;AAAA,QACb,SAAS;AAAA,UACP,QAAQ;AAAA,YACN,SAAS,EAAE,MAAM,QAAQ,SAAS,SAAS,QAAQ;AAAA,YACnD,MAAM;AAAA,iBACA,QAAQ,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,MAAM,SAAS,QAAQ,EAAE,IAAI,CAAC;AAAA,iBACrE,QAAQ,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,MAAM,SAAS,QAAQ,EAAE,IAAI,CAAC;AAAA,YAC3E;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA;AAAA,IAGF,MAAM,OAAO,KAAK,UAAU,WAAW;AAAA,IACvC,MAAM,MAAM,iBAAiB,KAAK;AAAA,IAClC,MAAM,SAAS,MAAM,YAAY;AAAA,MAC/B,QAAQ;AAAA,MACR;AAAA,MACA,SAAS;AAAA,QACP,gBAAgB;AAAA,MAClB;AAAA,MACA;AAAA,MACA,aAAa;AAAA,QACX,aAAa,KAAK;AAAA,QAClB,iBAAiB,KAAK;AAAA,QACtB,QAAQ,KAAK;AAAA,QACb,SAAS;AAAA,WACL,KAAK,eAAe,EAAE,cAAc,KAAK,aAAa,IAAI,CAAC;AAAA,MACjE;AAAA,IACF,CAAC;AAAA,IAED,MAAM,WAAW,MAAM,MAAM,KAAK;AAAA,MAChC,QAAQ;AAAA,MACR,SAAS,OAAO;AAAA,MAChB;AAAA,IACF,CAAC;AAAA,IAED,MAAM,UAAW,MAAM,SAAS,KAAK;AAAA,IAMrC,IAAI,CAAC,SAAS,IAAI;AAAA,MAChB,MAAM,IAAI,SACR,QAAQ,WAAW,iBACnB,SAAS,QACT,QAAQ,QAAQ,IAChB,SAAS,QAAQ,IAAI,kBAAkB,KAAK,EAC9C;AAAA,IACF;AAAA,IAEA,MAAM,YAAY,QAAQ,aAAa;AAAA,IACvC,OAAO;AAAA,MACL;AAAA,MACA,UAAU,CAAC,GAAG,UAAU,GAAG,UAAU,GAAG,SAAS;AAAA,MACjD,UAAU,CAAC;AAAA,MACX,UAAU,cAAc;AAAA,MACxB,UAAU;AAAA,QACR,MAAM,MAAM,WAAW;AAAA,QACvB,IAAI;AAAA,MACN;AAAA,IACF;AAAA;AAAA,OAII,OAAM,GAA0B;AAAA,IACpC,IAAI;AAAA,MACF,MAAM,MAAM,iBAAiB,KAAK;AAAA,MAClC,MAAM,SAAS,MAAM,YAAY;AAAA,QAC/B,QAAQ;AAAA,QACR;AAAA,QACA,SAAS,CAAC;AAAA,QACV,MAAM;AAAA,QACN,aAAa;AAAA,UACX,aAAa,KAAK;AAAA,UAClB,iBAAiB,KAAK;AAAA,UACtB,QAAQ,KAAK;AAAA,UACb,SAAS;AAAA,aACL,KAAK,eAAe,EAAE,cAAc,KAAK,aAAa,IAAI,CAAC;AAAA,QACjE;AAAA,MACF,CAAC;AAAA,MAED,MAAM,WAAW,MAAM,MAAM,KAAK;AAAA,QAChC,QAAQ;AAAA,QACR,SAAS,OAAO;AAAA,MAClB,CAAC;AAAA,MAED,MAAM,UAAW,MAAM,SAAS,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AAAA,MAEvD,IAAI,CAAC,SAAS,IAAI;AAAA,QAChB,OAAO;AAAA,UACL,IAAI;AAAA,UACJ,UAAU;AAAA,UACV,SAAS,QAAQ,WAAW,QAAQ,SAAS;AAAA,QAC/C;AAAA,MACF;AAAA,MAEA,OAAO,EAAE,IAAI,MAAM,UAAU,OAAO,SAAS,wBAAwB;AAAA,MACrE,OAAO,KAAK;AAAA,MACZ,OAAO;AAAA,QACL,IAAI;AAAA,QACJ,UAAU;AAAA,QACV,SAAS,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,MAC1D;AAAA;AAAA;AAGN;",
9
9
  "debugId": "E3DA50CEC668691D64756E2164756E21",
10
10
  "names": []
11
11
  }
@@ -6,7 +6,9 @@ import type { MailOptions, SendResult, SMTPConfig, SocketAdapter, Transport, Ver
6
6
  * SMTP transport orchestrating adapter, MIME builder, and protocol logic.
7
7
  */
8
8
  export declare class SMTPTransport implements Transport {
9
+ /** Resolved SMTP configuration with defaults applied. */
9
10
  private readonly config;
11
+ /** Active socket adapter for the current session, if any. */
10
12
  private adapter;
11
13
  /** Creates an SMTP transport with the given configuration. */
12
14
  constructor(config: SMTPConfig);
@@ -16,6 +18,7 @@ export declare class SMTPTransport implements Transport {
16
18
  verify(): Promise<VerifyResult>;
17
19
  /** Closes the underlying socket adapter if connected. */
18
20
  close(): Promise<void>;
21
+ /** Returns the configured socket adapter or throws if none is set. */
19
22
  private getAdapter;
20
23
  }
21
24
  /** Resolved SMTP transport configuration with defaults applied. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sently",
3
- "version": "0.4.5",
3
+ "version": "0.4.6",
4
4
  "description": "Runtime-agnostic email library for Node.js, Bun, Deno, and Cloudflare Workers. ESM-native Nodemailer alternative with zero dependencies.",
5
5
  "type": "module",
6
6
  "sideEffects": false,