sently 0.4.5 → 0.4.7

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 (97) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/README.md +94 -41
  3. package/dist/adapters/bun.d.ts +7 -0
  4. package/dist/adapters/bun.js +2 -183
  5. package/dist/adapters/bun.js.map +3 -3
  6. package/dist/adapters/cf.d.ts +6 -0
  7. package/dist/adapters/cf.js +2 -77
  8. package/dist/adapters/cf.js.map +3 -3
  9. package/dist/adapters/deno.d.ts +4 -0
  10. package/dist/adapters/deno.js +2 -72
  11. package/dist/adapters/deno.js.map +3 -3
  12. package/dist/adapters/node.d.ts +7 -0
  13. package/dist/adapters/node.js +2 -180
  14. package/dist/adapters/node.js.map +3 -3
  15. package/dist/auth/oauth2.d.ts +4 -0
  16. package/dist/auth/oauth2.js +2 -13
  17. package/dist/auth/oauth2.js.map +1 -1
  18. package/dist/chunk-2kcwa9gt.js +4 -0
  19. package/dist/chunk-2kcwa9gt.js.map +11 -0
  20. package/dist/chunk-2t6hjer3.js +5 -0
  21. package/dist/chunk-2t6hjer3.js.map +10 -0
  22. package/dist/chunk-6yggz45h.js +5 -0
  23. package/dist/{chunk-794hc3m4.js.map → chunk-6yggz45h.js.map} +2 -2
  24. package/dist/chunk-dgkh77yp.js +4 -0
  25. package/dist/chunk-dgkh77yp.js.map +10 -0
  26. package/dist/chunk-jfs80vhp.js +3 -0
  27. package/dist/chunk-jfs80vhp.js.map +10 -0
  28. package/dist/chunk-sqn04kae.js +4 -0
  29. package/dist/{chunk-v0bahtg2.js.map → chunk-sqn04kae.js.map} +1 -1
  30. package/dist/chunk-va2awz12.js +4 -0
  31. package/dist/{chunk-f4c9ttmr.js.map → chunk-va2awz12.js.map} +2 -2
  32. package/dist/chunk-wgtbr6ge.js +13 -0
  33. package/dist/chunk-wgtbr6ge.js.map +11 -0
  34. package/dist/core/mime.d.ts +4 -0
  35. package/dist/core/sigv4.d.ts +10 -0
  36. package/dist/core/smtp.d.ts +5 -0
  37. package/dist/core/smtp.js +2 -31
  38. package/dist/core/smtp.js.map +1 -1
  39. package/dist/core/types.d.ts +7 -0
  40. package/dist/detect.d.ts +2 -0
  41. package/dist/detect.js +2 -180
  42. package/dist/detect.js.map +4 -5
  43. package/dist/dkim.d.ts +21 -0
  44. package/dist/dkim.js +9 -0
  45. package/dist/dkim.js.map +10 -0
  46. package/dist/index.d.ts +74 -16
  47. package/dist/index.js +85 -14
  48. package/dist/mailer.d.ts +16 -0
  49. package/dist/mailer.js +3 -0
  50. package/dist/mailer.js.map +9 -0
  51. package/dist/plugins/template.js +2 -28
  52. package/dist/plugins/template.js.map +2 -2
  53. package/dist/pool/connection.d.ts +4 -0
  54. package/dist/pool/pool.d.ts +20 -0
  55. package/dist/pool/pool.js +2 -16
  56. package/dist/pool/pool.js.map +5 -3
  57. package/dist/transports/brevo.d.ts +1 -0
  58. package/dist/transports/brevo.js +2 -115
  59. package/dist/transports/brevo.js.map +3 -3
  60. package/dist/transports/mailgun.d.ts +3 -0
  61. package/dist/transports/mailgun.js +2 -119
  62. package/dist/transports/mailgun.js.map +3 -3
  63. package/dist/transports/postmark.d.ts +1 -0
  64. package/dist/transports/postmark.js +2 -113
  65. package/dist/transports/postmark.js.map +3 -3
  66. package/dist/transports/preview.d.ts +3 -0
  67. package/dist/transports/preview.js +2 -72
  68. package/dist/transports/preview.js.map +3 -3
  69. package/dist/transports/resend.d.ts +2 -0
  70. package/dist/transports/resend.js +2 -109
  71. package/dist/transports/resend.js.map +3 -3
  72. package/dist/transports/retry.d.ts +12 -1
  73. package/dist/transports/retry.js +2 -78
  74. package/dist/transports/retry.js.map +3 -3
  75. package/dist/transports/sendgrid.d.ts +1 -0
  76. package/dist/transports/sendgrid.js +2 -132
  77. package/dist/transports/sendgrid.js.map +3 -3
  78. package/dist/transports/ses.d.ts +5 -0
  79. package/dist/transports/ses.js +5 -251
  80. package/dist/transports/ses.js.map +4 -4
  81. package/dist/transports/smtp.d.ts +3 -0
  82. package/dist/transports/smtp.js +2 -26
  83. package/dist/transports/smtp.js.map +1 -1
  84. package/package.json +17 -6
  85. package/dist/chunk-794hc3m4.js +0 -105
  86. package/dist/chunk-7fqv71z1.js +0 -251
  87. package/dist/chunk-7fqv71z1.js.map +0 -10
  88. package/dist/chunk-f4c9ttmr.js +0 -154
  89. package/dist/chunk-mp5c9bfd.js +0 -270
  90. package/dist/chunk-mp5c9bfd.js.map +0 -11
  91. package/dist/chunk-tymfm441.js +0 -405
  92. package/dist/chunk-tymfm441.js.map +0 -11
  93. package/dist/chunk-v0bahtg2.js +0 -6
  94. package/dist/chunk-x3szga4k.js +0 -367
  95. package/dist/chunk-x3szga4k.js.map +0 -11
  96. package/dist/chunk-ym3zzv8b.js +0 -74
  97. package/dist/chunk-ym3zzv8b.js.map +0 -10
@@ -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;",
8
- "debugId": "F5AD32E7B75CAE2A64756E2164756E21",
7
+ "mappings": "6GA0BA,DAAM,HAAmB,CAAC,IAAK,IAAK,IAAK,IAAK,GAAG,EAEjD,SAAS,CAAY,CAAC,EAAiB,EAAiB,EAAsB,CAC5E,GAAI,IAAY,cACd,OAAO,EAAO,IAAM,EAAU,GAEhC,GAAI,IAAY,SACd,OAAO,EAAO,EAEhB,OAAO,EAGT,SAAS,CAAW,CAAC,EAAc,EAA4B,CAC7D,GAAI,aAAe,GAAa,EAAI,OAAS,IAC3C,MAAO,GAGT,GACE,OAAO,IAAQ,UACf,IAAQ,MACR,eAAgB,GAChB,OAAQ,EAAgC,aAAe,SAEvD,OAAO,EAAQ,SAAU,EAA+B,UAAU,EAGpE,MAAO,GAMF,MAAM,CAAoC,CAe5B,MAGA,OAhBF,YAEA,QAEA,UAEA,QAEA,QAGjB,WAAW,CAEQ,EACjB,EAEiB,EAAwC,CAAC,IACxD,IAAI,QAAQ,CAAC,IAAY,WAAW,EAAS,CAAE,CAAC,EAClD,CALiB,aAGA,cAGjB,KAAK,YAAc,GAAQ,aAAe,EAC1C,KAAK,QAAU,GAAQ,SAAW,cAClC,KAAK,UAAY,GAAQ,WAAa,KACtC,KAAK,QAAU,GAAQ,SAAW,EAClC,KAAK,QAAU,GAAQ,aAInB,KAAI,CAAC,EAA2C,CACpD,IAAI,EAEJ,QAAS,EAAU,EAAG,GAAW,KAAK,YAAa,IACjD,GAAI,CACF,OAAO,MAAM,KAAK,MAAM,KAAK,CAAO,EACpC,MAAO,EAAK,CAEZ,GADA,EAAY,EACR,IAAY,KAAK,YACnB,MAAM,EAER,GAAI,CAAC,EAAY,EAAK,KAAK,OAAO,EAChC,MAAM,EAER,IAAM,EAAQ,EAAa,EAAS,KAAK,QAAS,KAAK,SAAS,EAChE,KAAK,UAAU,EAAS,CAAG,EAC3B,MAAM,KAAK,OAAO,CAAK,EAI3B,MAAM,OAIF,OAAM,EAA0B,CACpC,GAAI,KAAK,MAAM,OACb,OAAO,KAAK,MAAM,OAAO,EAE3B,MAAO,CAAE,GAAI,GAAM,SAAU,OAAQ,OAIjC,MAAK,EAAkB,CAC3B,MAAM,KAAK,MAAM,QAAQ,EAE7B",
8
+ "debugId": "1E2375BF1AD866DC64756E2164756E21",
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);
@@ -1,133 +1,3 @@
1
- import {
2
- extractEmails,
3
- parseAddresses,
4
- resolveAttachments
5
- } from "../chunk-f4c9ttmr.js";
6
- import {
7
- encodeBase64
8
- } from "../chunk-794hc3m4.js";
9
- import"../chunk-v0bahtg2.js";
1
+ import{l as D,n as F,p as K}from"../chunk-va2awz12.js";import{E as J}from"../chunk-6yggz45h.js";import"../chunk-sqn04kae.js";class L extends Error{statusCode;apiError;constructor(q,C,z){super(q);this.statusCode=C;this.apiError=z;this.name="SendGridError"}}class N{apiKey;constructor(q){this.apiKey=q.apiKey}async send(q){let C=await K(q.attachments),z=D(q.from)[0],M={personalizations:[{to:D(q.to).map((w)=>({email:w.address,name:w.name})),...q.cc?{cc:D(q.cc).map((w)=>({email:w.address,name:w.name}))}:{},...q.bcc?{bcc:D(q.bcc).map((w)=>({email:w.address,name:w.name}))}:{}}],from:z?{email:z.address,...z.name?{name:z.name}:{}}:{email:""},subject:q.subject,...q.replyTo?{reply_to:D(q.replyTo).map((w)=>({email:w.address,name:w.name}))[0]}:{},content:[...q.text?[{type:"text/plain",value:q.text}]:[],...q.html?[{type:"text/html",value:q.html}]:[]],...C.length>0?{attachments:C.map((w)=>({filename:w.filename,type:w.contentType??"application/octet-stream",content:w.content instanceof Uint8Array?J(w.content).replace(/\r\n/g,""):w.content,...w.contentId?{content_id:w.contentId}:{},disposition:w.inline?"inline":"attachment"}))}:{}},H=await fetch("https://api.sendgrid.com/v3/mail/send",{method:"POST",headers:{Authorization:`Bearer ${this.apiKey}`,"Content-Type":"application/json"},body:JSON.stringify(M)});if(!H.ok){let w=await H.text();throw new L("SendGrid API error",H.status,w)}return{messageId:H.headers.get("x-message-id")??q.messageId??"",accepted:F(q.to),rejected:[],response:"Accepted",envelope:{from:z?.address??"",to:[...F(q.to),...q.cc?F(q.cc):[],...q.bcc?F(q.bcc):[]]}}}async verify(){try{let q=await fetch("https://api.sendgrid.com/v3/user/profile",{headers:{Authorization:`Bearer ${this.apiKey}`}});if(!q.ok)return{ok:!1,provider:"sendgrid",message:await q.text().catch(()=>"")||`HTTP ${q.status}`};let C=await q.json();return{ok:!0,provider:"sendgrid",...C.username?{message:C.username}:{}}}catch(q){return{ok:!1,provider:"sendgrid",message:q instanceof Error?q.message:String(q)}}}}export{N as SendGridTransport,L as SendGridError};
10
2
 
11
- // src/transports/sendgrid.ts
12
- class SendGridError extends Error {
13
- statusCode;
14
- apiError;
15
- constructor(message, statusCode, apiError) {
16
- super(message);
17
- this.statusCode = statusCode;
18
- this.apiError = apiError;
19
- this.name = "SendGridError";
20
- }
21
- }
22
-
23
- class SendGridTransport {
24
- apiKey;
25
- constructor(config) {
26
- this.apiKey = config.apiKey;
27
- }
28
- async send(options) {
29
- const attachments = await resolveAttachments(options.attachments);
30
- const from = parseAddresses(options.from)[0];
31
- const personalization = {
32
- to: parseAddresses(options.to).map((addr) => ({ email: addr.address, name: addr.name })),
33
- ...options.cc ? {
34
- cc: parseAddresses(options.cc).map((addr) => ({
35
- email: addr.address,
36
- name: addr.name
37
- }))
38
- } : {},
39
- ...options.bcc ? {
40
- bcc: parseAddresses(options.bcc).map((addr) => ({
41
- email: addr.address,
42
- name: addr.name
43
- }))
44
- } : {}
45
- };
46
- const body = {
47
- personalizations: [personalization],
48
- from: from ? { email: from.address, ...from.name ? { name: from.name } : {} } : { email: "" },
49
- subject: options.subject,
50
- ...options.replyTo ? {
51
- reply_to: parseAddresses(options.replyTo).map((addr) => ({
52
- email: addr.address,
53
- name: addr.name
54
- }))[0]
55
- } : {},
56
- content: [
57
- ...options.text ? [{ type: "text/plain", value: options.text }] : [],
58
- ...options.html ? [{ type: "text/html", value: options.html }] : []
59
- ],
60
- ...attachments.length > 0 ? {
61
- attachments: attachments.map((att) => ({
62
- filename: att.filename,
63
- type: att.contentType ?? "application/octet-stream",
64
- content: att.content instanceof Uint8Array ? encodeBase64(att.content).replace(/\r\n/g, "") : att.content,
65
- ...att.contentId ? { content_id: att.contentId } : {},
66
- disposition: att.inline ? "inline" : "attachment"
67
- }))
68
- } : {}
69
- };
70
- const response = await fetch("https://api.sendgrid.com/v3/mail/send", {
71
- method: "POST",
72
- headers: {
73
- Authorization: `Bearer ${this.apiKey}`,
74
- "Content-Type": "application/json"
75
- },
76
- body: JSON.stringify(body)
77
- });
78
- if (!response.ok) {
79
- const apiError = await response.text();
80
- throw new SendGridError("SendGrid API error", response.status, apiError);
81
- }
82
- const messageId = response.headers.get("x-message-id") ?? options.messageId ?? "";
83
- return {
84
- messageId,
85
- accepted: extractEmails(options.to),
86
- rejected: [],
87
- response: "Accepted",
88
- envelope: {
89
- from: from?.address ?? "",
90
- to: [
91
- ...extractEmails(options.to),
92
- ...options.cc ? extractEmails(options.cc) : [],
93
- ...options.bcc ? extractEmails(options.bcc) : []
94
- ]
95
- }
96
- };
97
- }
98
- async verify() {
99
- try {
100
- const response = await fetch("https://api.sendgrid.com/v3/user/profile", {
101
- headers: {
102
- Authorization: `Bearer ${this.apiKey}`
103
- }
104
- });
105
- if (!response.ok) {
106
- const apiError = await response.text().catch(() => "");
107
- return {
108
- ok: false,
109
- provider: "sendgrid",
110
- message: apiError || `HTTP ${response.status}`
111
- };
112
- }
113
- const payload = await response.json();
114
- return {
115
- ok: true,
116
- provider: "sendgrid",
117
- ...payload.username ? { message: payload.username } : {}
118
- };
119
- } catch (err) {
120
- return {
121
- ok: false,
122
- provider: "sendgrid",
123
- message: err instanceof Error ? err.message : String(err)
124
- };
125
- }
126
- }
127
- }
128
- export {
129
- SendGridTransport,
130
- SendGridError
131
- };
132
-
133
- //# debugId=53D65F527A73629564756E2164756E21
3
+ //# debugId=DB03032BE02F85E964756E2164756E21
@@ -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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",
8
- "debugId": "53D65F527A73629564756E2164756E21",
7
+ "mappings": "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",
8
+ "debugId": "DB03032BE02F85E964756E2164756E21",
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);
@@ -1,253 +1,7 @@
1
- import {
2
- buildMIME
3
- } from "../chunk-x3szga4k.js";
4
- import {
5
- extractEmails,
6
- parseAddresses,
7
- resolveAttachments,
8
- toMIMEHeader
9
- } from "../chunk-f4c9ttmr.js";
10
- import {
11
- encodeBase64
12
- } from "../chunk-794hc3m4.js";
13
- import"../chunk-v0bahtg2.js";
14
-
15
- // src/core/sigv4.ts
16
- var encoder = new TextEncoder;
17
- async function sha256Hex(data) {
18
- const hash = await crypto.subtle.digest("SHA-256", encoder.encode(data));
19
- return bytesToHex(new Uint8Array(hash));
20
- }
21
- async function hmacSHA256(key, data) {
22
- const keyBytes = typeof key === "string" ? encoder.encode(key) : new Uint8Array(key);
23
- const cryptoKey = await crypto.subtle.importKey("raw", keyBytes, { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
24
- const signature = await crypto.subtle.sign("HMAC", cryptoKey, encoder.encode(data));
25
- return new Uint8Array(signature);
26
- }
27
- async function signRequest(request) {
28
- const { method, url, body, credentials } = request;
29
- const parsed = new URL(url);
30
- const amzDate = request._date ?? `${new Date().toISOString().replace(/[-:]/g, "").slice(0, 15)}Z`;
31
- const dateStamp = amzDate.slice(0, 8);
32
- const headers = {
33
- ...Object.fromEntries(Object.entries(request.headers).map(([key, value]) => [key.toLowerCase(), value.trim()])),
34
- host: parsed.host,
35
- "x-amz-date": amzDate
36
- };
37
- if (credentials.sessionToken) {
38
- headers["x-amz-security-token"] = credentials.sessionToken;
39
- }
40
- const signedHeaderNames = Object.keys(headers).map((name) => name.toLowerCase()).sort();
41
- const signedHeaders = signedHeaderNames.join(";");
42
- const canonicalHeaders = `${signedHeaderNames.map((name) => `${name}:${headers[name]}`).join(`
1
+ import{j as A}from"../chunk-2t6hjer3.js";import{l as x,m as R,n as P,p as k}from"../chunk-va2awz12.js";import{E as T}from"../chunk-6yggz45h.js";import"../chunk-sqn04kae.js";var M=new TextEncoder;async function z(C){let F=await crypto.subtle.digest("SHA-256",M.encode(C));return K(new Uint8Array(F))}async function W(C,F){let J=typeof C==="string"?M.encode(C):new Uint8Array(C),L=await crypto.subtle.importKey("raw",J,{name:"HMAC",hash:"SHA-256"},!1,["sign"]),G=await crypto.subtle.sign("HMAC",L,M.encode(F));return new Uint8Array(G)}async function j(C){let{method:F,url:J,body:L,credentials:G}=C,X=new URL(J),Y=C._date??`${new Date().toISOString().replace(/[-:]/g,"").slice(0,15)}Z`,U=Y.slice(0,8),Z={...Object.fromEntries(Object.entries(C.headers).map(([O,E])=>[O.toLowerCase(),E.trim()])),host:X.host,"x-amz-date":Y};if(G.sessionToken)Z["x-amz-security-token"]=G.sessionToken;let $=Object.keys(Z).map((O)=>O.toLowerCase()).sort(),N=$.join(";"),_=`${$.map((O)=>`${O}:${Z[O]}`).join(`
43
2
  `)}
44
- `;
45
- const canonicalQuery = normalizeQuery(parsed.searchParams);
46
- const payloadHash = await sha256Hex(body);
47
- const canonicalRequest = [
48
- method.toUpperCase(),
49
- canonicalUri(parsed.pathname),
50
- canonicalQuery,
51
- canonicalHeaders,
52
- signedHeaders,
53
- payloadHash
54
- ].join(`
55
- `);
56
- const credentialScope = `${dateStamp}/${credentials.region}/${credentials.service}/aws4_request`;
57
- const stringToSign = [
58
- "AWS4-HMAC-SHA256",
59
- amzDate,
60
- credentialScope,
61
- await sha256Hex(canonicalRequest)
62
- ].join(`
63
- `);
64
- const signingKey = await deriveSigningKey(credentials.secretAccessKey, dateStamp, credentials.region, credentials.service);
65
- const signature = bytesToHex(await hmacSHA256(signingKey, stringToSign));
66
- const authorization = [
67
- `AWS4-HMAC-SHA256 Credential=${credentials.accessKeyId}/${credentialScope}`,
68
- `SignedHeaders=${signedHeaders}`,
69
- `Signature=${signature}`
70
- ].join(", ");
71
- return {
72
- headers: {
73
- ...headers,
74
- Authorization: authorization
75
- }
76
- };
77
- }
78
- async function deriveSigningKey(secretAccessKey, dateStamp, region, service) {
79
- const kDate = await hmacSHA256(`AWS4${secretAccessKey}`, dateStamp);
80
- const kRegion = await hmacSHA256(kDate, region);
81
- const kService = await hmacSHA256(kRegion, service);
82
- return hmacSHA256(kService, "aws4_request");
83
- }
84
- function canonicalUri(pathname) {
85
- if (!pathname || pathname === "/") {
86
- return "/";
87
- }
88
- return pathname.split("/").map((segment) => encodeURIComponent(decodeURIComponent(segment))).join("/");
89
- }
90
- function normalizeQuery(searchParams) {
91
- const pairs = [];
92
- for (const [key, value] of searchParams.entries()) {
93
- pairs.push(`${encodeRfc3986(key)}=${encodeRfc3986(value)}`);
94
- }
95
- pairs.sort();
96
- return pairs.join("&");
97
- }
98
- function encodeRfc3986(value) {
99
- return encodeURIComponent(value).replace(/[!'()*]/g, (char) => `%${char.charCodeAt(0).toString(16).toUpperCase()}`);
100
- }
101
- function bytesToHex(bytes) {
102
- return Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
103
- }
104
-
105
- // src/transports/ses.ts
106
- class SESError extends Error {
107
- statusCode;
108
- code;
109
- requestId;
110
- constructor(message, statusCode, code, requestId) {
111
- super(message);
112
- this.statusCode = statusCode;
113
- this.code = code;
114
- this.requestId = requestId;
115
- this.name = "SESError";
116
- }
117
- }
118
-
119
- class SESTransport {
120
- accessKeyId;
121
- secretAccessKey;
122
- region;
123
- sessionToken;
124
- dkim;
125
- constructor(config) {
126
- this.accessKeyId = config.accessKeyId;
127
- this.secretAccessKey = config.secretAccessKey;
128
- this.region = config.region ?? "us-east-1";
129
- this.sessionToken = config.sessionToken;
130
- this.dkim = config.dkim;
131
- }
132
- async send(options) {
133
- const attachments = await resolveAttachments(options.attachments);
134
- const resolvedOptions = { ...options, attachments };
135
- const from = parseAddresses(options.from)[0];
136
- const fromEmail = from ? toMIMEHeader(from) : "";
137
- const toEmails = extractEmails(options.to);
138
- const ccEmails = options.cc ? extractEmails(options.cc) : [];
139
- const bccEmails = options.bcc ? extractEmails(options.bcc) : [];
140
- const destination = {
141
- ToAddresses: toEmails,
142
- CcAddresses: ccEmails,
143
- BccAddresses: bccEmails
144
- };
145
- let requestBody;
146
- if (attachments.length > 0) {
147
- const mime = await buildMIME(resolvedOptions, this.dkim);
148
- requestBody = {
149
- FromEmailAddress: fromEmail,
150
- Destination: destination,
151
- Content: {
152
- Raw: {
153
- Data: encodeBase64(mime.raw).replace(/\r\n/g, "")
154
- }
155
- }
156
- };
157
- } else {
158
- requestBody = {
159
- FromEmailAddress: fromEmail,
160
- Destination: destination,
161
- Content: {
162
- Simple: {
163
- Subject: { Data: options.subject, Charset: "UTF-8" },
164
- Body: {
165
- ...options.text ? { Text: { Data: options.text, Charset: "UTF-8" } } : {},
166
- ...options.html ? { Html: { Data: options.html, Charset: "UTF-8" } } : {}
167
- }
168
- }
169
- }
170
- };
171
- }
172
- const body = JSON.stringify(requestBody);
173
- const url = `https://email.${this.region}.amazonaws.com/v2/email/outbound-emails`;
174
- const signed = await signRequest({
175
- method: "POST",
176
- url,
177
- headers: {
178
- "content-type": "application/json"
179
- },
180
- body,
181
- credentials: {
182
- accessKeyId: this.accessKeyId,
183
- secretAccessKey: this.secretAccessKey,
184
- region: this.region,
185
- service: "ses",
186
- ...this.sessionToken ? { sessionToken: this.sessionToken } : {}
187
- }
188
- });
189
- const response = await fetch(url, {
190
- method: "POST",
191
- headers: signed.headers,
192
- body
193
- });
194
- const payload = await response.json();
195
- if (!response.ok) {
196
- throw new SESError(payload.message ?? "SES API error", response.status, payload.Code ?? "", response.headers.get("x-amzn-requestid") ?? "");
197
- }
198
- const messageId = payload.MessageId ?? "";
199
- return {
200
- messageId,
201
- accepted: [...toEmails, ...ccEmails, ...bccEmails],
202
- rejected: [],
203
- response: `MessageId: ${messageId}`,
204
- envelope: {
205
- from: from?.address ?? "",
206
- to: toEmails
207
- }
208
- };
209
- }
210
- async verify() {
211
- try {
212
- const url = `https://email.${this.region}.amazonaws.com/v2/email/configuration-sets`;
213
- const signed = await signRequest({
214
- method: "GET",
215
- url,
216
- headers: {},
217
- body: "",
218
- credentials: {
219
- accessKeyId: this.accessKeyId,
220
- secretAccessKey: this.secretAccessKey,
221
- region: this.region,
222
- service: "ses",
223
- ...this.sessionToken ? { sessionToken: this.sessionToken } : {}
224
- }
225
- });
226
- const response = await fetch(url, {
227
- method: "GET",
228
- headers: signed.headers
229
- });
230
- const payload = await response.json().catch(() => ({}));
231
- if (!response.ok) {
232
- return {
233
- ok: false,
234
- provider: "ses",
235
- message: payload.message ?? `HTTP ${response.status}`
236
- };
237
- }
238
- return { ok: true, provider: "ses", message: "Credentials are valid" };
239
- } catch (err) {
240
- return {
241
- ok: false,
242
- provider: "ses",
243
- message: err instanceof Error ? err.message : String(err)
244
- };
245
- }
246
- }
247
- }
248
- export {
249
- SESTransport,
250
- SESError
251
- };
3
+ `,D=h(X.searchParams),w=await z(L),Q=[F.toUpperCase(),S(X.pathname),D,_,N,w].join(`
4
+ `),V=`${U}/${G.region}/${G.service}/aws4_request`,I=["AWS4-HMAC-SHA256",Y,V,await z(Q)].join(`
5
+ `),b=await f(G.secretAccessKey,U,G.region,G.service),q=K(await W(b,I)),H=[`AWS4-HMAC-SHA256 Credential=${G.accessKeyId}/${V}`,`SignedHeaders=${N}`,`Signature=${q}`].join(", ");return{headers:{...Z,Authorization:H}}}async function f(C,F,J,L){let G=await W(`AWS4${C}`,F),X=await W(G,J),Y=await W(X,L);return W(Y,"aws4_request")}function S(C){if(!C||C==="/")return"/";return C.split("/").map((F)=>encodeURIComponent(decodeURIComponent(F))).join("/")}function h(C){let F=[];for(let[J,L]of C.entries())F.push(`${B(J)}=${B(L)}`);return F.sort(),F.join("&")}function B(C){return encodeURIComponent(C).replace(/[!'()*]/g,(F)=>`%${F.charCodeAt(0).toString(16).toUpperCase()}`)}function K(C){return Array.from(C,(F)=>F.toString(16).padStart(2,"0")).join("")}class v extends Error{statusCode;code;requestId;constructor(C,F,J,L){super(C);this.statusCode=F;this.code=J;this.requestId=L;this.name="SESError"}}class u{accessKeyId;secretAccessKey;region;sessionToken;dkim;constructor(C){this.accessKeyId=C.accessKeyId,this.secretAccessKey=C.secretAccessKey,this.region=C.region??"us-east-1",this.sessionToken=C.sessionToken,this.dkim=C.dkim}async send(C){let F=await k(C.attachments),J={...C,attachments:F},L=x(C.from)[0],G=L?R(L):"",X=P(C.to),Y=C.cc?P(C.cc):[],U=C.bcc?P(C.bcc):[],Z={ToAddresses:X,CcAddresses:Y,BccAddresses:U},$;if(F.length>0){let I=await A(J,this.dkim);$={FromEmailAddress:G,Destination:Z,Content:{Raw:{Data:T(I.raw).replace(/\r\n/g,"")}}}}else $={FromEmailAddress:G,Destination:Z,Content:{Simple:{Subject:{Data:C.subject,Charset:"UTF-8"},Body:{...C.text?{Text:{Data:C.text,Charset:"UTF-8"}}:{},...C.html?{Html:{Data:C.html,Charset:"UTF-8"}}:{}}}}};let N=JSON.stringify($),_=`https://email.${this.region}.amazonaws.com/v2/email/outbound-emails`,D=await j({method:"POST",url:_,headers:{"content-type":"application/json"},body:N,credentials:{accessKeyId:this.accessKeyId,secretAccessKey:this.secretAccessKey,region:this.region,service:"ses",...this.sessionToken?{sessionToken:this.sessionToken}:{}}}),w=await fetch(_,{method:"POST",headers:D.headers,body:N}),Q=await w.json();if(!w.ok)throw new v(Q.message??"SES API error",w.status,Q.Code??"",w.headers.get("x-amzn-requestid")??"");let V=Q.MessageId??"";return{messageId:V,accepted:[...X,...Y,...U],rejected:[],response:`MessageId: ${V}`,envelope:{from:L?.address??"",to:X}}}async verify(){try{let C=`https://email.${this.region}.amazonaws.com/v2/email/configuration-sets`,F=await j({method:"GET",url:C,headers:{},body:"",credentials:{accessKeyId:this.accessKeyId,secretAccessKey:this.secretAccessKey,region:this.region,service:"ses",...this.sessionToken?{sessionToken:this.sessionToken}:{}}}),J=await fetch(C,{method:"GET",headers:F.headers}),L=await J.json().catch(()=>({}));if(!J.ok)return{ok:!1,provider:"ses",message:L.message??`HTTP ${J.status}`};return{ok:!0,provider:"ses",message:"Credentials are valid"}}catch(C){return{ok:!1,provider:"ses",message:C instanceof Error?C.message:String(C)}}}}export{u as SESTransport,v as SESError};
252
6
 
253
- //# debugId=E3DA50CEC668691D64756E2164756E21
7
+ //# debugId=0FA0C89C1B86049064756E2164756E21